diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..8f3c7858 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 +max_line_length = 80 + +[*.{js,jsx,ts,tsx,json,md,yml,svelte}] +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..c4b1c2da --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,27 @@ +{ + "recommendations": [ + "biomejs.biome", + "svelte.svelte-vscode", + "naumovs.color-highlight", + "fill-labs.dependi", + "ms-azuretools.vscode-docker", + "docker.docker", + "EditorConfig.EditorConfig", + "tamasfe.even-better-toml", + "ctcuff.font-preview", + "github.vscode-github-actions", + "GitHub.copilot", + "GitHub.copilot-chat", + "kisstkondoros.vscode-gutter-preview", + "oderwat.indent-rainbow", + "inlang.vs-code-extension", + "Vercel.turbo-vsc", + "vitest.explorer", + "ms-playwright.playwright", + "tomoki1207.pdf", + "streetsidesoftware.code-spell-checker", + "1YiB.rust-bundle", + "bradlc.vscode-tailwindcss", + "tauri-apps.tauri-vscode" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 3a997867..3b8b77f9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,31 @@ { - "eslint.workingDirectories": [ - { - "mode": "auto" - } - ] + "svelte.enable-ts-plugin": true, + "rust-analyzer.linkedProjects": [ + "infrastructure/eid-wallet/src-tauri/Cargo.toml" + ], + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "source.fixAll.biome": "explicit" + } + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "source.fixAll.biome": "explicit" + } + }, + "[svelte]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "source.fixAll.biome": "explicit" + } + } } diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 00000000..36249bb0 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,9 @@ +{ + "lsp": { + "rust-analyzer": { + "initialization_options": { + "linkedProjects": ["infrastructure/eid-wallet/src-tauri/Cargo.toml"] + } + } + } +} diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..96fffb83 --- /dev/null +++ b/biome.json @@ -0,0 +1,22 @@ +{ + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "formatter": { + "useEditorconfig": true + }, + "overrides": [ + { + "include": ["*.svelte", "*.astro", "*.vue"], + "linter": { + "rules": { + "style": { + "useConst": "off", + "useImportType": "off" + } + } + } + } + ], + "organizeImports": { + "enabled": true + } +} diff --git a/infrastructure/eid-wallet/biome.json b/infrastructure/eid-wallet/biome.json index 31df3c09..07b14fb4 100644 --- a/infrastructure/eid-wallet/biome.json +++ b/infrastructure/eid-wallet/biome.json @@ -1,16 +1,7 @@ { - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "overrides": [ - { - "include": ["*.svelte", "*.astro", "*.vue"], - "linter": { - "rules": { - "style": { - "useConst": "off", - "useImportType": "off" - } - } - } - } - ] + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "extends": ["../../biome.json"], + "organizeImports": { + "include": ["src/**/*.ts", "src/**/*.svelte"] + } } diff --git a/infrastructure/eid-wallet/package.json b/infrastructure/eid-wallet/package.json index ad12f551..3e50760f 100644 --- a/infrastructure/eid-wallet/package.json +++ b/infrastructure/eid-wallet/package.json @@ -1,65 +1,68 @@ { - "name": "eid-wallet", - "version": "0.1.0", - "description": "", - "type": "module", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && npx @biomejs/biome check ./src", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "format": "npx @biomejs/biome format --write ./src", - "check-format": "npx @biomejs/biome format ./src", - "lint": "npx @biomejs/biome lint --write ./src", - "check-lint": "npx @biomejs/biome lint ./src", - "tauri": "tauri", - "storybook": "svelte-kit sync && storybook dev -p 6006", - "build-storybook": "storybook build" - }, - "license": "MIT", - "dependencies": { - "@biomejs/biome": "^1.9.4", - "@hugeicons/core-free-icons": "^1.0.13", - "@hugeicons/svelte": "^1.0.2", - "@tailwindcss/container-queries": "^0.1.1", - "@tauri-apps/api": "^2", - "@tauri-apps/plugin-opener": "^2", - "clsx": "^2.1.1", - "flag-icons": "^7.3.2", - "tailwind-merge": "^3.0.2" - }, - "devDependencies": { - "@chromatic-com/storybook": "^3", - "@storybook/addon-essentials": "^8.6.7", - "@storybook/addon-interactions": "^8.6.7", - "@storybook/blocks": "^8.6.7", - "@storybook/experimental-addon-test": "^8.6.7", - "@storybook/svelte": "^8.6.7", - "@storybook/sveltekit": "^8.6.7", - "@storybook/test": "^8.6.7", - "@storybook/testing-library": "^0.2.2", - "@sveltejs/adapter-static": "^3.0.6", - "@sveltejs/kit": "^2.9.0", - "@sveltejs/vite-plugin-svelte": "^5.0.0", - "@tailwindcss/forms": "^0.5.10", - "@tailwindcss/typography": "^0.5.16", - "@tailwindcss/vite": "^4.0.14", - "@tauri-apps/cli": "^2", - "@types/node": "^22.13.10", - "@vitest/browser": "^3.0.9", - "@vitest/coverage-v8": "^3.0.9", - "autoprefixer": "^10.4.21", - "cupertino-pane": "^1.4.22", - "playwright": "^1.51.1", - "postcss": "^8.5.3", - "storybook": "^8.6.7", - "svelte": "^5.0.0", - "svelte-check": "^4.0.0", - "svelte-gestures": "^5.1.3", - "tailwindcss": "^4.0.14", - "typescript": "~5.6.2", - "vite": "^6.0.3", - "vitest": "^3.0.9" - } + "name": "eid-wallet", + "version": "0.1.0", + "description": "", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && npx @biomejs/biome check ./src", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "npx @biomejs/biome format --write ./src", + "check-format": "npx @biomejs/biome format ./src", + "lint": "npx @biomejs/biome lint --write ./src", + "check-lint": "npx @biomejs/biome lint ./src", + "tauri": "tauri", + "storybook": "svelte-kit sync && storybook dev -p 6006", + "build-storybook": "storybook build" + }, + "license": "MIT", + "dependencies": { + "@hugeicons/core-free-icons": "^1.0.13", + "@hugeicons/svelte": "^1.0.2", + "@tailwindcss/container-queries": "^0.1.1", + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-barcode-scanner": "^2.2.0", + "@tauri-apps/plugin-biometric": "^2.2.0", + "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-store": "^2.2.0", + "clsx": "^2.1.1", + "flag-icons": "^7.3.2", + "tailwind-merge": "^3.0.2" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@chromatic-com/storybook": "^3", + "@storybook/addon-essentials": "^8.6.7", + "@storybook/addon-interactions": "^8.6.7", + "@storybook/blocks": "^8.6.7", + "@storybook/experimental-addon-test": "^8.6.7", + "@storybook/svelte": "^8.6.7", + "@storybook/sveltekit": "^8.6.7", + "@storybook/test": "^8.6.7", + "@storybook/testing-library": "^0.2.2", + "@sveltejs/adapter-static": "^3.0.6", + "@sveltejs/kit": "^2.9.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", + "@tailwindcss/vite": "^4.0.14", + "@tauri-apps/cli": "^2", + "@types/node": "^22.13.10", + "@vitest/browser": "^3.0.9", + "@vitest/coverage-v8": "^3.0.9", + "autoprefixer": "^10.4.21", + "cupertino-pane": "^1.4.22", + "playwright": "^1.51.1", + "postcss": "^8.5.3", + "storybook": "^8.6.7", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "svelte-gestures": "^5.1.3", + "tailwindcss": "^4.0.14", + "typescript": "~5.6.2", + "vite": "^6.0.3", + "vitest": "^3.0.9" + } } diff --git a/infrastructure/eid-wallet/src-tauri/Cargo.lock b/infrastructure/eid-wallet/src-tauri/Cargo.lock index ebdd402e..82b35d11 100644 --- a/infrastructure/eid-wallet/src-tauri/Cargo.lock +++ b/infrastructure/eid-wallet/src-tauri/Cargo.lock @@ -62,6 +62,18 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -267,6 +279,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + [[package]] name = "bitflags" version = "1.3.2" @@ -282,6 +300,15 @@ dependencies = [ "serde", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -719,6 +746,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -822,11 +850,17 @@ checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" name = "eid-wallet" version = "0.1.0" dependencies = [ + "argon2", + "rand_core 0.6.4", "serde", "serde_json", "tauri", "tauri-build", + "tauri-plugin-barcode-scanner", + "tauri-plugin-biometric", "tauri-plugin-opener", + "tauri-plugin-store", + "thiserror 2.0.12", ] [[package]] @@ -2477,6 +2511,17 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "pathdiff" version = "0.2.3" @@ -3823,6 +3868,35 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-barcode-scanner" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c93f902a129a56710cd977210ac2c3f7f114c89639e6092f9c78483130a3a6e1" +dependencies = [ + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", +] + +[[package]] +name = "tauri-plugin-biometric" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97052263d415490460196611ec0d9b5582dcff26062c63e941f5dc0f3069eae6" +dependencies = [ + "log", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", +] + [[package]] name = "tauri-plugin-opener" version = "2.2.6" @@ -3845,6 +3919,22 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-store" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c0c08fae6995909f5e9a0da6038273b750221319f2c0f3b526d6de1cde21505" +dependencies = [ + "dunce", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", + "tokio", + "tracing", +] + [[package]] name = "tauri-runtime" version = "2.4.0" @@ -4076,9 +4166,21 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "tokio-rustls" version = "0.26.2" diff --git a/infrastructure/eid-wallet/src-tauri/Cargo.toml b/infrastructure/eid-wallet/src-tauri/Cargo.toml index 742b6937..6c42a7ad 100644 --- a/infrastructure/eid-wallet/src-tauri/Cargo.toml +++ b/infrastructure/eid-wallet/src-tauri/Cargo.toml @@ -22,4 +22,14 @@ tauri = { version = "2", features = [] } tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" +tauri-plugin-store = "2.2.0" + + +argon2 = { version = "0.5.3" } +rand_core = { version = "0.6", features = ["std"] } +thiserror = { version = "2.0.11" } + +[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] +tauri-plugin-barcode-scanner = "2" +tauri-plugin-biometric = "2.2.0" diff --git a/infrastructure/eid-wallet/src-tauri/Info.ios.plist b/infrastructure/eid-wallet/src-tauri/Info.ios.plist new file mode 100644 index 00000000..8f2e722b --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/Info.ios.plist @@ -0,0 +1,10 @@ + + + + + NSFaceIDUsageDescription + Authenticate with biometric + NSCameraUsageDescription + Read QR codes + + diff --git a/infrastructure/eid-wallet/src-tauri/capabilities/default.json b/infrastructure/eid-wallet/src-tauri/capabilities/default.json deleted file mode 100644 index a6ed8523..00000000 --- a/infrastructure/eid-wallet/src-tauri/capabilities/default.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../gen/schemas/desktop-schema.json", - "identifier": "default", - "description": "Capability for the main window", - "windows": ["main"], - "permissions": ["core:default", "opener:default"] -} diff --git a/infrastructure/eid-wallet/src-tauri/capabilities/mobile.json b/infrastructure/eid-wallet/src-tauri/capabilities/mobile.json new file mode 100644 index 00000000..159794f7 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/capabilities/mobile.json @@ -0,0 +1,19 @@ +{ + "$schema": "../gen/schemas/mobile-schema.json", + "identifier": "mobile-capability", + "description": "Capability for the main window on mobile", + "windows": [ + "main" + ], + "permissions": [ + "core:default", + "opener:default", + "store:default", + "biometric:default", + "barcode-scanner:default" + ], + "platforms": [ + "iOS", + "android" + ] +} \ No newline at end of file diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/.gitignore b/infrastructure/eid-wallet/src-tauri/gen/apple/.gitignore new file mode 100644 index 00000000..6726e2f8 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/.gitignore @@ -0,0 +1,3 @@ +xcuserdata/ +build/ +Externals/ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 00000000..a6ac2a8c Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 00000000..2869541f Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 00000000..2869541f Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 00000000..cf265a45 Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png new file mode 100644 index 00000000..29c9746c Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 00000000..a4e68c8d Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png new file mode 100644 index 00000000..a4e68c8d Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png new file mode 100644 index 00000000..e4adcbce Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 00000000..2869541f Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 00000000..a414e65b Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png new file mode 100644 index 00000000..a414e65b Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png new file mode 100644 index 00000000..a0807e5d Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png new file mode 100644 index 00000000..704c9291 Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 00000000..a0807e5d Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 00000000..2a9fbc26 Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 00000000..2cdf1848 Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png new file mode 100644 index 00000000..4723e4b4 Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 00000000..f26fee45 Binary files /dev/null and b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..90eea7ec --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "AppIcon-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "AppIcon-512@2x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/Contents.json b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/ExportOptions.plist b/infrastructure/eid-wallet/src-tauri/gen/apple/ExportOptions.plist new file mode 100644 index 00000000..0428a171 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/ExportOptions.plist @@ -0,0 +1,8 @@ + + + + + method + debugging + + diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/LaunchScreen.storyboard b/infrastructure/eid-wallet/src-tauri/gen/apple/LaunchScreen.storyboard new file mode 100644 index 00000000..81b5f90e --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/LaunchScreen.storyboard @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Podfile b/infrastructure/eid-wallet/src-tauri/gen/apple/Podfile new file mode 100644 index 00000000..abde6d80 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/Podfile @@ -0,0 +1,21 @@ +# Uncomment the next line to define a global platform for your project + +target 'eid-wallet_iOS' do +platform :ios, '13.0' + # Pods for eid-wallet_iOS +end + +target 'eid-wallet_macOS' do +platform :osx, '11.0' + # Pods for eid-wallet_macOS +end + +# Delete the deployment target for iOS and macOS, causing it to be inherited from the Podfile +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' + config.build_settings.delete 'MACOSX_DEPLOYMENT_TARGET' + end + end +end diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Sources/eid-wallet/bindings/bindings.h b/infrastructure/eid-wallet/src-tauri/gen/apple/Sources/eid-wallet/bindings/bindings.h new file mode 100644 index 00000000..51522007 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/Sources/eid-wallet/bindings/bindings.h @@ -0,0 +1,8 @@ +#pragma once + +namespace ffi { + extern "C" { + void start_app(); + } +} + diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/Sources/eid-wallet/main.mm b/infrastructure/eid-wallet/src-tauri/gen/apple/Sources/eid-wallet/main.mm new file mode 100644 index 00000000..7793a9d5 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/Sources/eid-wallet/main.mm @@ -0,0 +1,6 @@ +#include "bindings/bindings.h" + +int main(int argc, char * argv[]) { + ffi::start_app(); + return 0; +} diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj new file mode 100644 index 00000000..f58fe249 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj @@ -0,0 +1,499 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 1C11B244952BAFF14E4D4141 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5279FA309F2DB44506822E5D /* MetalKit.framework */; }; + 4D6AC217595CF08802C9CF46 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22B32503AE94582901B0FAEC /* CoreGraphics.framework */; }; + 4E754F655E0516959C80FADD /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDF2205F9536D029E0940F79 /* Metal.framework */; }; + 838BE4B37D8F54069C9B82FE /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = D515D2306077569F964CC0DF /* main.mm */; }; + 91082224B29E1D9A9D87EA83 /* libapp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DFA24DF1761A162D24B41D74 /* libapp.a */; }; + B2107FCDCA9872BB452B52D2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 072F58C0115039255B267035 /* LaunchScreen.storyboard */; }; + BAAFC114A80097C599FF1498 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = C636AF063138DEFB3E6A6052 /* assets */; }; + CCED10424E419D684DFE2CCC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 084A9EFEC9BEF4DCF96B6BDD /* UIKit.framework */; }; + CD413A6E4CD07CBF5418F94A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EE003FFC6919B8C953DCDDE4 /* Assets.xcassets */; }; + D0CD07AE159D1CFDEA977479 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6694E9EC8CE5328251C3F669 /* Security.framework */; }; + D6315A8B0A92F0B08471413B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 673F0CB787E12AAB4660FE44 /* WebKit.framework */; }; + E3142FD942E61E18DFCBCC93 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEB29855FD57B9D7DC26CF5B /* QuartzCore.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0415B3A57B47E1B636C4A0A5 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = ""; }; + 072F58C0115039255B267035 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 084A9EFEC9BEF4DCF96B6BDD /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 146D168CE7F512A66DF2C79B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 22B32503AE94582901B0FAEC /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 40ABD3C35AA06E16DA39304E /* main.rs */ = {isa = PBXFileReference; lastKnownFileType = text; path = main.rs; sourceTree = ""; }; + 5279FA309F2DB44506822E5D /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; }; + 6694E9EC8CE5328251C3F669 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 673F0CB787E12AAB4660FE44 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; + 6DACB922029A0F360BDBD599 /* eid-wallet_iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "eid-wallet_iOS.entitlements"; sourceTree = ""; }; + 754F6FBE80C64C6326B8E0D5 /* eid-wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "eid-wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C636AF063138DEFB3E6A6052 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = SOURCE_ROOT; }; + D515D2306077569F964CC0DF /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; + DC24C9E28ECAF90D40C14313 /* lib.rs */ = {isa = PBXFileReference; lastKnownFileType = text; path = lib.rs; sourceTree = ""; }; + DDF2205F9536D029E0940F79 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; + DEB29855FD57B9D7DC26CF5B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DFA24DF1761A162D24B41D74 /* libapp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libapp.a; sourceTree = ""; }; + EE003FFC6919B8C953DCDDE4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9AF5DA4B7074325218DAFA17 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 91082224B29E1D9A9D87EA83 /* libapp.a in Frameworks */, + 4D6AC217595CF08802C9CF46 /* CoreGraphics.framework in Frameworks */, + 4E754F655E0516959C80FADD /* Metal.framework in Frameworks */, + 1C11B244952BAFF14E4D4141 /* MetalKit.framework in Frameworks */, + E3142FD942E61E18DFCBCC93 /* QuartzCore.framework in Frameworks */, + D0CD07AE159D1CFDEA977479 /* Security.framework in Frameworks */, + CCED10424E419D684DFE2CCC /* UIKit.framework in Frameworks */, + D6315A8B0A92F0B08471413B /* WebKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0D0DAFCCB1F1630FE5DF02CB /* Products */ = { + isa = PBXGroup; + children = ( + 754F6FBE80C64C6326B8E0D5 /* eid-wallet.app */, + ); + name = Products; + sourceTree = ""; + }; + 1E772D6A351CCE2327B74186 /* eid-wallet_iOS */ = { + isa = PBXGroup; + children = ( + 6DACB922029A0F360BDBD599 /* eid-wallet_iOS.entitlements */, + 146D168CE7F512A66DF2C79B /* Info.plist */, + ); + path = "eid-wallet_iOS"; + sourceTree = ""; + }; + 234EEFFEA1E82BEEC00B358A = { + isa = PBXGroup; + children = ( + C636AF063138DEFB3E6A6052 /* assets */, + EE003FFC6919B8C953DCDDE4 /* Assets.xcassets */, + 072F58C0115039255B267035 /* LaunchScreen.storyboard */, + 1E772D6A351CCE2327B74186 /* eid-wallet_iOS */, + A62230F71CECA336EFCEBE94 /* Externals */, + A6ADBB8DF8929ECEC47EC7DA /* Sources */, + 61D5698478D645257706C604 /* src */, + BDEDF63E45DECA4A3486AF0E /* Frameworks */, + 0D0DAFCCB1F1630FE5DF02CB /* Products */, + ); + sourceTree = ""; + }; + 61D5698478D645257706C604 /* src */ = { + isa = PBXGroup; + children = ( + DC24C9E28ECAF90D40C14313 /* lib.rs */, + 40ABD3C35AA06E16DA39304E /* main.rs */, + ); + name = src; + path = ../../src; + sourceTree = ""; + }; + A62230F71CECA336EFCEBE94 /* Externals */ = { + isa = PBXGroup; + children = ( + ); + path = Externals; + sourceTree = ""; + }; + A6ADBB8DF8929ECEC47EC7DA /* Sources */ = { + isa = PBXGroup; + children = ( + BB8DA2E53FA34A0E37D18116 /* eid-wallet */, + ); + path = Sources; + sourceTree = ""; + }; + BB8DA2E53FA34A0E37D18116 /* eid-wallet */ = { + isa = PBXGroup; + children = ( + D515D2306077569F964CC0DF /* main.mm */, + F2C16594FD92B8F9B7E43507 /* bindings */, + ); + path = "eid-wallet"; + sourceTree = ""; + }; + BDEDF63E45DECA4A3486AF0E /* Frameworks */ = { + isa = PBXGroup; + children = ( + 22B32503AE94582901B0FAEC /* CoreGraphics.framework */, + DFA24DF1761A162D24B41D74 /* libapp.a */, + DDF2205F9536D029E0940F79 /* Metal.framework */, + 5279FA309F2DB44506822E5D /* MetalKit.framework */, + DEB29855FD57B9D7DC26CF5B /* QuartzCore.framework */, + 6694E9EC8CE5328251C3F669 /* Security.framework */, + 084A9EFEC9BEF4DCF96B6BDD /* UIKit.framework */, + 673F0CB787E12AAB4660FE44 /* WebKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F2C16594FD92B8F9B7E43507 /* bindings */ = { + isa = PBXGroup; + children = ( + 0415B3A57B47E1B636C4A0A5 /* bindings.h */, + ); + path = bindings; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6595F7B25D907151021EDDEB /* eid-wallet_iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 08FC9E4A17D042EC7F151325 /* Build configuration list for PBXNativeTarget "eid-wallet_iOS" */; + buildPhases = ( + 201C6D63F069654C8B494CB8 /* Build Rust Code */, + 2F949954338F0E9247A43C22 /* Sources */, + 4D9662C6F7CA7BA9E7B47BDB /* Resources */, + 9AF5DA4B7074325218DAFA17 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "eid-wallet_iOS"; + productName = "eid-wallet_iOS"; + productReference = 754F6FBE80C64C6326B8E0D5 /* eid-wallet.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0E48EB77F51237830E86BD7F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; + }; + buildConfigurationList = E5B525C97C77034D6B61DFD6 /* Build configuration list for PBXProject "eid-wallet" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = 234EEFFEA1E82BEEC00B358A; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6595F7B25D907151021EDDEB /* eid-wallet_iOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4D9662C6F7CA7BA9E7B47BDB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CD413A6E4CD07CBF5418F94A /* Assets.xcassets in Resources */, + B2107FCDCA9872BB452B52D2 /* LaunchScreen.storyboard in Resources */, + BAAFC114A80097C599FF1498 /* assets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 201C6D63F069654C8B494CB8 /* Build Rust Code */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Build Rust Code"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a", + "$(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a", + "$(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/libapp.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "pnpm tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths \"${FRAMEWORK_SEARCH_PATHS:?}\" --header-search-paths \"${HEADER_SEARCH_PATHS:?}\" --gcc-preprocessor-definitions \"${GCC_PREPROCESSOR_DEFINITIONS:-}\" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2F949954338F0E9247A43C22 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 838BE4B37D8F54069C9B82FE /* main.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 3CF46A769820012140B5DEB8 /* release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + }; + name = release; + }; + 4CA2C3263E163B2931B82729 /* debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = debug; + }; + 94881BB625CAD2CAC180D9C1 /* release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ARCHS = ( + arm64, + "arm64-sim", + ); + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = 7F2T2WK6DR; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64"; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\".\"", + ); + INFOPLIST_FILE = "eid-wallet_iOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = ( + "$(inherited)", + "$(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION)", + "$(SDKROOT)/usr/lib/swift", + "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + ); + "LIBRARY_SEARCH_PATHS[arch=arm64]" = ( + "$(inherited)", + "$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)", + "$(SDKROOT)/usr/lib/swift", + "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + ); + "LIBRARY_SEARCH_PATHS[arch=x86_64]" = ( + "$(inherited)", + "$(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION)", + "$(SDKROOT)/usr/lib/swift", + "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.eid-wallet.app; + PRODUCT_NAME = "eid-wallet"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 arm64-sim"; + }; + name = release; + }; + FB09AAA6FA8DA19CE414A277 /* debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ARCHS = ( + arm64, + "arm64-sim", + ); + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = 7F2T2WK6DR; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64"; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\".\"", + ); + INFOPLIST_FILE = "eid-wallet_iOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = ( + "$(inherited)", + "$(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION)", + "$(SDKROOT)/usr/lib/swift", + "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + ); + "LIBRARY_SEARCH_PATHS[arch=arm64]" = ( + "$(inherited)", + "$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)", + "$(SDKROOT)/usr/lib/swift", + "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + ); + "LIBRARY_SEARCH_PATHS[arch=x86_64]" = ( + "$(inherited)", + "$(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION)", + "$(SDKROOT)/usr/lib/swift", + "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.eid-wallet.app; + PRODUCT_NAME = "eid-wallet"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 arm64-sim"; + }; + name = debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 08FC9E4A17D042EC7F151325 /* Build configuration list for PBXNativeTarget "eid-wallet_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FB09AAA6FA8DA19CE414A277 /* debug */, + 94881BB625CAD2CAC180D9C1 /* release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = debug; + }; + E5B525C97C77034D6B61DFD6 /* Build configuration list for PBXProject "eid-wallet" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CA2C3263E163B2931B82729 /* debug */, + 3CF46A769820012140B5DEB8 /* release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0E48EB77F51237830E86BD7F /* Project object */; +} diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..ac90d5ac --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + BuildSystemType + Original + DisableBuildSystemDeprecationDiagnostic + + + diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/xcshareddata/xcschemes/eid-wallet_iOS.xcscheme b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/xcshareddata/xcschemes/eid-wallet_iOS.xcscheme new file mode 100644 index 00000000..cc03c9c2 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/xcshareddata/xcschemes/eid-wallet_iOS.xcscheme @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet_iOS/Info.plist b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet_iOS/Info.plist new file mode 100644 index 00000000..eb0c6606 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet_iOS/Info.plist @@ -0,0 +1,48 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.1.0 + CFBundleVersion + 0.1.0 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + metal + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + NSFaceIDUsageDescription + Authenticate with biometric + NSCameraUsageDescription + Read QR codes + + \ No newline at end of file diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet_iOS/eid-wallet_iOS.entitlements b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet_iOS/eid-wallet_iOS.entitlements new file mode 100644 index 00000000..0c67376e --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet_iOS/eid-wallet_iOS.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/project.yml b/infrastructure/eid-wallet/src-tauri/gen/apple/project.yml new file mode 100644 index 00000000..ccbe64bb --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/project.yml @@ -0,0 +1,91 @@ +name: eid-wallet +options: + bundleIdPrefix: com.eid-wallet.app + deploymentTarget: + iOS: 13.0 +fileGroups: [../../src] +configs: + debug: debug + release: release +settingGroups: + app: + base: + PRODUCT_NAME: eid-wallet + PRODUCT_BUNDLE_IDENTIFIER: com.eid-wallet.app +targetTemplates: + app: + type: application + sources: + - path: Sources + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + settings: + groups: [app] +targets: + eid-wallet_iOS: + type: application + platform: iOS + sources: + - path: Sources + - path: Assets.xcassets + - path: Externals + - path: eid-wallet_iOS + - path: assets + buildPhase: resources + type: folder + - path: LaunchScreen.storyboard + info: + path: eid-wallet_iOS/Info.plist + properties: + LSRequiresIPhoneOS: true + UILaunchStoryboardName: LaunchScreen + UIRequiredDeviceCapabilities: [arm64, metal] + UISupportedInterfaceOrientations: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + UISupportedInterfaceOrientations~ipad: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationPortraitUpsideDown + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + CFBundleShortVersionString: 0.1.0 + CFBundleVersion: 0.1.0 + entitlements: + path: eid-wallet_iOS/eid-wallet_iOS.entitlements + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + settings: + base: + ENABLE_BITCODE: false + ARCHS: [arm64, arm64-sim] + VALID_ARCHS: arm64 arm64-sim + LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) + LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) + LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) $(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true + EXCLUDED_ARCHS[sdk=iphonesimulator*]: arm64 + EXCLUDED_ARCHS[sdk=iphoneos*]: arm64-sim x86_64 + groups: [app] + dependencies: + - framework: libapp.a + embed: false + - sdk: CoreGraphics.framework + - sdk: Metal.framework + - sdk: MetalKit.framework + - sdk: QuartzCore.framework + - sdk: Security.framework + - sdk: UIKit.framework + - sdk: WebKit.framework + preBuildScripts: + - script: pnpm tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + name: Build Rust Code + basedOnDependencyAnalysis: false + outputFiles: + - $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a + - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a + - $(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/libapp.a \ No newline at end of file diff --git a/infrastructure/eid-wallet/src-tauri/src/errors.rs b/infrastructure/eid-wallet/src-tauri/src/errors.rs new file mode 100644 index 00000000..72322934 --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/src/errors.rs @@ -0,0 +1,19 @@ +// create the error type that represents all errors possible in our program +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Io(#[from] std::io::Error), + + #[error("Other: `{0}`")] + Other(String), +} + +// we must manually implement serde::Serialize +impl serde::Serialize for Error { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + serializer.serialize_str(self.to_string().as_ref()) + } +} diff --git a/infrastructure/eid-wallet/src-tauri/src/funcs/mod.rs b/infrastructure/eid-wallet/src-tauri/src/funcs/mod.rs new file mode 100644 index 00000000..97227e6d --- /dev/null +++ b/infrastructure/eid-wallet/src-tauri/src/funcs/mod.rs @@ -0,0 +1,23 @@ +use argon2::{ + password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, + Argon2, +}; + +use crate::errors::Error; + +pub fn hash(pin: String) -> Result { + let salt = SaltString::generate(&mut OsRng); + let argon2 = Argon2::default(); + let pin_hash = argon2 + .hash_password(pin.as_bytes(), &salt) + .map_err(|err| Error::Other(err.to_string()))? + .to_string(); + Ok(pin_hash) +} + +pub fn verify(pin: String, hash: String) -> Result { + let argon2 = Argon2::default(); + let parsed_hash = PasswordHash::new(&hash).map_err(|err| Error::Other(err.to_string()))?; + let is_valid = argon2.verify_password(pin.as_bytes(), &parsed_hash).is_ok(); + Ok(is_valid) +} diff --git a/infrastructure/eid-wallet/src-tauri/src/lib.rs b/infrastructure/eid-wallet/src-tauri/src/lib.rs index 4a277ef3..410df29f 100644 --- a/infrastructure/eid-wallet/src-tauri/src/lib.rs +++ b/infrastructure/eid-wallet/src-tauri/src/lib.rs @@ -1,14 +1,52 @@ +mod errors; +mod funcs; + // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ +// #[tauri::command] +// fn greet(name: &str) -> String { +// format!("Hello, {}! You've been greeted from Rust!", name) +// } + +/// Hashes a PIN using Argon2 with a random salt +/// +/// # Arguments +/// * `pin` - A string containing the user's PIN +/// +/// # Returns +/// * `Result` - The hashed PIN string or an error message +#[tauri::command] +async fn hash(pin: String) -> Result { + funcs::hash(pin).map_err(|err| format!("Failed to hash PIN: {}", err)) +} + +/// Verifies a PIN against a stored hash using Argon2 +/// +/// # Arguments +/// * `pin` - A string containing the user's PIN to verify +/// * `hash` - The stored hash to compare against +/// +/// # Returns +/// * `Result` - Whether the PIN matches the hash, or an error message #[tauri::command] -fn greet(name: &str) -> String { - format!("Hello, {}! You've been greeted from Rust!", name) +async fn verify(pin: String, hash: String) -> Result { + funcs::verify(pin, hash).map_err(|err| format!("Failed to verify PIN: {}", err)) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) - .invoke_handler(tauri::generate_handler![greet]) + .plugin(tauri_plugin_store::Builder::new().build()) + .setup(move |_app| { + #[cfg(mobile)] + { + _app.handle().plugin(tauri_plugin_biometric::init())?; + _app.handle().plugin(tauri_plugin_barcode_scanner::init())?; + } + Ok(()) + }) + // Register the commands with Tauri. + .invoke_handler(tauri::generate_handler![hash, verify]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/infrastructure/eid-wallet/src-tauri/tauri.conf.json b/infrastructure/eid-wallet/src-tauri/tauri.conf.json index b2213169..3b0ce118 100644 --- a/infrastructure/eid-wallet/src-tauri/tauri.conf.json +++ b/infrastructure/eid-wallet/src-tauri/tauri.conf.json @@ -1,35 +1,36 @@ { - "$schema": "https://schema.tauri.app/config/2", - "productName": "eid-wallet", - "version": "0.1.0", - "identifier": "com.eid-wallet.app", - "build": { - "beforeDevCommand": "pnpm dev", - "devUrl": "http://localhost:1420", - "beforeBuildCommand": "pnpm build", - "frontendDist": "../build" - }, - "app": { - "windows": [ - { - "title": "eid-wallet", - "width": 800, - "height": 600 - } - ], - "security": { - "csp": null - } - }, - "bundle": { - "active": true, - "targets": "all", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ] - } + "$schema": "https://schema.tauri.app/config/2", + "productName": "eid-wallet", + "version": "0.1.0", + "identifier": "com.eid-wallet.app", + "build": { + "beforeDevCommand": "pnpm dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "pnpm build", + "frontendDist": "../build" + }, + "app": { + "windows": [ + { + "title": "eid-wallet", + "width": 800, + "height": 600 + } + ], + "security": { + "capabilities": ["mobile-capability"], + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } } diff --git a/infrastructure/eid-wallet/src/app.css b/infrastructure/eid-wallet/src/app.css index 5f3ffe2e..93fccdd4 100644 --- a/infrastructure/eid-wallet/src/app.css +++ b/infrastructure/eid-wallet/src/app.css @@ -2,170 +2,170 @@ @import "flag-icons/css/flag-icons.min.css"; @font-face { - font-family: "Archivo"; - src: url("/fonts/Archivo-VariableFont_wdth,wght.ttf") format("truetype"); - font-weight: 100 900; - font-style: normal; + font-family: "Archivo"; + src: url("/fonts/Archivo-VariableFont_wdth,wght.ttf") format("truetype"); + font-weight: 100 900; + font-style: normal; } @layer base { - /* Typography */ - h1 { - @apply text-[90px]/[1.5] text-black font-semibold; - } - - h2 { - @apply text-6xl/[1.5] text-black font-semibold; - } - - h3 { - @apply text-3xl/[1.5] text-black font-semibold; - } - - h4 { - @apply text-xl/[1.5] text-black font-semibold; - } - - p { - @apply text-base/[1.5] text-black font-normal; - } - - .small { - @apply text-xs/[1.5] text-black font-normal; - } + /* Typography */ + h1 { + @apply text-[90px]/[1.5] text-black font-semibold; + } + + h2 { + @apply text-6xl/[1.5] text-black font-semibold; + } + + h3 { + @apply text-3xl/[1.5] text-black font-semibold; + } + + h4 { + @apply text-xl/[1.5] text-black font-semibold; + } + + p { + @apply text-base/[1.5] text-black font-normal; + } + + .small { + @apply text-xs/[1.5] text-black font-normal; + } } @theme { - /* Custom theme */ - --color-primary: #8e52ff; - --color-primary-100: #e8dcff; - --color-primary-200: #d2baff; - --color-primary-300: #bb97ff; - --color-primary-400: #a575ff; - --color-primary-500: #8e52ff; - - --color-secondary: #73efd5; - --color-secondary-100: #e3fcf7; - --color-secondary-200: #c7f9ee; - --color-secondary-300: #abf6e6; - --color-secondary-400: #8ff2dd; - --color-secondary-500: #73efd5; - - --color-white: #ffffff; - --color-gray: #f5f5f5; - - --color-black: #1f1f1f; - --color-black-100: #d2d2d2; - --color-black-300: #a5a5a5; - --color-black-500: #797979; - --color-black-700: #4c4c4c; - --color-black-900: #1f1f1f; - - --color-danger: #ff5255; - --color-danger-100: #ffdcdd; - --color-danger-200: #ffb1a7; - --color-danger-300: #ff968e; - --color-danger-400: #ff7b77; - --color-danger-500: #ff5255; + /* Custom theme */ + --color-primary: #8e52ff; + --color-primary-100: #e8dcff; + --color-primary-200: #d2baff; + --color-primary-300: #bb97ff; + --color-primary-400: #a575ff; + --color-primary-500: #8e52ff; + + --color-secondary: #73efd5; + --color-secondary-100: #e3fcf7; + --color-secondary-200: #c7f9ee; + --color-secondary-300: #abf6e6; + --color-secondary-400: #8ff2dd; + --color-secondary-500: #73efd5; + + --color-white: #ffffff; + --color-gray: #f5f5f5; + + --color-black: #1f1f1f; + --color-black-100: #d2d2d2; + --color-black-300: #a5a5a5; + --color-black-500: #797979; + --color-black-700: #4c4c4c; + --color-black-900: #1f1f1f; + + --color-danger: #ff5255; + --color-danger-100: #ffdcdd; + --color-danger-200: #ffb1a7; + --color-danger-300: #ff968e; + --color-danger-400: #ff7b77; + --color-danger-500: #ff5255; } body { - font-family: "Archivo", sans-serif; - padding-top: env(safe-area-inset-top); - padding-bottom: env(safe-area-inset-bottom); - padding-left: env(safe-area-inset-left); - padding-right: env(safe-area-inset-right); + font-family: "Archivo", sans-serif; + padding-top: env(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); } /* Ensure background remains correct during transitions */ :root[data-transition]::view-transition-group(root), :root[data-transition]::view-transition-old(root), :root[data-transition]::view-transition-new(root) { - background-color: white !important; /* Default to white */ + background-color: white !important; /* Default to white */ } .dark:root[data-transition]::view-transition-group(root), .dark:root[data-transition]::view-transition-old(root), .dark:root[data-transition]::view-transition-new(root) { - background-color: #0b0d13 !important; /* Use dark background in dark mode */ + background-color: #0b0d13 !important; /* Use dark background in dark mode */ } /* Prevent flickering */ :root[data-transition]::view-transition-old(root), :root[data-transition]::view-transition-new(root) { - contain: paint; - will-change: transform, opacity; + contain: paint; + will-change: transform, opacity; } /* Slide-in from the right without fade */ @keyframes slide-from-right { - from { - transform: translateX(100%); /* Start from the right */ - opacity: 1; /* Ensure fully visible */ - } - to { - transform: translateX(0); /* Move to original position */ - opacity: 1; - } + from { + transform: translateX(100%); /* Start from the right */ + opacity: 1; /* Ensure fully visible */ + } + to { + transform: translateX(0); /* Move to original position */ + opacity: 1; + } } /* Slide-out to the right without fade */ @keyframes slide-to-right { - from { - transform: translateX(0); /* Start at original position */ - opacity: 1; - } - to { - transform: translateX(100%); /* Move to the right */ - opacity: 1; - } + from { + transform: translateX(0); /* Start at original position */ + opacity: 1; + } + to { + transform: translateX(100%); /* Move to the right */ + opacity: 1; + } } /* Slide-in from the left without fade */ @keyframes slide-from-left { - from { - transform: translateX(-100%); /* Start from the left */ - opacity: 1; - } - to { - transform: translateX(0); /* Move to original position */ - opacity: 1; - } + from { + transform: translateX(-100%); /* Start from the left */ + opacity: 1; + } + to { + transform: translateX(0); /* Move to original position */ + opacity: 1; + } } /* Slide-out to the left without fade */ @keyframes slide-to-left { - from { - transform: translateX(0); /* Start at original position */ - opacity: 1; - } - to { - transform: translateX(-100%); /* Move to the left */ - opacity: 1; - } + from { + transform: translateX(0); /* Start at original position */ + opacity: 1; + } + to { + transform: translateX(-100%); /* Move to the left */ + opacity: 1; + } } @keyframes fade-out { - from { - opacity: 1; - } - to { - opacity: 0; - } + from { + opacity: 1; + } + to { + opacity: 0; + } } :root[data-transition]::view-transition-old(root) { - animation: 400ms ease-out both fade-out; + animation: 400ms ease-out both fade-out; } :root[data-transition="right"]::view-transition-new(root) { - animation: 200ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right; - position: relative; - z-index: 1; + animation: 200ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right; + position: relative; + z-index: 1; } :root[data-transition="left"]::view-transition-new(root) { - animation: 200ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left; - position: relative; - z-index: 1; + animation: 200ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left; + position: relative; + z-index: 1; } diff --git a/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.stories.ts b/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.stories.ts index 60d4a38f..d77f3653 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.stories.ts +++ b/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.stories.ts @@ -2,20 +2,20 @@ import type { ComponentProps } from "svelte"; import AppNav from "./AppNav.svelte"; export default { - title: "Fragments/AppNav", - component: AppNav, - tags: ["autodocs"], - render: (args: { - Component: AppNav; - props: ComponentProps; - }) => ({ - Component: AppNav, - props: args, - }), + title: "Fragments/AppNav", + component: AppNav, + tags: ["autodocs"], + render: (args: { + Component: AppNav; + props: ComponentProps; + }) => ({ + Component: AppNav, + props: args, + }), }; export const Basic = { - args: { - title: "Settings", - }, + args: { + title: "Settings", + }, }; diff --git a/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.svelte b/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.svelte index e5cc3743..5743cc36 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.svelte +++ b/infrastructure/eid-wallet/src/lib/fragments/AppNav/AppNav.svelte @@ -4,15 +4,15 @@ import { cn } from "$lib/utils"; import { ArrowLeft01Icon, Settings02Icon } from "@hugeicons/core-free-icons"; import type { HTMLAttributes } from "svelte/elements"; interface IHeroProps extends HTMLAttributes { - title?: string; - titleClasses?: string; - iconColor?: string; + title?: string; + titleClasses?: string; + iconColor?: string; } const { - title, - titleClasses, - iconColor = "black", - ...restProps + title, + titleClasses, + iconColor = "black", + ...restProps }: IHeroProps = $props(); const baseClasses = "w-full relative flex justify-center h-14 items-center"; diff --git a/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.stories.ts b/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.stories.ts index 6978b3f9..de5ffd5f 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.stories.ts +++ b/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.stories.ts @@ -2,30 +2,30 @@ import type { ComponentProps } from "svelte"; import Hero from "./Hero.svelte"; export default { - title: "Fragments/Hero", - component: Hero, - tags: ["autodocs"], - render: (args: { - Component: Hero; - props: ComponentProps; - }) => ({ - Component: Hero, - props: args, - }), + title: "Fragments/Hero", + component: Hero, + tags: ["autodocs"], + render: (args: { + Component: Hero; + props: ComponentProps; + }) => ({ + Component: Hero, + props: args, + }), }; export const Basic = { - args: { - title: "Create PIN", - subtitle: "Create a PIN to protect your wallet", - }, + args: { + title: "Create PIN", + subtitle: "Create a PIN to protect your wallet", + }, }; export const WithSettings = { - args: { - title: "Good morning!", - subtitle: "Don't forget to drink water.", - titleClasses: "-mb-2", - showSettings: true, - }, + args: { + title: "Good morning!", + subtitle: "Don't forget to drink water.", + titleClasses: "-mb-2", + showSettings: true, + }, }; diff --git a/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.svelte b/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.svelte index d84b2617..938a8a69 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.svelte +++ b/infrastructure/eid-wallet/src/lib/fragments/Hero/Hero.svelte @@ -5,18 +5,18 @@ import { Settings02Icon } from "@hugeicons/core-free-icons"; import type { HTMLAttributes } from "svelte/elements"; interface IHeroProps extends HTMLAttributes { - title?: string; - subtitle?: string; - showSettings?: boolean; - titleClasses?: string; + title?: string; + subtitle?: string; + showSettings?: boolean; + titleClasses?: string; } const { - title, - subtitle, - showSettings = false, - titleClasses, - children, - ...restProps + title, + subtitle, + showSettings = false, + titleClasses, + children, + ...restProps }: IHeroProps = $props(); const baseClasses = "w-full flex justify-between items-center"; diff --git a/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.stories.ts b/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.stories.ts index e7c221bb..2a720a98 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.stories.ts +++ b/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.stories.ts @@ -1,53 +1,53 @@ import IdentityCard from "./IdentityCard.svelte"; interface userData { - [fieldName: string]: string; + [fieldName: string]: string; } export default { - title: "UI/IdentityCard", - component: IdentityCard, - tags: ["autodocs"], - render: (args: { - variant: string; - userId: string; - shareBtn: () => void; - viewBtn: () => void; - userData: userData; - usedStorage: number; - totalStorage: number; - }) => ({ - Component: IdentityCard, - props: args, - }), + title: "UI/IdentityCard", + component: IdentityCard, + tags: ["autodocs"], + render: (args: { + variant: string; + userId: string; + shareBtn: () => void; + viewBtn: () => void; + userData: userData; + usedStorage: number; + totalStorage: number; + }) => ({ + Component: IdentityCard, + props: args, + }), }; export const eName = { - args: { - variant: "eName", - userId: "ananyayayayaya", - shareBtn: () => alert("Share"), - viewBtn: () => alert("View"), - }, + args: { + variant: "eName", + userId: "ananyayayayaya", + shareBtn: () => alert("Share"), + viewBtn: () => alert("View"), + }, }; export const ePassport = { - args: { - variant: "ePassport", - viewBtn: () => alert("View"), - userData: { - Name: "Ananya", - Dob: "29 Nov 2003", - Nationality: "Indian", - Passport: "234dfvgsdfg", - }, - }, + args: { + variant: "ePassport", + viewBtn: () => alert("View"), + userData: { + Name: "Ananya", + Dob: "29 Nov 2003", + Nationality: "Indian", + Passport: "234dfvgsdfg", + }, + }, }; export const eVault = { - args: { - variant: "eVault", - usedStorage: "15", - totalStorage: "80", - }, + args: { + variant: "eVault", + usedStorage: "15", + totalStorage: "80", + }, }; diff --git a/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte b/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte index c2dae005..98483ab4 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte +++ b/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte @@ -2,43 +2,43 @@ import * as Button from "$lib/ui/Button"; import { cn } from "$lib/utils"; import { - CheckmarkBadge02Icon, - Upload03Icon, - ViewIcon, + CheckmarkBadge02Icon, + Upload03Icon, + ViewIcon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/svelte"; import type { HTMLAttributes } from "svelte/elements"; interface userData { - [fieldName: string]: string; + [fieldName: string]: string; } interface IIdentityCard extends HTMLAttributes { - variant?: "eName" | "ePassport" | "eVault"; - userId?: string; - viewBtn?: () => void; - shareBtn?: () => void; - userData?: userData; - totalStorage?: number; - usedStorage?: number; + variant?: "eName" | "ePassport" | "eVault"; + userId?: string; + viewBtn?: () => void; + shareBtn?: () => void; + userData?: userData; + totalStorage?: number; + usedStorage?: number; } const { - variant = "eName", - userId, - viewBtn, - shareBtn, - userData, - totalStorage = 0, - usedStorage = 0, - ...restProps + variant = "eName", + userId, + viewBtn, + shareBtn, + userData, + totalStorage = 0, + usedStorage = 0, + ...restProps }: IIdentityCard = $props(); const state = $state({ - progressWidth: "0%", + progressWidth: "0%", }); $effect(() => { - state.progressWidth = - usedStorage > 0 ? `${(usedStorage / totalStorage) * 100}%` : "0%"; + state.progressWidth = + usedStorage > 0 ? `${(usedStorage / totalStorage) * 100}%` : "0%"; }); const baseClasses = `relative ${variant === "eName" ? "bg-black-900" : variant === "ePassport" ? "bg-primary" : "bg-gray"} rounded-3xl w-full min-h-[150px] text-white overflow-hidden`; diff --git a/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.stories.ts b/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.stories.ts index b5f5edb7..46363113 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.stories.ts +++ b/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.stories.ts @@ -3,22 +3,22 @@ import type { ComponentProps } from "svelte"; import SettingsNavigationBtn from "./SettingsNavigationBtn.svelte"; export default { - title: "Fragments/SettingsNavigationBtn", - component: SettingsNavigationBtn, - tags: ["autodocs"], - render: (args: { - Component: SettingsNavigationBtn; - props: ComponentProps; - }) => ({ - Component: SettingsNavigationBtn, - props: args, - }), + title: "Fragments/SettingsNavigationBtn", + component: SettingsNavigationBtn, + tags: ["autodocs"], + render: (args: { + Component: SettingsNavigationBtn; + props: ComponentProps; + }) => ({ + Component: SettingsNavigationBtn, + props: args, + }), }; export const Primary = { - args: { - icon: LanguageSquareIcon, - label: "Language", - href: "#", - }, + args: { + icon: LanguageSquareIcon, + label: "Language", + href: "#", + }, }; diff --git a/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.svelte b/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.svelte index 0eb4f6d8..b691c240 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.svelte +++ b/infrastructure/eid-wallet/src/lib/fragments/SettingsNavigationBtn/SettingsNavigationBtn.svelte @@ -6,9 +6,9 @@ import { HugeiconsIcon, type IconSvgElement } from "@hugeicons/svelte"; import type { HTMLAttributes } from "svelte/elements"; interface ISettingsNavigationBtn extends HTMLAttributes { - icon: IconSvgElement; - label: string; - href: string; + icon: IconSvgElement; + label: string; + href: string; } const { icon, label, href, ...restProps }: ISettingsNavigationBtn = $props(); diff --git a/infrastructure/eid-wallet/src/lib/global/controllers/index.ts b/infrastructure/eid-wallet/src/lib/global/controllers/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/eid-wallet/src/lib/global/controllers/security.ts b/infrastructure/eid-wallet/src/lib/global/controllers/security.ts new file mode 100644 index 00000000..7c669954 --- /dev/null +++ b/infrastructure/eid-wallet/src/lib/global/controllers/security.ts @@ -0,0 +1,219 @@ +import { invoke } from "@tauri-apps/api/core"; +import { type Status, checkStatus } from "@tauri-apps/plugin-biometric"; +import type { Store } from "@tauri-apps/plugin-store"; +/** + * @author SoSweetHam + * @description A security controller that can enable/disable biometric authentication for the app and provide for basic pin based application authentication schemes. + * + * Uses the following namespaces in the store: + * - `pin` - The pin hash + * - `biometrics` - The biometric authentication status + * + * @memberof GlobalState + * You should not use this class directly, it is intended for use through the GlobalState. + * + * @constructor Meant to be used as a singleton through the GlobalState, should already be handled. + * @param store - The store to use for storing the pin hash and biometric authentication status + * @example + * ```ts + * import { GlobalState } from "./state"; + * const globalState = await GlobalState.create(); + * globalState.securityController.updatePin("1234", "1234"); + * console.log(globalState.securityController.pinHash); + * ``` + */ +export class SecurityController { + #store: Store; + constructor(store: Store) { + this.#store = store; + } + + /** + * @author SoSweetHam + * @description Store hash of app pin lock by providing the pin in 4 digit plain text + * @memberof SecurityController + * @param pin - The pin in plain text + * @returns void + * @throws Error if the pin is not valid + * @throws Error if the pin cannot be hashed + * @throws Error if the pin cannot be stored + */ + async #setPin(pin: string) { + const regex = /^\d{4}$/; + if (!regex.test(pin)) { + throw new Error("Invalid pin"); + } + const hash = await invoke("hash", { pin }); + if (!hash) { + throw new Error("Pin not set"); + } + await this.#store.set("pin", hash); + } + + /** + * @author SoSweetHam + * @returns The pin hash if set, else undefined + * @throws Error if the pin is not set + * @description Get the pin hash for the app if set + */ + async #getPin() { + return this.#store.get("pin").then((pin) => { + if (!pin) { + return undefined; + } + return pin; + }); + } + + /** + * @author SoSweetHam + * @description Clear the pin for the app - For debug use only, ideally. + * @memberof SecurityController + * @returns void + */ + async clearPin() { + await this.#store.delete("pin"); + } + + /** + * @author SoSweetHam + * @description Verify the pin for the app + * @memberof SecurityController + * @param pin The pin in plain text. + * @returns True if the pin is correct else False. + * @throws Error if the pin is not set. + */ + async verifyPin(pin: string) { + const hash = await this.#getPin(); + if (!hash) { + throw new Error("Pin not set"); + } + const isValid = await invoke("verify", { pin, hash }); + return isValid; + } + + /** + * @author SoSweetHam + * @memberof SecurityController + * @description Set/Update the pin for the app + * @param newPin The new pin in plain text + * @param confirmPin Copy of new pin (ideally both user provided directly) + * @param oldPin Required if the pin on the app is already set + * @returns void + * @throws Error if the pin is not valid + * @throws Error if the pin cannot be hashed + * @throws Error if the pin cannot be stored + * @throws Error if the old pin is not valid + * @throws Error if the new pin and confirm pin do not match + * @throws Error if the old pin is not provided + * @example + * ```ts + * const globalState = await GlobalState.create(); + * globalState.securityController.updatePin("1234", "1234"); + * console.log(globalState.securityController.pinHash); + * ``` + */ + async updatePin(newPin: string, confirmPin: string, oldPin?: string) { + const hash = await this.#getPin(); + if (!hash) { + if (newPin !== confirmPin) { + throw new Error("Pins are not the same!"); + } + return await this.#setPin(newPin); + } + if (oldPin) { + const isValid = await invoke("verify", { + pin: oldPin, + hash, + }); + if (!isValid) { + throw new Error("Invalid pin"); + } + await this.#setPin(newPin); + return; + } + throw new Error("Old pin not provided!"); + } + + /** + * @author SoSweetHam + * @memberof SecurityController + * @description Get the pin hash for the app if set + * @returns A promise for the pin hash + * @example + * ```ts + * const globalState = await GlobalState.create(); + * const pinHash = await globalState.pinHash; + * console.log(pinHash); + * ``` + */ + get pinHash() { + return this.#getPin(); + } + + /** + * @author SoSweetHam + * @memberof SecurityController + * @description Set the biometric authentication for the app + * @param value - Enable/Disable biometric authentication + * @returns void + * @throws Error if the biometric is not supported and trying to enable it + * @example + * ```ts + * const globalState = await GlobalState.create(); + * globalState.enableBiometric = true; + * ``` + */ + async #setBiometric(value: boolean | Promise) { + const status: Status = await checkStatus(); + if (status.isAvailable) { + await this.#store.set("biometrics", await value); + } else { + await this.#store.set("biometrics", false); + if (await value) { + throw new Error("Biometric not supported"); + } + } + } + + /** + * @author SoSweetHam + * @memberof SecurityController + * @description Set the biometric authentication status for the app, if the biometric is not supported, it will be set to false + * @param value - Enable/Disable biometric authentication + * @returns void + * @throws Error if the biometric is not supported and trying to enable it + * @example + * ```ts + * const globalState = await GlobalState.create(); + * globalState.biometricSupport = true; + * ``` + */ + set biometricSupport(value: boolean | Promise) { + this.#setBiometric(value).catch((error) => { + console.error("Failed to set biometric support:", error); + // Consider how to handle errors in a setter - possibly notify via an event + }); + } + + /** + * @author SoSweetHam + * @memberof SecurityController + * @description Get the biometric authentication status for the app + * @returns A promise for the biometric authentication status + * @example + * ```ts + * const globalState = await GlobalState.create(); + * const biometricSupport = await globalState.biometricSupport; + * console.log(biometricSupport); + * ``` + */ + get biometricSupport() { + return this.#store.get("biometrics").then((biometric) => { + if (biometric === undefined) { + return false; + } + return biometric; + }); + } +} diff --git a/infrastructure/eid-wallet/src/lib/global/controllers/user.ts b/infrastructure/eid-wallet/src/lib/global/controllers/user.ts new file mode 100644 index 00000000..bf37ff5a --- /dev/null +++ b/infrastructure/eid-wallet/src/lib/global/controllers/user.ts @@ -0,0 +1,86 @@ +import type { Store } from "@tauri-apps/plugin-store"; + +/** + * @author SoSweetHam + * @version 0.0.1-alpha/Stub + * @date 2025-04-16 + * Would evolve with w3id spec and implementation proposals + * + * @description A user controller that can be used to manage the user state of the application. + * + * Uses the following namespaces in the store: + * - `user` - The user state + * + * @memberof GlobalState + * You should not use this class directly, it is intended for use through the GlobalState. + * + * @constructor Meant to be used as a singleton through the GlobalState, should already be handled. + * @param store - The store to use for storing the user state + * @example + * ```ts + * import { GlobalState } from "./state"; + * const globalState = await GlobalState.create(); + * globalState.userController.user = { + * name: "John Doe", + * "Date of Birth": "01/01/2000", + * "ID submitted": "American Passport", + * "Passport Number": "1234567-US" + * }; + * console.log(globalState.userController.user); + * ``` + */ +export class UserController { + #store: Store; + constructor(store: Store) { + this.#store = store; + } + + /** + * @author SoSweetHam + * @description Sets the user state in the store + * + * @returns {void} + * @example + * ```ts + * import { GlobalState } from "./state"; + * const globalState = await GlobalState.create(); + * globalState.userController.user = { + * name: "John Doe", + * "Date of Birth": "01/01/2000", + * "ID submitted": "American Passport", + * "Passport Number": "1234567-US" + * }; + * console.log(globalState.userController.user); + * ``` + * @throws {Error} If the user state cannot be set in the store + */ + set user(user: + | Promise | undefined> + | Record + | undefined) { + if (user instanceof Promise) { + user.then((resolvedUser) => { + this.#store.set("user", resolvedUser); + }).catch((error) => { + console.error("Failed to set user:", error); + }); + } else { + this.#store.set("user", user); + } + } + + get user() { + return this.#store + .get>("user") + .then((user) => { + if (!user) { + return undefined; + } + return user; + }) + .catch((error) => { + console.error("Failed to get user:", error); + return undefined; + }); + } +} diff --git a/infrastructure/eid-wallet/src/lib/global/index.ts b/infrastructure/eid-wallet/src/lib/global/index.ts new file mode 100644 index 00000000..bc0e6b49 --- /dev/null +++ b/infrastructure/eid-wallet/src/lib/global/index.ts @@ -0,0 +1,2 @@ +export { GlobalState } from "./state"; +export { runtime } from "./runtime.svelte"; diff --git a/infrastructure/eid-wallet/src/lib/global/runtime.svelte.ts b/infrastructure/eid-wallet/src/lib/global/runtime.svelte.ts index d3716274..a6a8a414 100644 --- a/infrastructure/eid-wallet/src/lib/global/runtime.svelte.ts +++ b/infrastructure/eid-wallet/src/lib/global/runtime.svelte.ts @@ -1,11 +1,21 @@ +import type { BiometryType } from "@tauri-apps/plugin-biometric"; + export const runtime = $state<{ - header: { - title: string | undefined; - backEnabled: boolean | undefined; - }; + header: { + title: string | undefined; + backEnabled: boolean | undefined; + }; + /** + * None = 0, + * TouchID = 1, + * FaceID = 2, + * Iris = 3 + */ + biometry: BiometryType | undefined; }>({ - header: { - title: undefined, - backEnabled: undefined, - }, + header: { + title: undefined, + backEnabled: undefined, + }, + biometry: undefined, }); diff --git a/infrastructure/eid-wallet/src/lib/global/state.ts b/infrastructure/eid-wallet/src/lib/global/state.ts new file mode 100644 index 00000000..1da2647d --- /dev/null +++ b/infrastructure/eid-wallet/src/lib/global/state.ts @@ -0,0 +1,51 @@ +import { Store } from "@tauri-apps/plugin-store"; +import { SecurityController } from "./controllers/security"; +import { UserController } from "./controllers/user"; +/** + * @author SoSweetHam + * @description A centralized state that can be used to control the global state of the application, meant to be used as a singleton through the main layout component. + * + * @constructor + * You cannot instance this class directly, instead use the `GlobalState.create()` method to get an instance. + * + * @example + * ```ts + * import { onMount, setContext } from "svelte"; + * let globalState: GlobalState | undefined = $state(undefined); + * setContext('globalState', () => globalState); + * onMount(async() => { + * const globalState = await GlobalState.create(); + * console.log(globalState); + * }) + * ``` + */ +export class GlobalState { + #store: Store; + securityController: SecurityController; + userController: UserController; + private constructor(store: Store) { + this.#store = store; + this.securityController = new SecurityController(store); + this.userController = new UserController(store); + } + + /** + * @author SoSweetHam + * @description Creator of the GlobalState singleton + * @returns A promise of a new instance of GlobalState + * @throws Error if the store cannot be loaded + */ + static async create() { + const store = await Store.load("global-state.json", { + autoSave: true, + }); + const alreadyInitialized = await store.get("initialized"); + + const instance = new GlobalState(store); + + if (!alreadyInitialized) { + await instance.#store.set("initialized", true); + } + return instance; + } +} diff --git a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.stories.ts b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.stories.ts index 934b3f97..c9385658 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.stories.ts +++ b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.stories.ts @@ -3,44 +3,44 @@ import { ButtonText } from "./Button.stories.snippet.svelte"; import ButtonAction from "./ButtonAction.svelte"; export default { - title: "UI/ButtonAction", - component: ButtonAction, - tags: ["autodocs"], - render: (args: { - Component: ButtonAction; - props: ComponentProps; - }) => ({ - Component: ButtonAction, - props: args, - }), + title: "UI/ButtonAction", + component: ButtonAction, + tags: ["autodocs"], + render: (args: { + Component: ButtonAction; + props: ComponentProps; + }) => ({ + Component: ButtonAction, + props: args, + }), }; export const Solid = { - args: { variant: "solid", children: ButtonText }, + args: { variant: "solid", children: ButtonText }, }; export const Soft = { - args: { variant: "soft", children: ButtonText }, + args: { variant: "soft", children: ButtonText }, }; export const Danger = { - args: { variant: "danger", children: ButtonText }, + args: { variant: "danger", children: ButtonText }, }; export const DangerSoft = { - args: { variant: "danger-soft", children: ButtonText }, + args: { variant: "danger-soft", children: ButtonText }, }; export const Loading = { - args: { isLoading: true, children: ButtonText }, + args: { isLoading: true, children: ButtonText }, }; export const BlockingClick = { - args: { - blockingClick: true, - children: ButtonText, - callback: async () => { - await new Promise((resolve) => setTimeout(resolve, 2000)); - }, - }, + args: { + blockingClick: true, + children: ButtonText, + callback: async () => { + await new Promise((resolve) => setTimeout(resolve, 2000)); + }, + }, }; diff --git a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte index 74d512ba..404d8b53 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte @@ -3,76 +3,76 @@ import { cn } from "$lib/utils"; import type { HTMLButtonAttributes } from "svelte/elements"; interface IButtonProps extends HTMLButtonAttributes { - variant?: "solid" | "soft" | "danger" | "danger-soft" | "white"; - isLoading?: boolean; - callback?: () => Promise | void; - blockingClick?: boolean; - type?: "button" | "submit" | "reset"; - size?: "sm" | "md"; + variant?: "solid" | "soft" | "danger" | "danger-soft" | "white"; + isLoading?: boolean; + callback?: () => Promise | void; + blockingClick?: boolean; + type?: "button" | "submit" | "reset"; + size?: "sm" | "md"; } const { - variant = "solid", - isLoading, - callback, - onclick, - blockingClick, - type = "button", - size = "md", - children = undefined, - ...restProps + variant = "solid", + isLoading, + callback, + onclick, + blockingClick, + type = "button", + size = "md", + children = undefined, + ...restProps }: IButtonProps = $props(); let isSubmitting = $state(false); const disabled = $derived(restProps.disabled || isLoading || isSubmitting); const handleClick = async () => { - if (typeof callback !== "function") return; + if (typeof callback !== "function") return; - if (blockingClick) isSubmitting = true; - try { - await callback(); - } catch (error) { - console.error("Error in button callback:", error); - } finally { - isSubmitting = false; - } + if (blockingClick) isSubmitting = true; + try { + await callback(); + } catch (error) { + console.error("Error in button callback:", error); + } finally { + isSubmitting = false; + } }; const variantClasses = { - solid: { background: "bg-primary-500", text: "text-white" }, - soft: { background: "bg-primary-100", text: "text-primary-500" }, - danger: { background: "bg-danger-500", text: "text-white" }, - "danger-soft": { background: "bg-danger-100", text: "text-danger-500" }, - white: { background: "bg-white", text: "text-black" }, + solid: { background: "bg-primary-500", text: "text-white" }, + soft: { background: "bg-primary-100", text: "text-primary-500" }, + danger: { background: "bg-danger-500", text: "text-white" }, + "danger-soft": { background: "bg-danger-100", text: "text-danger-500" }, + white: { background: "bg-white", text: "text-black" }, }; const disabledVariantClasses = { - solid: { background: "bg-primary-300", text: "text-white" }, - soft: { background: "bg-primary-100", text: "text-primary-300" }, - danger: { background: "bg-danger-400", text: "text-white" }, - "danger-soft": { background: "bg-danger-100", text: "text-danger-400" }, - white: { background: "bg-black-100", text: "text-black-700" }, + solid: { background: "bg-primary-300", text: "text-white" }, + soft: { background: "bg-primary-100", text: "text-primary-300" }, + danger: { background: "bg-danger-400", text: "text-white" }, + "danger-soft": { background: "bg-danger-100", text: "text-danger-400" }, + white: { background: "bg-black-100", text: "text-black-700" }, }; const sizeVariant = { - sm: "px-4 py-1.5 text-base h-11", - md: "px-8 py-2.5 text-xl h-14", + sm: "px-4 py-1.5 text-base h-11", + md: "px-8 py-2.5 text-xl h-14", }; const classes = $derived({ - common: cn( - "cursor-pointer w-min flex items-center justify-center rounded-full font-semibold duration-100", - sizeVariant[size], - ), - background: disabled - ? disabledVariantClasses[variant].background || - variantClasses[variant].background - : variantClasses[variant].background, - text: disabled - ? disabledVariantClasses[variant].text || variantClasses[variant].text - : variantClasses[variant].text, - disabled: "cursor-not-allowed", + common: cn( + "cursor-pointer w-min flex items-center justify-center rounded-full font-semibold duration-100", + sizeVariant[size], + ), + background: disabled + ? disabledVariantClasses[variant].background || + variantClasses[variant].background + : variantClasses[variant].background, + text: disabled + ? disabledVariantClasses[variant].text || variantClasses[variant].text + : variantClasses[variant].text, + disabled: "cursor-not-allowed", }); diff --git a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.stories.ts b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.stories.ts index 9cdec274..6333a0db 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.stories.ts +++ b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.stories.ts @@ -3,73 +3,73 @@ import type { ComponentProps } from "svelte"; import ButtonIcon from "./ButtonIcon.svelte"; export default { - title: "UI/ButtonIcon", - component: ButtonIcon, - tags: ["autodocs"], - render: (args: ComponentProps) => ({ - Component: ButtonIcon, - props: args, - }), + title: "UI/ButtonIcon", + component: ButtonIcon, + tags: ["autodocs"], + render: (args: ComponentProps) => ({ + Component: ButtonIcon, + props: args, + }), }; export const Default = { - render: () => ({ - Component: ButtonIcon, - props: { - ariaLabel: "Default button", - bgSize: "md", // Predefined size - iconSize: "md", - icon: ViewIcon, - bgColor: "black", - iconColor: "white", - }, - }), + render: () => ({ + Component: ButtonIcon, + props: { + ariaLabel: "Default button", + bgSize: "md", // Predefined size + iconSize: "md", + icon: ViewIcon, + bgColor: "black", + iconColor: "white", + }, + }), }; export const CustomSize = { - render: () => ({ - Component: ButtonIcon, - props: { - ariaLabel: "Custom sized button", - bgSize: "w-[120px] h-[120px]", // Custom Tailwind size - iconSize: 56, // Custom pixel size - icon: FlashlightIcon, - bgColor: "bg-danger", - iconColor: "white", - }, - }), + render: () => ({ + Component: ButtonIcon, + props: { + ariaLabel: "Custom sized button", + bgSize: "w-[120px] h-[120px]", // Custom Tailwind size + iconSize: 56, // Custom pixel size + icon: FlashlightIcon, + bgColor: "bg-danger", + iconColor: "white", + }, + }), }; export const Loading = { - render: () => ({ - Component: ButtonIcon, - props: { - ariaLabel: "Loading button", - bgSize: "md", - iconSize: "md", - icon: FlashlightIcon, - isLoading: true, - bgColor: "black", - iconColor: "white", - }, - }), + render: () => ({ + Component: ButtonIcon, + props: { + ariaLabel: "Loading button", + bgSize: "md", + iconSize: "md", + icon: FlashlightIcon, + isLoading: true, + bgColor: "black", + iconColor: "white", + }, + }), }; export const WithCallback = { - render: () => ({ - Component: ButtonIcon, - props: { - ariaLabel: "Button with async callback", - bgSize: "md", - iconSize: "md", - icon: FlashlightIcon, - callback: async () => { - await new Promise((resolve) => setTimeout(resolve, 2000)); - console.log("Action completed!"); - }, - blockingClick: true, - bgColor: "primary", - iconColor: "white", - }, - }), + render: () => ({ + Component: ButtonIcon, + props: { + ariaLabel: "Button with async callback", + bgSize: "md", + iconSize: "md", + icon: FlashlightIcon, + callback: async () => { + await new Promise((resolve) => setTimeout(resolve, 2000)); + console.log("Action completed!"); + }, + blockingClick: true, + bgColor: "primary", + iconColor: "white", + }, + }), }; diff --git a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.svelte b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.svelte index f998d065..45b569bf 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.svelte @@ -4,122 +4,122 @@ import { HugeiconsIcon, type IconSvgElement } from "@hugeicons/svelte"; import type { HTMLButtonAttributes } from "svelte/elements"; interface IButtonProps extends HTMLButtonAttributes { - icon: IconSvgElement; - isLoading?: boolean; - callback?: () => Promise | void; - onclick?: () => void; - blockingClick?: boolean; - type?: "button" | "submit" | "reset"; - bgSize?: "sm" | "md" | "lg" | number | string; - bgColor?: - | "black" - | "white" - | "gray" - | "primary" - | "secondary" - | "danger" - | string; - iconSize?: "sm" | "md" | "lg" | number | string; - iconColor?: - | "black" - | "white" - | "gray" - | "primary" - | "secondary" - | "danger" - | string; - strokeWidth?: number; + icon: IconSvgElement; + isLoading?: boolean; + callback?: () => Promise | void; + onclick?: () => void; + blockingClick?: boolean; + type?: "button" | "submit" | "reset"; + bgSize?: "sm" | "md" | "lg" | number | string; + bgColor?: + | "black" + | "white" + | "gray" + | "primary" + | "secondary" + | "danger" + | string; + iconSize?: "sm" | "md" | "lg" | number | string; + iconColor?: + | "black" + | "white" + | "gray" + | "primary" + | "secondary" + | "danger" + | string; + strokeWidth?: number; } const { - icon, - isLoading, - callback, - onclick, - blockingClick, - type = "button", - bgSize, - bgColor = "transparent", - iconSize = "md", - iconColor = "black", - strokeWidth = 1.5, - children = undefined, - ...restProps + icon, + isLoading, + callback, + onclick, + blockingClick, + type = "button", + bgSize, + bgColor = "transparent", + iconSize = "md", + iconColor = "black", + strokeWidth = 1.5, + children = undefined, + ...restProps }: IButtonProps = $props(); let isSubmitting = $state(false); const disabled = $derived(restProps.disabled || isLoading || isSubmitting); const handleClick = async () => { - if (typeof callback !== "function") return; - - if (blockingClick) isSubmitting = true; - try { - await callback(); - } catch (error) { - console.error("Error in button callback:", error); - } finally { - isSubmitting = false; - } + if (typeof callback !== "function") return; + + if (blockingClick) isSubmitting = true; + try { + await callback(); + } catch (error) { + console.error("Error in button callback:", error); + } finally { + isSubmitting = false; + } }; const sizeVariant = { - sm: "h-8 w-8", - md: "h-[54px] w-[54px]", - lg: "h-[108px] w-[108px]", + sm: "h-8 w-8", + md: "h-[54px] w-[54px]", + lg: "h-[108px] w-[108px]", } as const; const iconSizeVariant = { - sm: 24, - md: 30, - lg: 36, + sm: 24, + md: 30, + lg: 36, } as const; const backgroundColor: Record = { - black: "bg-black", - white: "bg-white", - gray: "bg-gray", - primary: "bg-primary", - secondary: "bg-secondary", - danger: "bg-danger", + black: "bg-black", + white: "bg-white", + gray: "bg-gray", + primary: "bg-primary", + secondary: "bg-secondary", + danger: "bg-danger", } as const; const textColor: Record = { - black: "text-black", - white: "text-white", - gray: "text-gray", - primary: "text-primary", - secondary: "text-secondary", - danger: "text-danger", + black: "text-black", + white: "text-white", + gray: "text-gray", + primary: "text-primary", + secondary: "text-secondary", + danger: "text-danger", } as const; const resolvedIconSize = - iconSize === undefined - ? iconSizeVariant.md - : typeof iconSize === "number" - ? iconSize - : iconSize in iconSizeVariant - ? iconSizeVariant[iconSize as keyof typeof iconSizeVariant] - : iconSize; + iconSize === undefined + ? iconSizeVariant.md + : typeof iconSize === "number" + ? iconSize + : iconSize in iconSizeVariant + ? iconSizeVariant[iconSize as keyof typeof iconSizeVariant] + : iconSize; const resolvedBgSize = - bgSize === undefined - ? "" // if bgSize is empty, there is no background - : typeof bgSize === "number" - ? `h-${bgSize} w-${bgSize}` - : bgSize in sizeVariant - ? sizeVariant[bgSize as keyof typeof sizeVariant] - : bgSize; + bgSize === undefined + ? "" // if bgSize is empty, there is no background + : typeof bgSize === "number" + ? `h-${bgSize} w-${bgSize}` + : bgSize in sizeVariant + ? sizeVariant[bgSize as keyof typeof sizeVariant] + : bgSize; const classes = $derived({ - common: cn( - "cursor-pointer flex items-center justify-center rounded-full font-semibold duration-100", - ), - bgSize: resolvedBgSize, - background: bgColor in backgroundColor ? backgroundColor[bgColor] : bgColor, - iconColor: iconColor in textColor ? textColor[iconColor] : iconColor, - disabled: "cursor-not-allowed opacity-50", - iconSize: resolvedIconSize, + common: cn( + "cursor-pointer flex items-center justify-center rounded-full font-semibold duration-100", + ), + bgSize: resolvedBgSize, + background: bgColor in backgroundColor ? backgroundColor[bgColor] : bgColor, + iconColor: iconColor in textColor ? textColor[iconColor] : iconColor, + disabled: "cursor-not-allowed opacity-50", + iconSize: resolvedIconSize, }); diff --git a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.stories.ts b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.stories.ts index 7c844501..90d9738a 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.stories.ts +++ b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.stories.ts @@ -1,31 +1,31 @@ import type { ComponentProps } from "svelte"; import { - ButtonNavSettings, - ButtonNavText, + ButtonNavSettings, + ButtonNavText, } from "./Button.stories.snippet.svelte"; import ButtonNav from "./ButtonNav.svelte"; export default { - title: "UI/ButtonNav", - component: ButtonNav, - tags: ["autodocs"], - render: (args: { - Component: ButtonNav; - props: ComponentProps; - }) => ({ - Component: ButtonNav, - props: args, - }), + title: "UI/ButtonNav", + component: ButtonNav, + tags: ["autodocs"], + render: (args: { + Component: ButtonNav; + props: ComponentProps; + }) => ({ + Component: ButtonNav, + props: args, + }), }; export const Default = { - args: { href: "#", children: ButtonNavText }, + args: { href: "#", children: ButtonNavText }, }; export const ForSettings = { - args: { - href: "#", - children: ButtonNavSettings, - class: "flex items-center justify-between px-3 py-2", - }, + args: { + href: "#", + children: ButtonNavSettings, + class: "flex items-center justify-between px-3 py-2", + }, }; diff --git a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.svelte b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.svelte index 6d09ae92..8e3ea015 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonNav.svelte @@ -4,8 +4,8 @@ import type { Snippet } from "svelte"; import type { HTMLAnchorAttributes } from "svelte/elements"; interface IButtonNav extends HTMLAnchorAttributes { - href: string; - children: Snippet; + href: string; + children: Snippet; } const { href, children, ...restProps }: IButtonNav = $props(); diff --git a/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.stories.ts b/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.stories.ts index a50698cd..db6e6d24 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.stories.ts +++ b/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.stories.ts @@ -1,26 +1,25 @@ import Connection from "./Connection.svelte"; export default { - title: "UI/Connection", - component: Connection, - tags: ["autodocs"], - render: (args: { - imgSrc: string; - connectionName: string; - lastConnected: string; - onClick: () => void; - }) => ({ - Component: Connection, - props: args, - }), + title: "UI/Connection", + component: Connection, + tags: ["autodocs"], + render: (args: { + imgSrc: string; + connectionName: string; + lastConnected: string; + onClick: () => void; + }) => ({ + Component: Connection, + props: args, + }), }; export const Primary = { - args: { - imgSrc: - "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRGPvo_J4nWlDM0kxFW0rsfR5UeOOC6uMvpfQ&s", - connectionName: "Facebook.com", - lastConnected: `${new Date().toDateString()}, ${new Date().toLocaleTimeString()}`, - onClick: () => alert("Disconnected!"), - }, + args: { + imgSrc: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRGPvo_J4nWlDM0kxFW0rsfR5UeOOC6uMvpfQ&s", + connectionName: "Facebook.com", + lastConnected: `${new Date().toDateString()}, ${new Date().toLocaleTimeString()}`, + onClick: () => alert("Disconnected!"), + }, }; diff --git a/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.svelte b/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.svelte index 5844470e..8bc7ab59 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/Connection/Connection.svelte @@ -3,18 +3,18 @@ import { cn } from "$lib/utils"; import type { HTMLAttributes } from "svelte/elements"; interface IConnection extends HTMLAttributes { - imgSrc: string; - connectionName: string; - lastConnected: Date; - onClick?: () => void; + imgSrc: string; + connectionName: string; + lastConnected: Date; + onClick?: () => void; } let { - imgSrc, - connectionName, - lastConnected, - onClick, - ...restProps + imgSrc, + connectionName, + lastConnected, + onClick, + ...restProps }: IConnection = $props(); diff --git a/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.stories.ts b/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.stories.ts index 6c70b401..f5633513 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.stories.ts +++ b/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.stories.ts @@ -3,21 +3,21 @@ import { InnerContent } from "./Drawer.stories.snippet.svelte"; import Drawer from "./Drawer.svelte"; export default { - title: "UI/Drawer", - component: Drawer, - tags: ["autodocs"], - render: (args: { - Component: Drawer; - props: ComponentProps; - }) => ({ - Component: Drawer, - props: args, - }), + title: "UI/Drawer", + component: Drawer, + tags: ["autodocs"], + render: (args: { + Component: Drawer; + props: ComponentProps; + }) => ({ + Component: Drawer, + props: args, + }), }; export const Default = { - args: { - isPaneOpen: true, - children: InnerContent, - }, + args: { + isPaneOpen: true, + children: InnerContent, + }, }; diff --git a/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte b/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte index 3ed84005..da53651a 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte @@ -6,50 +6,50 @@ import { swipe } from "svelte-gestures"; import type { HTMLAttributes } from "svelte/elements"; interface IDrawerProps extends HTMLAttributes { - isPaneOpen?: boolean; - children?: Snippet; - handleSwipe?: (isOpen: boolean | undefined) => void; + isPaneOpen?: boolean; + children?: Snippet; + handleSwipe?: (isOpen: boolean | undefined) => void; } let drawerElem: HTMLDivElement; let pane: CupertinoPane; let { - isPaneOpen = $bindable(), - children = undefined, - handleSwipe, - ...restProps + isPaneOpen = $bindable(), + children = undefined, + handleSwipe, + ...restProps }: IDrawerProps = $props(); const handleClickOutside = () => { - pane?.destroy({ animate: true }); - isPaneOpen = false; + pane?.destroy({ animate: true }); + isPaneOpen = false; }; $effect(() => { - if (!drawerElem) return; - pane = new CupertinoPane(drawerElem, { - fitHeight: true, - backdrop: true, - backdropOpacity: 0.5, - backdropBlur: true, - bottomClose: true, - buttonDestroy: false, - showDraggable: true, - upperThanTop: true, - breaks: { - bottom: { enabled: true, height: 250 }, - }, - initialBreak: "bottom", - }); + if (!drawerElem) return; + pane = new CupertinoPane(drawerElem, { + fitHeight: true, + backdrop: true, + backdropOpacity: 0.5, + backdropBlur: true, + bottomClose: true, + buttonDestroy: false, + showDraggable: true, + upperThanTop: true, + breaks: { + bottom: { enabled: true, height: 250 }, + }, + initialBreak: "bottom", + }); - if (isPaneOpen) { - pane.present({ animate: true }); - } else { - pane.destroy({ animate: true }); - } + if (isPaneOpen) { + pane.present({ animate: true }); + } else { + pane.destroy({ animate: true }); + } - return () => pane.destroy(); + return () => pane.destroy(); }); diff --git a/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.stories.ts b/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.stories.ts index e382104b..1a2e9dce 100644 --- a/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.stories.ts +++ b/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.stories.ts @@ -2,34 +2,34 @@ import type { ComponentProps } from "svelte"; import InputPin from "./InputPin.svelte"; export default { - title: "UI/InputPin", - component: InputPin, - tags: ["autodocs"], - render: (args: { - Component: InputPin; - props: ComponentProps; - }) => ({ - Component: InputPin, - props: args, - }), + title: "UI/InputPin", + component: InputPin, + tags: ["autodocs"], + render: (args: { + Component: InputPin; + props: ComponentProps; + }) => ({ + Component: InputPin, + props: args, + }), }; export const Default = { - args: { - size: 4, - }, + args: { + size: 4, + }, }; export const Small = { - args: { - size: 4, - variant: "sm", - }, + args: { + size: 4, + variant: "sm", + }, }; export const isError = { - args: { - size: 4, - isError: true, - }, + args: { + size: 4, + isError: true, + }, }; diff --git a/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.svelte b/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.svelte index f75e7fac..e2a6adf0 100644 --- a/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/InputPin/InputPin.svelte @@ -4,107 +4,119 @@ import { onMount } from "svelte"; import type { HTMLAttributes } from "svelte/elements"; const KEYBOARD = { - BACKSPACE: "Backspace", - DELETE: "Delete", - ANDROID_BACKSPACE: "Backspace", + BACKSPACE: "Backspace", + DELETE: "Delete", + ANDROID_BACKSPACE: "Backspace", }; let inputs = $state([0]); let pins: { [key: number]: string } = $state({}); -interface IInputPinProps extends HTMLAttributes { - pin: string; - variant?: "lg" | "sm"; - size?: number; - focusOnMount?: boolean | undefined; - inFocus?: boolean | undefined; - isError?: boolean; +interface IInputPinProps extends HTMLAttributes { + pin: string; + variant?: "lg" | "sm"; + size?: number; + focusOnMount?: boolean | undefined; + inFocus?: boolean | undefined; + isError?: boolean; } let { - pin = $bindable(""), - variant = "lg", - size = 4, - focusOnMount = true, - inFocus = false, - isError = $bindable(false), - ...restProps + pin = $bindable(""), + variant = "lg", + size = 4, + focusOnMount = true, + inFocus = false, + isError = $bindable(false), + ...restProps }: IInputPinProps = $props(); onMount(async () => { - inputs = createArray(size); - pins = await createValueSlot(inputs); - pin = calcPin(pins); - if (!focusOnMount) return; - document.getElementById("pin0")?.focus(); + inputs = createArray(size); + pins = await createValueSlot(inputs); + pin = calcPin(pins); + if (!focusOnMount) return; + document.getElementById("pin0")?.focus(); }); $effect(() => { - pin = calcPin(pins); + pin = calcPin(pins); }); const calcPin = (pins: { [key: number]: string }) => { - return Object.values(pins).join("") || ""; + return Object.values(pins).join("") || ""; }; const isKeyDelete = (key: string) => { - return ( - key === KEYBOARD.BACKSPACE || - key === KEYBOARD.DELETE || - key === KEYBOARD.ANDROID_BACKSPACE - ); + return ( + key === KEYBOARD.BACKSPACE || + key === KEYBOARD.DELETE || + key === KEYBOARD.ANDROID_BACKSPACE + ); }; const changeHandler = (e: KeyboardEvent, i: number) => { - const current = document.activeElement ?? document.getElementById("pin0"); - const items = Array.from(document.getElementsByClassName("pin-item")); - const currentIndex = items.indexOf(current as HTMLElement); - let newIndex: number; - - const regx = /^\d+$/; - - if (isKeyDelete(e.key)) { - if (pins[i] !== "") { - pins[i] = ""; - return; - } - if (currentIndex > 0) { - newIndex = currentIndex - 1; - (items[newIndex] as HTMLInputElement)?.focus(); - } - } - - if (regx.test(e.key)) { - pins[i] = e.key; - if (currentIndex < items.length - 1) { - newIndex = currentIndex + 1; - (items[newIndex] as HTMLInputElement)?.focus(); - } - } + const current = document.activeElement ?? document.getElementById("pin0"); + const items = Array.from(document.getElementsByClassName("pin-item")); + const currentIndex = items.indexOf(current as HTMLElement); + let newIndex: number; + + const regx = /^\d+$/; + + if (isKeyDelete(e.key)) { + e.preventDefault(); + if (pins[i] !== "") { + pins[i] = ""; + return; + } + if (currentIndex > 0) { + newIndex = currentIndex - 1; + (items[newIndex] as HTMLInputElement)?.focus(); + } + } + + if (regx.test(e.key)) { + e.preventDefault(); + pins[i] = e.key; + if (currentIndex < items.length - 1) { + newIndex = currentIndex + 1; + (items[newIndex] as HTMLInputElement)?.focus(); + } + } + + // Allow arrow keys for navigation + if (e.key === "ArrowLeft" && currentIndex > 0) { + newIndex = currentIndex - 1; + (items[newIndex] as HTMLInputElement)?.focus(); + } + + if (e.key === "ArrowRight" && currentIndex < items.length - 1) { + newIndex = currentIndex + 1; + (items[newIndex] as HTMLInputElement)?.focus(); + } }; const createArray = (size: number) => { - return new Array(size); + return new Array(size); }; const createValueSlot = (arr: number[]) => { - return arr.reduce( - (obj, item) => { - obj[item] = ""; - return obj; - }, - {} as Record, - ); + return arr.reduce( + (obj, item) => { + obj[item] = ""; + return obj; + }, + {} as Record, + ); }; const uniqueId = `input${Math.random().toString().split(".")[1]}`; const cBase = - "relative w-full margin-x-[auto] flex justify-between items-center gap-[10px] flex-row flex-nowrap select-none"; + "relative w-full margin-x-[auto] flex justify-between items-center gap-[10px] flex-row flex-nowrap select-none"; - +
{#if inputs.length} @@ -126,15 +138,15 @@ const cBase = }} maxlength="1" onkeydown={(event) => { - event.preventDefault() changeHandler(event, i) }} placeholder="" + {...restProps} /> {#if pins[i] !== ''} -
{/if}
diff --git a/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.stories.ts b/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.stories.ts index 693dc87b..27cd56d9 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.stories.ts +++ b/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.stories.ts @@ -1,46 +1,46 @@ import type { ComponentProps } from "svelte"; import { - BasicContent, - WithIconContent, + BasicContent, + WithIconContent, } from "./Selector.stories.snippet.svelte"; import Selector from "./Selector.svelte"; export default { - title: "UI/Selector", - component: Selector, - tags: ["autodocs"], - render: (args: { - Component: Selector; - props: ComponentProps; - }) => ({ - Component: Selector, - props: args, - }), + title: "UI/Selector", + component: Selector, + tags: ["autodocs"], + render: (args: { + Component: Selector; + props: ComponentProps; + }) => ({ + Component: Selector, + props: args, + }), }; export const WithIcon = { - render: () => ({ - Component: Selector, - props: { - id: "option-1", - name: "lang", - value: "option-1", - selected: "option-1", - icon: WithIconContent, - children: BasicContent, - }, - }), + render: () => ({ + Component: Selector, + props: { + id: "option-1", + name: "lang", + value: "option-1", + selected: "option-1", + icon: WithIconContent, + children: BasicContent, + }, + }), }; export const WithoutIcon = { - render: () => ({ - Component: Selector, - props: { - id: "option-1", - name: "lang", - value: "option-1", - selected: "option-1", - children: BasicContent, - }, - }), + render: () => ({ + Component: Selector, + props: { + id: "option-1", + name: "lang", + value: "option-1", + selected: "option-1", + children: BasicContent, + }, + }), }; diff --git a/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.svelte b/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.svelte index 02a98bff..026cabdd 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/Selector/Selector.svelte @@ -7,22 +7,22 @@ import type { HTMLLabelAttributes } from "svelte/elements"; import { fade } from "svelte/transition"; interface ISelectorProps extends HTMLLabelAttributes { - id: string; - name: string; - value: string; - icon?: Snippet<[string]>; - selected?: string; - children?: Snippet; + id: string; + name: string; + value: string; + icon?: Snippet<[string]>; + selected?: string; + children?: Snippet; } let { - id, - name, - value, - icon = undefined, - selected = $bindable(), - children = undefined, - ...restProps + id, + name, + value, + icon = undefined, + selected = $bindable(), + children = undefined, + ...restProps }: ISelectorProps = $props(); diff --git a/infrastructure/eid-wallet/src/lib/utils/clickOutside.ts b/infrastructure/eid-wallet/src/lib/utils/clickOutside.ts index f62a3b43..f2058540 100644 --- a/infrastructure/eid-wallet/src/lib/utils/clickOutside.ts +++ b/infrastructure/eid-wallet/src/lib/utils/clickOutside.ts @@ -1,21 +1,21 @@ /** Dispatch event on click outside of node */ export const clickOutside = (node: HTMLElement, callback: () => void) => { - const handleClick = (event: Event) => { - if ( - node && - !node.contains(event.target as Node) && - !event.defaultPrevented - ) { - callback(); // Call the provided callback - node.dispatchEvent(new CustomEvent("click_outside")); - } - }; + const handleClick = (event: Event) => { + if ( + node && + !node.contains(event.target as Node) && + !event.defaultPrevented + ) { + callback(); // Call the provided callback + node.dispatchEvent(new CustomEvent("click_outside")); + } + }; - document.addEventListener("click", handleClick, true); + document.addEventListener("click", handleClick, true); - return { - destroy() { - document.removeEventListener("click", handleClick, true); - }, - }; + return { + destroy() { + document.removeEventListener("click", handleClick, true); + }, + }; }; diff --git a/infrastructure/eid-wallet/src/lib/utils/getLanguage.ts b/infrastructure/eid-wallet/src/lib/utils/getLanguage.ts index 48ce5281..4ebaf2c6 100644 --- a/infrastructure/eid-wallet/src/lib/utils/getLanguage.ts +++ b/infrastructure/eid-wallet/src/lib/utils/getLanguage.ts @@ -8,16 +8,16 @@ * getLanguageWithCountry('es') // { code: 'es', name: 'Spanish' } */ export const getLanguageWithCountry = (locale: string) => { - const parts = locale.split("-"); - const langCode = parts[0]; - const countryCode = parts[1]?.toLowerCase() || ""; + const parts = locale.split("-"); + const langCode = parts[0]; + const countryCode = parts[1]?.toLowerCase() || ""; - return { - code: countryCode || langCode, - name: - new Intl.DisplayNames(["en"], { type: "language" }).of(langCode) + - (countryCode - ? ` (${new Intl.DisplayNames(["en"], { type: "region" }).of(countryCode.toUpperCase())})` - : ""), - }; + return { + code: countryCode || langCode, + name: + new Intl.DisplayNames(["en"], { type: "language" }).of(langCode) + + (countryCode + ? ` (${new Intl.DisplayNames(["en"], { type: "region" }).of(countryCode.toUpperCase())})` + : ""), + }; }; diff --git a/infrastructure/eid-wallet/src/lib/utils/mergeClasses.ts b/infrastructure/eid-wallet/src/lib/utils/mergeClasses.ts index ac680b30..e6447944 100644 --- a/infrastructure/eid-wallet/src/lib/utils/mergeClasses.ts +++ b/infrastructure/eid-wallet/src/lib/utils/mergeClasses.ts @@ -2,5 +2,5 @@ import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)); } diff --git a/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte b/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte index e7af1510..f6e1e531 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte @@ -9,12 +9,12 @@ let currentRoute = $derived(page.url.pathname.split("/").pop() || "home"); -{#if currentRoute === "scan-qr"} +
{@render children()} -
\ No newline at end of file + diff --git a/infrastructure/eid-wallet/src/routes/(app)/ePassport/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/ePassport/+page.svelte index 4ada3866..837109c1 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/ePassport/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/ePassport/+page.svelte @@ -5,22 +5,22 @@ import { Share05Icon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/svelte"; const dummyData = { - Name: "John Doe", - "Date of birth": "01-01-1990", - "ID submitted": "American Passport", - "Passport number": "1234567-US", + Name: "John Doe", + "Date of birth": "01-01-1990", + "ID submitted": "American Passport", + "Passport number": "1234567-US", }; const secondDummyData = { - "Birth Name": "John Doe", - "Place of birth": "New York, USA", - Nationality: "American", - "ID submitted": "American Passport", - "Expiry date": "19-12-2027", + "Birth Name": "John Doe", + "Place of birth": "New York, USA", + Nationality: "American", + "ID submitted": "American Passport", + "Expiry date": "19-12-2027", }; function shareEPassport() { - alert("EPassport Code shared!"); + alert("EPassport Code shared!"); } diff --git a/infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte index 2924f3a3..ad9718e2 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte @@ -8,17 +8,17 @@ import { HugeiconsIcon } from "@hugeicons/svelte"; import type { Snippet } from "svelte"; const dummyData = { - Name: "John Doe", - "Date of birth": "01 - 01 - 1990", - "ID submitted": "American Passport", - "Passport number": "1234567-US", + Name: "John Doe", + "Date of birth": "01 - 01 - 1990", + "ID submitted": "American Passport", + "Passport number": "1234567-US", }; let shareQRdrawerOpen = $state(false); function shareQR() { - alert("QR Code shared!"); - shareQRdrawerOpen = false; + alert("QR Code shared!"); + shareQRdrawerOpen = false; } diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte index 3f61726e..1b317ea5 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte @@ -3,23 +3,95 @@ import AppNav from "$lib/fragments/AppNav/AppNav.svelte"; import { Drawer } from "$lib/ui"; import * as Button from "$lib/ui/Button"; import { - FlashlightIcon, - Image02Icon, - QrCodeIcon, + FlashlightIcon, + Image02Icon, + QrCodeIcon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/svelte"; +import { + Format, + type PermissionState, + type Scanned, + cancel, + checkPermissions, + requestPermissions, + scan, +} from "@tauri-apps/plugin-barcode-scanner"; +import { onDestroy, onMount } from "svelte"; import type { SVGAttributes } from "svelte/elements"; const pathProps: SVGAttributes = { - stroke: "white", - "stroke-width": 7, - "stroke-linecap": "round", - "stroke-linejoin": "round", + stroke: "white", + "stroke-width": 7, + "stroke-linecap": "round", + "stroke-linejoin": "round", }; let codeScannedDrawerOpen = $state(false); let loggedInDrawerOpen = $state(false); let flashlightOn = $state(false); + +let scannedData: Scanned | undefined = $state(undefined); + +let scanning = false; +let loading = false; + +let permissions_nullable: PermissionState | null; + +async function startScan() { + let permissions = await checkPermissions() + .then((permissions) => { + return permissions; + }) + .catch(() => { + return null; // possibly return "denied"? or does that imply that the check has been successful, but was actively denied? + }); + + // TODO: handle receiving "prompt-with-rationale" (issue: https://github.com/tauri-apps/plugins-workspace/issues/979) + if (permissions === "prompt") { + permissions = await requestPermissions(); // handle in more detail? + } + + permissions_nullable = permissions; + + if (permissions === "granted") { + // Scanning parameters + const formats = [Format.QRCode]; + const windowed = true; + + if (scanning) return; + scanning = true; + scan({ formats, windowed }) + .then((res) => { + console.log("Scan result:", res); + scannedData = res; + codeScannedDrawerOpen = true; + }) + .catch((error) => { + // TODO: display error to user + console.error("Scan error:", error); + }) + .finally(() => { + scanning = false; + }); + } + + console.error("Permission denied or not granted"); + // TODO: consider handling GUI for permission denied +} + +async function cancelScan() { + await cancel(); + scanning = false; +} + +onMount(async () => { + startScan(); +}); + +onDestroy(async () => { + await cancelScan(); +}); @@ -32,8 +104,8 @@ let flashlightOn = $state(false);

Point the camera at the code

- -
+ +
(flashlightOn = !flashlightOn)} />
@@ -66,7 +138,7 @@ let flashlightOn = $state(false);
-

Website URL

-

https://www.aave.inc/login

+

{scannedData?.content}

{ codeScannedDrawerOpen = false; }} + callback={() => { codeScannedDrawerOpen = false; startScan(); }} > Decline @@ -96,6 +168,7 @@ let flashlightOn = $state(false); callback={() => { codeScannedDrawerOpen = false loggedInDrawerOpen = true + startScan(); }} > Confirm @@ -112,7 +185,7 @@ let flashlightOn = $state(false);
- { loggedInDrawerOpen = false; }} + callback={() => { loggedInDrawerOpen = false; startScan(); }} > Close diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte index 7d3a9d9f..e88d6712 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte @@ -2,15 +2,15 @@ import { SettingsNavigationBtn } from "$lib/fragments"; import { runtime } from "$lib/global/runtime.svelte"; import { - Key01Icon, - LanguageSquareIcon, - Link02Icon, - PinCodeIcon, - Shield01Icon, + Key01Icon, + LanguageSquareIcon, + Link02Icon, + PinCodeIcon, + Shield01Icon, } from "@hugeicons/core-free-icons"; $effect(() => { - runtime.header.title = "Settings"; + runtime.header.title = "Settings"; }); diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte index bfa2a99e..a54f803e 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/settings/history/+page.svelte @@ -2,7 +2,7 @@ import { runtime } from "$lib/global/runtime.svelte"; $effect(() => { - runtime.header.title = "History"; + runtime.header.title = "History"; }); diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte index 1c64d656..b70b794a 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/settings/language/+page.svelte @@ -3,15 +3,15 @@ import { runtime } from "$lib/global/runtime.svelte"; import { Selector } from "$lib/ui"; let languages: { name: string; country: string }[] = [ - { name: "English", country: "gb" }, - { name: "Spanish", country: "es" }, - { name: "German", country: "de" }, - { name: "French", country: "fr" }, + { name: "English", country: "gb" }, + { name: "Spanish", country: "es" }, + { name: "German", country: "de" }, + { name: "French", country: "fr" }, ]; let selected = $state(""); $effect(() => { - runtime.header.title = "Language"; + runtime.header.title = "Language"; }); diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte index 8607c2c1..6a0ba3db 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/settings/pin/+page.svelte @@ -11,18 +11,18 @@ let isError = $state(false); let showDrawer = $state(false); const handleClose = async () => { - // close functionality goes here. - showDrawer = false; + // close functionality goes here. + showDrawer = false; }; const handleChangePIN = async () => { - if (repeatPin.length === 4 && newPin !== repeatPin) isError = true; - if (!isError) showDrawer = true; + if (repeatPin.length === 4 && newPin !== repeatPin) isError = true; + if (!isError) showDrawer = true; }; $effect(() => { - runtime.header.title = "Change PIN"; - if (repeatPin.length === 4 && newPin === repeatPin) isError = false; + runtime.header.title = "Change PIN"; + if (repeatPin.length === 4 && newPin === repeatPin) isError = false; }); diff --git a/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte index 452ecfec..9ceac2eb 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/settings/privacy/+page.svelte @@ -2,7 +2,7 @@ import { runtime } from "$lib/global/runtime.svelte"; $effect(() => { - runtime.header.title = "Privacy"; + runtime.header.title = "Privacy"; }); diff --git a/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte index 90cf9095..9a8b7324 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte @@ -5,7 +5,7 @@ import IdentityCard from "$lib/fragments/IdentityCard/IdentityCard.svelte"; import { ButtonAction } from "$lib/ui"; const handleFinish = async () => { - await goto("/main"); + await goto("/main"); }; diff --git a/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte new file mode 100644 index 00000000..4fd62b79 --- /dev/null +++ b/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte @@ -0,0 +1,97 @@ + + +
+
+ + handlePinInput(pin)} /> +

Your PIN does not match, try again.

+
+ + Clear Pin + +
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte index c349b53e..ec88a5f3 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte @@ -6,13 +6,13 @@ import { ButtonAction, Drawer } from "$lib/ui"; let isPaneOpen = $state(false); const handleGetStarted = async () => { - //get started functionality - isPaneOpen = true; + //get started functionality + isPaneOpen = true; }; const handleNext = async () => { - //handle next functionlity - goto("/verify"); + //handle next functionlity + goto("/verify"); }; diff --git a/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte index e910a576..b9d788a6 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/register/+page.svelte @@ -1,55 +1,87 @@ @@ -61,7 +93,8 @@ $effect(() => { subtitle="Enter a 4-digit PIN code" class="mb-[14vh]" /> - + +

Your PIN does not match, try again.

Confirm @@ -73,8 +106,7 @@ $effect(() => { subtitle="Confirm by entering pin again" class="mb-[14vh]" /> - -

Your PIN does not match, try again.

+ Confirm @@ -106,10 +138,13 @@ $effect(() => { {#if !isBiometricsAdded}
Skip - Set up +
+ Set up +

Biometrics unavailable.

+
{:else} Continue {/if} {/if} - \ No newline at end of file + diff --git a/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte index 1490cbc0..93ff78a1 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte @@ -4,7 +4,7 @@ import { Hero, IdentityCard } from "$lib/fragments"; import { ButtonAction } from "$lib/ui"; const handleNext = async () => { - await goto("/e-passport"); + await goto("/e-passport"); }; diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte index 9940fb04..c7fb2de2 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte @@ -1,7 +1,28 @@
@@ -12,5 +33,12 @@ import { ButtonAction } from "$lib/ui"; /> passport - goto("/register")}>I'm ready -
\ No newline at end of file + { + try { + await handleVerification(); + } catch (error) { + console.error("Verification failed:", error); + // Consider adding user-facing error handling here + } + }}>I'm ready + diff --git a/infrastructure/eid-wallet/src/routes/+layout.svelte b/infrastructure/eid-wallet/src/routes/+layout.svelte index 10cf01da..dc6e7a12 100644 --- a/infrastructure/eid-wallet/src/routes/+layout.svelte +++ b/infrastructure/eid-wallet/src/routes/+layout.svelte @@ -1,68 +1,93 @@ - + {#if showSplashScreen} {:else} diff --git a/infrastructure/eid-wallet/src/routes/+page.svelte b/infrastructure/eid-wallet/src/routes/+page.svelte index b709cedf..0936edc9 100644 --- a/infrastructure/eid-wallet/src/routes/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/+page.svelte @@ -1,7 +1,40 @@ + + + Clear Pin + + +{cleared ? "Pin cleared, restart app" : "Login"} diff --git a/infrastructure/w3id/biome.json b/infrastructure/w3id/biome.json index f8e45519..860a937f 100644 --- a/infrastructure/w3id/biome.json +++ b/infrastructure/w3id/biome.json @@ -1,3 +1,4 @@ { - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json" + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "extends": ["../../biome.json"] } diff --git a/infrastructure/w3id/package.json b/infrastructure/w3id/package.json index a278bb85..5d09a542 100644 --- a/infrastructure/w3id/package.json +++ b/infrastructure/w3id/package.json @@ -1,47 +1,48 @@ { - "name": "w3id", - "version": "1.0.0", - "description": "", - "scripts": { - "test": "vitest", - "dev": "tsc --watch", - "check-format": "npx @biomejs/biome format ./src", - "format": "npx @biomejs/biome format --write ./src", - "lint": "npx @biomejs/biome lint --write ./src", - "check": "npx @biomejs/biome check --write ./src", - "check-types": "tsc --noEmit", - "build": "npm run build:node && npm run build:browser", - "build:node": "tsc -p tsconfig.node.json", - "build:browser": "tsc -p tsconfig.browser.json", - "postinstall": "npm run build" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "canonicalize": "^2.1.0", - "multiformats": "^13.3.2", - "tweetnacl": "^1.0.3", - "uuid": "^11.1.0" - }, - "devDependencies": { - "@ngneat/falso": "^7.3.0", - "@types/node": "^22.13.10", - "typescript": "^5.8.2", - "vitest": "^3.0.9" - }, - "main": "./dist/node/index.js", - "module": "./dist/browser/index.js", - "types": "./dist/types/index.d.ts", - "exports": { - ".": { - "require": "./dist/node/index.js", - "import": "./dist/browser/index.js", - "types": "./dist/types/index.d.ts" + "name": "w3id", + "version": "1.0.0", + "description": "", + "scripts": { + "test": "vitest", + "dev": "tsc --watch", + "check-format": "npx @biomejs/biome format ./src", + "format": "npx @biomejs/biome format --write ./src", + "lint": "npx @biomejs/biome lint --write ./src", + "check": "npx @biomejs/biome check --write ./src", + "check-types": "tsc --noEmit", + "build": "npm run build:node && npm run build:browser", + "build:node": "tsc -p tsconfig.node.json", + "build:browser": "tsc -p tsconfig.browser.json", + "postinstall": "npm run build" }, - "./package.json": "./package.json" - }, - "files": [ - "dist/**/*" - ] + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "canonicalize": "^2.1.0", + "multiformats": "^13.3.2", + "tweetnacl": "^1.0.3", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@ngneat/falso": "^7.3.0", + "@types/node": "^22.13.10", + "typescript": "^5.8.2", + "vitest": "^3.0.9" + }, + "main": "./dist/node/index.js", + "module": "./dist/browser/index.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "require": "./dist/node/index.js", + "import": "./dist/browser/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "files": [ + "dist/**/*" + ] } diff --git a/infrastructure/w3id/src/errors/errors.ts b/infrastructure/w3id/src/errors/errors.ts index 31bc90ef..2dafde3b 100644 --- a/infrastructure/w3id/src/errors/errors.ts +++ b/infrastructure/w3id/src/errors/errors.ts @@ -1,34 +1,34 @@ export class MalformedIndexChainError extends Error { - constructor(message = "Malformed index chain detected") { - super(message); - this.name = "MalformedIndexChainError"; - } + constructor(message = "Malformed index chain detected") { + super(message); + this.name = "MalformedIndexChainError"; + } } export class MalformedHashChainError extends Error { - constructor(message = "Malformed hash chain detected") { - super(message); - this.name = "MalformedHashChainError"; - } + constructor(message = "Malformed hash chain detected") { + super(message); + this.name = "MalformedHashChainError"; + } } export class BadSignatureError extends Error { - constructor(message = "Bad signature detected") { - super(message); - this.name = "BadSignatureError"; - } + constructor(message = "Bad signature detected") { + super(message); + this.name = "BadSignatureError"; + } } export class BadNextKeySpecifiedError extends Error { - constructor(message = "Bad next key specified") { - super(message); - this.name = "BadNextKeySpecifiedError"; - } + constructor(message = "Bad next key specified") { + super(message); + this.name = "BadNextKeySpecifiedError"; + } } export class BadOptionsSpecifiedError extends Error { - constructor(message = "Bad options specified") { - super(message); - this.name = "BadOptionsSpecifiedError"; - } + constructor(message = "Bad options specified") { + super(message); + this.name = "BadOptionsSpecifiedError"; + } } diff --git a/infrastructure/w3id/src/logs/log-manager.ts b/infrastructure/w3id/src/logs/log-manager.ts index d7cf2f6b..99a64c4d 100644 --- a/infrastructure/w3id/src/logs/log-manager.ts +++ b/infrastructure/w3id/src/logs/log-manager.ts @@ -1,22 +1,22 @@ import canonicalize from "canonicalize"; import { - BadNextKeySpecifiedError, - BadOptionsSpecifiedError, - BadSignatureError, - MalformedHashChainError, - MalformedIndexChainError, + BadNextKeySpecifiedError, + BadOptionsSpecifiedError, + BadSignatureError, + MalformedHashChainError, + MalformedIndexChainError, } from "../errors/errors"; import { isSubsetOf } from "../utils/array"; import { hash } from "../utils/hash"; import { - type CreateLogEventOptions, - type GenesisLogOptions, - type LogEvent, - type RotationLogOptions, - type Signer, - type VerifierCallback, - isGenesisOptions, - isRotationOptions, + type CreateLogEventOptions, + type GenesisLogOptions, + type LogEvent, + type RotationLogOptions, + type Signer, + type VerifierCallback, + isGenesisOptions, + isRotationOptions, } from "./log.types"; import type { StorageSpec } from "./storage/storage-spec"; @@ -28,175 +28,185 @@ import type { StorageSpec } from "./storage/storage-spec"; // TODO: Create a specification link inside our docs for how generation of identifier works export class IDLogManager { - repository: StorageSpec; - signer: Signer; - - constructor(repository: StorageSpec, signer: Signer) { - this.repository = repository; - this.signer = signer; - } - - /** - * Validate a chain of W3ID logs - * - * @param {LogEvent[]} log - * @param {VerifierCallback} verifyCallback - * @returns {Promise} - */ - - static async validateLogChain( - log: LogEvent[], - verifyCallback: VerifierCallback, - ): Promise { - let currIndex = 0; - let currentNextKeyHashesSeen: string[] = []; - let lastUpdateKeysSeen: string[] = []; - let lastHash: string | null = null; - - for (const e of log) { - const [_index, _hash] = e.versionId.split("-"); - const index = Number(_index); - if (currIndex !== index) throw new MalformedIndexChainError(); - const hashedUpdateKeys = await Promise.all( - e.updateKeys.map(async (k) => await hash(k)), - ); - if (index > 0) { - const updateKeysSeen = isSubsetOf( - hashedUpdateKeys, - currentNextKeyHashesSeen, - ); - if (!updateKeysSeen || lastHash !== _hash) - throw new MalformedHashChainError(); - } - - currentNextKeyHashesSeen = e.nextKeyHashes; - await IDLogManager.verifyLogEventProof( - e, - lastUpdateKeysSeen.length > 0 ? lastUpdateKeysSeen : e.updateKeys, - verifyCallback, - ); - lastUpdateKeysSeen = e.updateKeys; - currIndex++; - lastHash = await hash(canonicalize(e) as string); - } - return true; - } - - /** - * Validate cryptographic signature on a single LogEvent - * - * @param {LogEvent} e - * @param {string[]} currentUpdateKeys - * @param {VerifierCallback} verifyCallback - * @returns {Promise} - */ - private static async verifyLogEventProof( - e: LogEvent, - currentUpdateKeys: string[], - verifyCallback: VerifierCallback, - ): Promise { - const proofs = e.proofs; - const copy = JSON.parse(JSON.stringify(e)); - // biome-ignore lint/performance/noDelete: we need to delete proof completely - delete copy.proofs; - const canonicalJson = canonicalize(copy); - let verified = false; - if (!proofs) - throw new BadSignatureError("No proof found in the log event."); - for (const key of currentUpdateKeys) { - const signValidates = await verifyCallback( - canonicalJson as string, - proofs, - key, - ); - if (signValidates) verified = true; - } - if (!verified) throw new BadSignatureError(); - } - - /** - * Append a new log entry for a W3ID - * - * @param {LogEvent[]} entries - * @param {RotationLogOptions} options - * @returns Promise - */ - private async appendEntry(entries: LogEvent[], options: RotationLogOptions) { - const { nextKeyHashes, nextKeySigner } = options; - const latestEntry = entries[entries.length - 1]; - const logHash = await hash(latestEntry); - const index = Number(latestEntry.versionId.split("-")[0]) + 1; - - const currKeyHash = await hash(nextKeySigner.pubKey); - if (!latestEntry.nextKeyHashes.includes(currKeyHash)) - throw new BadNextKeySpecifiedError(); - - const logEvent: LogEvent = { - id: latestEntry.id, - versionTime: new Date(Date.now()), - versionId: `${index}-${logHash}`, - updateKeys: [nextKeySigner.pubKey], - nextKeyHashes: nextKeyHashes, - method: "w3id:v0.0.0", - }; - - const signature = await this.signer.sign(canonicalize(logEvent) as string); - logEvent.proofs = [ - { - kid: `${logEvent.id}#0`, - alg: this.signer.alg, - signature, - }, - ]; - - await this.repository.create(logEvent); - this.signer = nextKeySigner; - return logEvent; - } - - /** - * Create genesis entry for a W3ID log - * - * @param {GenesisLogOptions} options - * @returns Promise - */ - private async createGenesisEntry(options: GenesisLogOptions) { - const { id, nextKeyHashes } = options; - const idTag = id.includes("@") ? id.split("@")[1] : id; - const logEvent: LogEvent = { - id, - versionId: `0-${idTag}`, - versionTime: new Date(Date.now()), - updateKeys: [this.signer.pubKey], - nextKeyHashes: nextKeyHashes, - method: "w3id:v0.0.0", - }; - const signature = await this.signer.sign(canonicalize(logEvent) as string); - logEvent.proofs = [ - { - kid: `${id}#0`, - alg: this.signer.alg, - signature, - }, - ]; - - await this.repository.create(logEvent); - return logEvent; - } - - /** - * Create a log event and save it to the repository - * - * @param {CreateLogEventOptions} options - * @returns Promise - */ - async createLogEvent(options: CreateLogEventOptions): Promise { - const entries = await this.repository.findMany({}); - if (entries.length > 0) { - if (!isRotationOptions(options)) throw new BadOptionsSpecifiedError(); - return this.appendEntry(entries, options); - } - if (!isGenesisOptions(options)) throw new BadOptionsSpecifiedError(); - return this.createGenesisEntry(options); - } + repository: StorageSpec; + signer: Signer; + + constructor(repository: StorageSpec, signer: Signer) { + this.repository = repository; + this.signer = signer; + } + + /** + * Validate a chain of W3ID logs + * + * @param {LogEvent[]} log + * @param {VerifierCallback} verifyCallback + * @returns {Promise} + */ + + static async validateLogChain( + log: LogEvent[], + verifyCallback: VerifierCallback, + ): Promise { + let currIndex = 0; + let currentNextKeyHashesSeen: string[] = []; + let lastUpdateKeysSeen: string[] = []; + let lastHash: string | null = null; + + for (const e of log) { + const [_index, _hash] = e.versionId.split("-"); + const index = Number(_index); + if (currIndex !== index) throw new MalformedIndexChainError(); + const hashedUpdateKeys = await Promise.all( + e.updateKeys.map(async (k) => await hash(k)), + ); + if (index > 0) { + const updateKeysSeen = isSubsetOf( + hashedUpdateKeys, + currentNextKeyHashesSeen, + ); + if (!updateKeysSeen || lastHash !== _hash) + throw new MalformedHashChainError(); + } + + currentNextKeyHashesSeen = e.nextKeyHashes; + await IDLogManager.verifyLogEventProof( + e, + lastUpdateKeysSeen.length > 0 + ? lastUpdateKeysSeen + : e.updateKeys, + verifyCallback, + ); + lastUpdateKeysSeen = e.updateKeys; + currIndex++; + lastHash = await hash(canonicalize(e) as string); + } + return true; + } + + /** + * Validate cryptographic signature on a single LogEvent + * + * @param {LogEvent} e + * @param {string[]} currentUpdateKeys + * @param {VerifierCallback} verifyCallback + * @returns {Promise} + */ + private static async verifyLogEventProof( + e: LogEvent, + currentUpdateKeys: string[], + verifyCallback: VerifierCallback, + ): Promise { + const proofs = e.proofs; + const copy = JSON.parse(JSON.stringify(e)); + // biome-ignore lint/performance/noDelete: we need to delete proof completely + delete copy.proofs; + const canonicalJson = canonicalize(copy); + let verified = false; + if (!proofs) + throw new BadSignatureError("No proof found in the log event."); + for (const key of currentUpdateKeys) { + const signValidates = await verifyCallback( + canonicalJson as string, + proofs, + key, + ); + if (signValidates) verified = true; + } + if (!verified) throw new BadSignatureError(); + } + + /** + * Append a new log entry for a W3ID + * + * @param {LogEvent[]} entries + * @param {RotationLogOptions} options + * @returns Promise + */ + private async appendEntry( + entries: LogEvent[], + options: RotationLogOptions, + ) { + const { nextKeyHashes, nextKeySigner } = options; + const latestEntry = entries[entries.length - 1]; + const logHash = await hash(latestEntry); + const index = Number(latestEntry.versionId.split("-")[0]) + 1; + + const currKeyHash = await hash(nextKeySigner.pubKey); + if (!latestEntry.nextKeyHashes.includes(currKeyHash)) + throw new BadNextKeySpecifiedError(); + + const logEvent: LogEvent = { + id: latestEntry.id, + versionTime: new Date(Date.now()), + versionId: `${index}-${logHash}`, + updateKeys: [nextKeySigner.pubKey], + nextKeyHashes: nextKeyHashes, + method: "w3id:v0.0.0", + }; + + const signature = await this.signer.sign( + canonicalize(logEvent) as string, + ); + logEvent.proofs = [ + { + kid: `${logEvent.id}#0`, + alg: this.signer.alg, + signature, + }, + ]; + + await this.repository.create(logEvent); + this.signer = nextKeySigner; + return logEvent; + } + + /** + * Create genesis entry for a W3ID log + * + * @param {GenesisLogOptions} options + * @returns Promise + */ + private async createGenesisEntry(options: GenesisLogOptions) { + const { id, nextKeyHashes } = options; + const idTag = id.includes("@") ? id.split("@")[1] : id; + const logEvent: LogEvent = { + id, + versionId: `0-${idTag}`, + versionTime: new Date(Date.now()), + updateKeys: [this.signer.pubKey], + nextKeyHashes: nextKeyHashes, + method: "w3id:v0.0.0", + }; + const signature = await this.signer.sign( + canonicalize(logEvent) as string, + ); + logEvent.proofs = [ + { + kid: `${id}#0`, + alg: this.signer.alg, + signature, + }, + ]; + + await this.repository.create(logEvent); + return logEvent; + } + + /** + * Create a log event and save it to the repository + * + * @param {CreateLogEventOptions} options + * @returns Promise + */ + async createLogEvent(options: CreateLogEventOptions): Promise { + const entries = await this.repository.findMany({}); + if (entries.length > 0) { + if (!isRotationOptions(options)) + throw new BadOptionsSpecifiedError(); + return this.appendEntry(entries, options); + } + if (!isGenesisOptions(options)) throw new BadOptionsSpecifiedError(); + return this.createGenesisEntry(options); + } } diff --git a/infrastructure/w3id/src/logs/log.types.ts b/infrastructure/w3id/src/logs/log.types.ts index 0545ed62..a40f3eb4 100644 --- a/infrastructure/w3id/src/logs/log.types.ts +++ b/infrastructure/w3id/src/logs/log.types.ts @@ -1,67 +1,67 @@ export type Proof = { - kid: string; - signature: string; - alg: string; + kid: string; + signature: string; + alg: string; }; export type LogEvent = { - id: string; - versionId: string; - versionTime: Date; - updateKeys: string[]; - nextKeyHashes: string[]; - method: `w3id:v${string}`; - proofs?: Proof[]; + id: string; + versionId: string; + versionTime: Date; + updateKeys: string[]; + nextKeyHashes: string[]; + method: `w3id:v${string}`; + proofs?: Proof[]; }; export type VerifierCallback = ( - message: string, - proofs: Proof[], - pubKey: string, + message: string, + proofs: Proof[], + pubKey: string, ) => Promise; export type JWTHeader = { - alg: string; - typ: "JWT"; - kid?: string; + alg: string; + typ: "JWT"; + kid?: string; }; export type JWTPayload = { - [key: string]: unknown; - iat?: number; - exp?: number; - nbf?: number; - iss?: string; - sub?: string; - aud?: string; - jti?: string; + [key: string]: unknown; + iat?: number; + exp?: number; + nbf?: number; + iss?: string; + sub?: string; + aud?: string; + jti?: string; }; export type Signer = { - sign: (message: string) => Promise | string; - pubKey: string; - alg: string; + sign: (message: string) => Promise | string; + pubKey: string; + alg: string; }; export type RotationLogOptions = { - nextKeyHashes: string[]; - nextKeySigner: Signer; + nextKeyHashes: string[]; + nextKeySigner: Signer; }; export type GenesisLogOptions = { - nextKeyHashes: string[]; - id: string; + nextKeyHashes: string[]; + id: string; }; export function isGenesisOptions( - options: CreateLogEventOptions, + options: CreateLogEventOptions, ): options is GenesisLogOptions { - return "id" in options; + return "id" in options; } export function isRotationOptions( - options: CreateLogEventOptions, + options: CreateLogEventOptions, ): options is RotationLogOptions { - return "nextKeySigner" in options; + return "nextKeySigner" in options; } export type CreateLogEventOptions = GenesisLogOptions | RotationLogOptions; diff --git a/infrastructure/w3id/src/logs/storage/storage-spec.ts b/infrastructure/w3id/src/logs/storage/storage-spec.ts index ab66f9d7..9a41770c 100644 --- a/infrastructure/w3id/src/logs/storage/storage-spec.ts +++ b/infrastructure/w3id/src/logs/storage/storage-spec.ts @@ -5,40 +5,40 @@ */ export declare class StorageSpec { - /** - * Build a new storage driver - * - * @param {...any[]} props - * @returns Promise - */ - - // biome-ignore lint: lint/suspicious/noExplicitAny - public static build(...props: any[]): Promise>; - - /** - * Create a new entry in the storage - * - * @param body - * @returns Promise - */ - - public create(body: T): Promise; - - /** - * Find one entry in the storage using partial of K - * - * @param {Partial} options - * @returns Promise - */ - - public findOne(options: Partial): Promise; - - /** - * Find many entities in the storage using partial of K - * - * @param {Partial} options - * @returns Promise - */ - - public findMany(options: Partial): Promise; + /** + * Build a new storage driver + * + * @param {...any[]} props + * @returns Promise + */ + + // biome-ignore lint: lint/suspicious/noExplicitAny + public static build(...props: any[]): Promise>; + + /** + * Create a new entry in the storage + * + * @param body + * @returns Promise + */ + + public create(body: T): Promise; + + /** + * Find one entry in the storage using partial of K + * + * @param {Partial} options + * @returns Promise + */ + + public findOne(options: Partial): Promise; + + /** + * Find many entities in the storage using partial of K + * + * @param {Partial} options + * @returns Promise + */ + + public findMany(options: Partial): Promise; } diff --git a/infrastructure/w3id/src/utils/array.ts b/infrastructure/w3id/src/utils/array.ts index 19224fec..f1dd49f7 100644 --- a/infrastructure/w3id/src/utils/array.ts +++ b/infrastructure/w3id/src/utils/array.ts @@ -11,18 +11,18 @@ */ export function isSubsetOf(a: unknown[], b: unknown[]) { - const map = new Map(); + const map = new Map(); - for (const el of b) { - map.set(el, (map.get(el) || 0) + 1); - } + for (const el of b) { + map.set(el, (map.get(el) || 0) + 1); + } - for (const el of a) { - if (!map.has(el) || map.get(el) === 0) { - return false; - } - map.set(el, map.get(el) - 1); - } + for (const el of a) { + if (!map.has(el) || map.get(el) === 0) { + return false; + } + map.set(el, map.get(el) - 1); + } - return true; + return true; } diff --git a/infrastructure/w3id/src/utils/codec.ts b/infrastructure/w3id/src/utils/codec.ts index f05ce212..8181f59e 100644 --- a/infrastructure/w3id/src/utils/codec.ts +++ b/infrastructure/w3id/src/utils/codec.ts @@ -1,20 +1,20 @@ export function uint8ArrayToHex(bytes: Uint8Array): string { - return Array.from(bytes) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); + return Array.from(bytes) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); } export function hexToUint8Array(hex: string): Uint8Array { - if (hex.length % 2 !== 0) { - throw new Error("Hex string must have an even length"); - } - const bytes = new Uint8Array(hex.length / 2); - for (let i = 0; i < hex.length; i += 2) { - bytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16); - } - return bytes; + if (hex.length % 2 !== 0) { + throw new Error("Hex string must have an even length"); + } + const bytes = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length; i += 2) { + bytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16); + } + return bytes; } export function stringToUint8Array(str: string): Uint8Array { - return new TextEncoder().encode(str); + return new TextEncoder().encode(str); } diff --git a/infrastructure/w3id/src/utils/hash.ts b/infrastructure/w3id/src/utils/hash.ts index d7e8c852..4055a495 100644 --- a/infrastructure/w3id/src/utils/hash.ts +++ b/infrastructure/w3id/src/utils/hash.ts @@ -2,25 +2,25 @@ import canonicalize from "canonicalize"; import { uint8ArrayToHex } from "./codec"; export async function hash( - input: string | Record, + input: string | Record, ): Promise { - let dataToHash: string; + let dataToHash: string; - if (typeof input === "string") { - dataToHash = input; - } else { - const canonical = canonicalize(input); - if (!canonical) { - throw new Error( - `Failed to canonicalize object: ${JSON.stringify(input).substring(0, 100)}...`, - ); - } - dataToHash = canonical; - } + if (typeof input === "string") { + dataToHash = input; + } else { + const canonical = canonicalize(input); + if (!canonical) { + throw new Error( + `Failed to canonicalize object: ${JSON.stringify(input).substring(0, 100)}...`, + ); + } + dataToHash = canonical; + } - const buffer = new TextEncoder().encode(dataToHash); - const hashBuffer = await crypto.subtle.digest("SHA-256", buffer); - const hashHex = uint8ArrayToHex(new Uint8Array(hashBuffer)); + const buffer = new TextEncoder().encode(dataToHash); + const hashBuffer = await crypto.subtle.digest("SHA-256", buffer); + const hashHex = uint8ArrayToHex(new Uint8Array(hashBuffer)); - return hashHex; + return hashHex; } diff --git a/infrastructure/w3id/src/utils/jwt.ts b/infrastructure/w3id/src/utils/jwt.ts index c163f937..41868427 100644 --- a/infrastructure/w3id/src/utils/jwt.ts +++ b/infrastructure/w3id/src/utils/jwt.ts @@ -4,21 +4,21 @@ import type { JWTHeader, JWTPayload, Signer } from "../logs/log.types"; * Encodes a string to base64url format */ function base64urlEncode(str: string): string { - return Buffer.from(str) - .toString("base64") - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=/g, ""); + return Buffer.from(str) + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); } /** * Decodes a base64url string */ function base64urlDecode(str: string): string { - return Buffer.from( - str.replace(/-/g, "+").replace(/_/g, "/"), - "base64", - ).toString(); + return Buffer.from( + str.replace(/-/g, "+").replace(/_/g, "/"), + "base64", + ).toString(); } /** @@ -28,9 +28,9 @@ function base64urlDecode(str: string): string { * @returns The unsigned JWT */ export function createJWT(header: JWTHeader, payload: JWTPayload): string { - const encodedHeader = base64urlEncode(JSON.stringify(header)); - const encodedPayload = base64urlEncode(JSON.stringify(payload)); - return `${encodedHeader}.${encodedPayload}`; + const encodedHeader = base64urlEncode(JSON.stringify(header)); + const encodedPayload = base64urlEncode(JSON.stringify(payload)); + return `${encodedHeader}.${encodedPayload}`; } /** @@ -42,19 +42,19 @@ export function createJWT(header: JWTHeader, payload: JWTPayload): string { * @returns The signed JWT */ export async function signJWT( - signer: Signer, - payload: JWTPayload, - kid: string, - header?: JWTHeader, + signer: Signer, + payload: JWTPayload, + kid: string, + header?: JWTHeader, ): Promise { - const jwtHeader = header || { - alg: signer.alg, - typ: "JWT", - kid, - }; - const jwt = createJWT(jwtHeader, payload); - const signature = await signer.sign(jwt); - return `${jwt}.${signature}`; + const jwtHeader = header || { + alg: signer.alg, + typ: "JWT", + kid, + }; + const jwt = createJWT(jwtHeader, payload); + const signature = await signer.sign(jwt); + return `${jwt}.${signature}`; } /** @@ -66,16 +66,16 @@ export async function signJWT( * @returns True if the signature is valid, false otherwise */ export async function verifyJWT( - jwt: string, - signature: string, - verifier: ( - message: string, - signature: string, - pubKey: string, - ) => Promise, - pubKey: string, + jwt: string, + signature: string, + verifier: ( + message: string, + signature: string, + pubKey: string, + ) => Promise, + pubKey: string, ): Promise { - return verifier(jwt, signature, pubKey); + return verifier(jwt, signature, pubKey); } /** @@ -84,8 +84,8 @@ export async function verifyJWT( * @returns The JWT header */ export function getJWTHeader(jwt: string): JWTHeader { - const [header] = jwt.split("."); - return JSON.parse(base64urlDecode(header)); + const [header] = jwt.split("."); + return JSON.parse(base64urlDecode(header)); } /** @@ -94,6 +94,6 @@ export function getJWTHeader(jwt: string): JWTHeader { * @returns The JWT payload */ export function getJWTPayload(jwt: string): JWTPayload { - const [, payload] = jwt.split("."); - return JSON.parse(base64urlDecode(payload)); + const [, payload] = jwt.split("."); + return JSON.parse(base64urlDecode(payload)); } diff --git a/infrastructure/w3id/src/utils/rand.ts b/infrastructure/w3id/src/utils/rand.ts index f9710fe0..8360fa88 100644 --- a/infrastructure/w3id/src/utils/rand.ts +++ b/infrastructure/w3id/src/utils/rand.ts @@ -6,17 +6,17 @@ */ export function generateRandomAlphaNum(length = 16): string { - const chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let result = ""; - const charsLength = chars.length; - const randomValues = new Uint32Array(length); + const chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let result = ""; + const charsLength = chars.length; + const randomValues = new Uint32Array(length); - crypto.getRandomValues(randomValues); + crypto.getRandomValues(randomValues); - for (let i = 0; i < length; i++) { - result += chars.charAt(randomValues[i] % charsLength); - } + for (let i = 0; i < length; i++) { + result += chars.charAt(randomValues[i] % charsLength); + } - return result; + return result; } diff --git a/infrastructure/w3id/src/utils/uuid.ts b/infrastructure/w3id/src/utils/uuid.ts index 1ffc1ae1..ecb953b3 100644 --- a/infrastructure/w3id/src/utils/uuid.ts +++ b/infrastructure/w3id/src/utils/uuid.ts @@ -10,8 +10,8 @@ import { v4 as uuidv4, v5 as uuidv5 } from "uuid"; */ export function generateUuid( - entropy: string, - namespace: string = uuidv4(), + entropy: string, + namespace: string = uuidv4(), ): string { - return uuidv5(entropy, namespace); + return uuidv5(entropy, namespace); } diff --git a/infrastructure/w3id/src/w3id.ts b/infrastructure/w3id/src/w3id.ts index b2acb846..c6db4579 100644 --- a/infrastructure/w3id/src/w3id.ts +++ b/infrastructure/w3id/src/w3id.ts @@ -7,150 +7,152 @@ import { generateRandomAlphaNum } from "./utils/rand"; import { generateUuid } from "./utils/uuid"; export class W3ID { - constructor( - public id: string, - public logs?: IDLogManager, - ) {} + constructor( + public id: string, + public logs?: IDLogManager, + ) {} - /** - * Signs a JWT with the W3ID's signer - * @param payload - The JWT payload - * @param header - Optional JWT header (defaults to using the signer's alg and W3ID's id as kid) - * @returns The signed JWT - */ - public async signJWT( - payload: JWTPayload, - header?: JWTHeader, - ): Promise { - if (!this.logs?.signer) { - throw new Error("W3ID must have a signer to sign JWTs"); - } - return signJWT(this.logs.signer, payload, `@${this.id}#0`, header); - } + /** + * Signs a JWT with the W3ID's signer + * @param payload - The JWT payload + * @param header - Optional JWT header (defaults to using the signer's alg and W3ID's id as kid) + * @returns The signed JWT + */ + public async signJWT( + payload: JWTPayload, + header?: JWTHeader, + ): Promise { + if (!this.logs?.signer) { + throw new Error("W3ID must have a signer to sign JWTs"); + } + return signJWT(this.logs.signer, payload, `@${this.id}#0`, header); + } } export class W3IDBuilder { - private signer?: Signer; - private repository?: StorageSpec; - private entropy?: string; - private namespace?: string; - private nextKeyHash?: string; - private global?: boolean = false; - private id?: string; + private signer?: Signer; + private repository?: StorageSpec; + private entropy?: string; + private namespace?: string; + private nextKeyHash?: string; + private global?: boolean = false; + private id?: string; - /** - * Specify entropy to create the identity with - * - * @param {string} str - */ - public withEntropy(str: string): W3IDBuilder { - this.entropy = str; - return this; - } + /** + * Specify entropy to create the identity with + * + * @param {string} str + */ + public withEntropy(str: string): W3IDBuilder { + this.entropy = str; + return this; + } - /** - * Specify namespace to use to generate the UUIDv5 - * - * @param {string} uuid - */ - public withNamespace(uuid: string): W3IDBuilder { - this.namespace = uuid; - return this; - } + /** + * Specify namespace to use to generate the UUIDv5 + * + * @param {string} uuid + */ + public withNamespace(uuid: string): W3IDBuilder { + this.namespace = uuid; + return this; + } - /** - * Specify whether to create a global identifier or a local identifer - * - * According to the project specification there are supposed to be 2 main types of - * W3ID's ones which are tied to more permanent entities - * - * A global identifer is expected to live at the registry and starts with an \`@\` - * - * @param {boolean} isGlobal - */ - public withGlobal(isGlobal: boolean): W3IDBuilder { - this.global = isGlobal; - return this; - } + /** + * Specify whether to create a global identifier or a local identifer + * + * According to the project specification there are supposed to be 2 main types of + * W3ID's ones which are tied to more permanent entities + * + * A global identifer is expected to live at the registry and starts with an \`@\` + * + * @param {boolean} isGlobal + */ + public withGlobal(isGlobal: boolean): W3IDBuilder { + this.global = isGlobal; + return this; + } - /** - * Add a logs repository to the W3ID, a rotateble key attached W3ID would need a - * repository in which the logs would be stored - * - * @param {StorageSpec} storage - */ - public withRepository(storage: StorageSpec): W3IDBuilder { - this.repository = storage; - return this; - } + /** + * Add a logs repository to the W3ID, a rotateble key attached W3ID would need a + * repository in which the logs would be stored + * + * @param {StorageSpec} storage + */ + public withRepository( + storage: StorageSpec, + ): W3IDBuilder { + this.repository = storage; + return this; + } - /** - * Pre-specify a UUID to use as the W3ID - * - * @param {string} id - */ - public withId(id: string): W3IDBuilder { - this.id = id; - return this; - } + /** + * Pre-specify a UUID to use as the W3ID + * + * @param {string} id + */ + public withId(id: string): W3IDBuilder { + this.id = id; + return this; + } - /** - * Attach a keypair to the W3ID, a key attached W3ID would also need a repository - * to be added. - * - * @param {Signer} signer - */ - public withSigner(signer: Signer): W3IDBuilder { - this.signer = signer; - return this; - } + /** + * Attach a keypair to the W3ID, a key attached W3ID would also need a repository + * to be added. + * + * @param {Signer} signer + */ + public withSigner(signer: Signer): W3IDBuilder { + this.signer = signer; + return this; + } - /** - * Specify the SHA256 hash of the next key which will sign the next log entry after - * rotation of keys - * - * @param {string} hash - */ - public withNextKeyHash(hash: string): W3IDBuilder { - this.nextKeyHash = hash; - return this; - } + /** + * Specify the SHA256 hash of the next key which will sign the next log entry after + * rotation of keys + * + * @param {string} hash + */ + public withNextKeyHash(hash: string): W3IDBuilder { + this.nextKeyHash = hash; + return this; + } - /** - * Build the W3ID with provided builder options - * - * @returns Promise - */ - public async build(): Promise { - if ((this.id && this.namespace) || (this.id && this.entropy)) - throw new Error( - "Namespace and Entropy can't be specified when using pre-defined ID", - ); - this.entropy = this.entropy ?? generateRandomAlphaNum(); - this.namespace = this.namespace ?? uuidv4(); - this.id = this.id?.includes("@") ? this.id.split("@")[1] : this.id; - const id = `${ - this.global ? "@" : "" - }${this.id?.toString() ?? generateUuid(this.entropy, this.namespace)}`; - if (!this.signer) { - return new W3ID(id); - } - if (!this.repository) - throw new Error( - "Repository is required, pass with `withRepository` method", - ); - if (!this.nextKeyHash) - throw new Error( - "NextKeyHash is required pass with `withNextKeyHash` method", - ); - const logs = new IDLogManager(this.repository, this.signer); + /** + * Build the W3ID with provided builder options + * + * @returns Promise + */ + public async build(): Promise { + if ((this.id && this.namespace) || (this.id && this.entropy)) + throw new Error( + "Namespace and Entropy can't be specified when using pre-defined ID", + ); + this.entropy = this.entropy ?? generateRandomAlphaNum(); + this.namespace = this.namespace ?? uuidv4(); + this.id = this.id?.includes("@") ? this.id.split("@")[1] : this.id; + const id = `${ + this.global ? "@" : "" + }${this.id?.toString() ?? generateUuid(this.entropy, this.namespace)}`; + if (!this.signer) { + return new W3ID(id); + } + if (!this.repository) + throw new Error( + "Repository is required, pass with `withRepository` method", + ); + if (!this.nextKeyHash) + throw new Error( + "NextKeyHash is required pass with `withNextKeyHash` method", + ); + const logs = new IDLogManager(this.repository, this.signer); - const currentLogs = await logs.repository.findMany({}); - if (currentLogs.length > 0) return new W3ID(id, logs); - await logs.createLogEvent({ - id, - nextKeyHashes: [this.nextKeyHash], - }); - return new W3ID(id, logs); - } + const currentLogs = await logs.repository.findMany({}); + if (currentLogs.length > 0) return new W3ID(id, logs); + await logs.createLogEvent({ + id, + nextKeyHashes: [this.nextKeyHash], + }); + return new W3ID(id, logs); + } } diff --git a/infrastructure/web3-adapter/package.json b/infrastructure/web3-adapter/package.json index 645d2f68..23b7325c 100644 --- a/infrastructure/web3-adapter/package.json +++ b/infrastructure/web3-adapter/package.json @@ -1,34 +1,38 @@ { - "name": "web3-adapter", - "version": "1.0.0", - "description": "Web3 adapter for platform-specific data mapping to universal schema", - "type": "module", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsc", - "test": "vitest", - "lint": "eslint src/**/*.ts" - }, - "dependencies": { - "evault-core": "workspace:*", - "test": "^3.3.0", - "vitest": "^3.1.2" - }, - "devDependencies": { - "@types/jest": "^29.5.0", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "eslint": "^8.38.0", - "jest": "^29.5.0", - "ts-jest": "^29.1.0", - "typescript": "^5.0.4" - }, - "jest": { - "preset": "ts-jest", - "testEnvironment": "node", - "testMatch": [ - "**/__tests__/**/*.test.ts" - ] - } + "name": "web3-adapter", + "version": "1.0.0", + "description": "Web3 adapter for platform-specific data mapping to universal schema", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest", + "check-format": "npx @biomejs/biome format ./src", + "format": "npx @biomejs/biome format --write ./src", + "lint": "npx @biomejs/biome lint --write ./src", + "check": "npx @biomejs/biome check --write ./src", + "check-types": "tsc --noEmit" + }, + "dependencies": { + "evault-core": "workspace:*", + "test": "^3.3.0", + "vitest": "^3.1.2" + }, + "devDependencies": { + "@types/jest": "^29.5.0", + "@typescript-eslint/eslint-plugin": "^5.59.0", + "@typescript-eslint/parser": "^5.59.0", + "eslint": "^8.38.0", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "typescript": "^5.0.4" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "testMatch": [ + "**/__tests__/**/*.test.ts" + ] + } } diff --git a/infrastructure/web3-adapter/src/__tests__/adapter.test.ts b/infrastructure/web3-adapter/src/__tests__/adapter.test.ts index 8b7491c9..4d384cdd 100644 --- a/infrastructure/web3-adapter/src/__tests__/adapter.test.ts +++ b/infrastructure/web3-adapter/src/__tests__/adapter.test.ts @@ -1,73 +1,73 @@ -import { describe, it, expect, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { Web3Adapter } from "../adapter.js"; describe("Web3Adapter", () => { - let adapter: Web3Adapter; + let adapter: Web3Adapter; - beforeEach(() => { - adapter = new Web3Adapter(); - }); + beforeEach(() => { + adapter = new Web3Adapter(); + }); - it("should transform platform data to universal format", () => { - // Register mappings for a platform - adapter.registerMapping("twitter", [ - { sourceField: "tweet", targetField: "content" }, - { sourceField: "likes", targetField: "reactions" }, - { sourceField: "replies", targetField: "comments" }, - ]); + it("should transform platform data to universal format", () => { + // Register mappings for a platform + adapter.registerMapping("twitter", [ + { sourceField: "tweet", targetField: "content" }, + { sourceField: "likes", targetField: "reactions" }, + { sourceField: "replies", targetField: "comments" }, + ]); - const twitterData = { - tweet: "Hello world!", - likes: 42, - replies: ["user1", "user2"], - }; + const twitterData = { + tweet: "Hello world!", + likes: 42, + replies: ["user1", "user2"], + }; - const universalData = adapter.toUniversal("twitter", twitterData); - expect(universalData).toEqual({ - content: "Hello world!", - reactions: 42, - comments: ["user1", "user2"], + const universalData = adapter.toUniversal("twitter", twitterData); + expect(universalData).toEqual({ + content: "Hello world!", + reactions: 42, + comments: ["user1", "user2"], + }); }); - }); - it("should transform universal data to platform format", () => { - // Register mappings for a platform - adapter.registerMapping("instagram", [ - { sourceField: "caption", targetField: "content" }, - { sourceField: "hearts", targetField: "reactions" }, - { sourceField: "comments", targetField: "comments" }, - ]); + it("should transform universal data to platform format", () => { + // Register mappings for a platform + adapter.registerMapping("instagram", [ + { sourceField: "caption", targetField: "content" }, + { sourceField: "hearts", targetField: "reactions" }, + { sourceField: "comments", targetField: "comments" }, + ]); - const universalData = { - content: "Hello world!", - reactions: 42, - comments: ["user1", "user2"], - }; + const universalData = { + content: "Hello world!", + reactions: 42, + comments: ["user1", "user2"], + }; - const instagramData = adapter.fromUniversal("instagram", universalData); - expect(instagramData).toEqual({ - caption: "Hello world!", - hearts: 42, - comments: ["user1", "user2"], + const instagramData = adapter.fromUniversal("instagram", universalData); + expect(instagramData).toEqual({ + caption: "Hello world!", + hearts: 42, + comments: ["user1", "user2"], + }); }); - }); - it("should handle field transformations", () => { - adapter.registerMapping("custom", [ - { - sourceField: "timestamp", - targetField: "date", - transform: (value: number) => new Date(value).toISOString(), - }, - ]); + it("should handle field transformations", () => { + adapter.registerMapping("custom", [ + { + sourceField: "timestamp", + targetField: "date", + transform: (value: number) => new Date(value).toISOString(), + }, + ]); - const customData = { - timestamp: 1677721600000, - }; + const customData = { + timestamp: 1677721600000, + }; - const universalData = adapter.toUniversal("custom", customData); - expect(universalData).toEqual({ - date: "2023-03-02T01:46:40.000Z", + const universalData = adapter.toUniversal("custom", customData); + expect(universalData).toEqual({ + date: "2023-03-02T01:46:40.000Z", + }); }); - }); }); diff --git a/infrastructure/web3-adapter/src/__tests__/evault.test.ts b/infrastructure/web3-adapter/src/__tests__/evault.test.ts index 06e51e99..67aa4d09 100644 --- a/infrastructure/web3-adapter/src/__tests__/evault.test.ts +++ b/infrastructure/web3-adapter/src/__tests__/evault.test.ts @@ -1,57 +1,57 @@ -import { describe, it, expect, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { Web3Adapter } from "../adapter.js"; const EVaultEndpoint = "http://localhost:4000/graphql"; async function queryGraphQL( - query: string, - variables: Record = {} + query: string, + variables: Record = {}, ) { - const response = await fetch(EVaultEndpoint, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ query, variables }), - }); - return response.json(); + const response = await fetch(EVaultEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ query, variables }), + }); + return response.json(); } describe("eVault Integration", () => { - let adapter: Web3Adapter; - let storedId: string; - - beforeEach(() => { - adapter = new Web3Adapter(); - }); - - it("should store and retrieve data from eVault", async () => { - // Register mappings for a platform - adapter.registerMapping("twitter", [ - { sourceField: "tweet", targetField: "text" }, - { sourceField: "likes", targetField: "userLikes" }, - { sourceField: "replies", targetField: "interactions" }, - { sourceField: "image", targetField: "image" }, - { - sourceField: "timestamp", - targetField: "dateCreated", - transform: (value: number) => new Date(value).toISOString(), - }, - ]); - - // Create platform-specific data - const twitterData = { - tweet: "Hello world!", - likes: ["@user1", "@user2"], - replies: ["reply1", "reply2"], - image: "https://example.com/image.jpg", - }; - - // Convert to universal format - const universalData = adapter.toUniversal("twitter", twitterData); - - // Store in eVault - const storeMutation = ` + let adapter: Web3Adapter; + let storedId: string; + + beforeEach(() => { + adapter = new Web3Adapter(); + }); + + it("should store and retrieve data from eVault", async () => { + // Register mappings for a platform + adapter.registerMapping("twitter", [ + { sourceField: "tweet", targetField: "text" }, + { sourceField: "likes", targetField: "userLikes" }, + { sourceField: "replies", targetField: "interactions" }, + { sourceField: "image", targetField: "image" }, + { + sourceField: "timestamp", + targetField: "dateCreated", + transform: (value: number) => new Date(value).toISOString(), + }, + ]); + + // Create platform-specific data + const twitterData = { + tweet: "Hello world!", + likes: ["@user1", "@user2"], + replies: ["reply1", "reply2"], + image: "https://example.com/image.jpg", + }; + + // Convert to universal format + const universalData = adapter.toUniversal("twitter", twitterData); + + // Store in eVault + const storeMutation = ` mutation StoreMetaEnvelope($input: MetaEnvelopeInput!) { storeMetaEnvelope(input: $input) { metaEnvelope { @@ -63,20 +63,22 @@ describe("eVault Integration", () => { } `; - const storeResult = await queryGraphQL(storeMutation, { - input: { - ontology: "SocialMediaPost", - payload: universalData, - acl: ["*"], - }, - }); - - expect(storeResult.errors).toBeUndefined(); - expect(storeResult.data.storeMetaEnvelope.metaEnvelope.id).toBeDefined(); - storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id; - - // Retrieve from eVault - const retrieveQuery = ` + const storeResult = await queryGraphQL(storeMutation, { + input: { + ontology: "SocialMediaPost", + payload: universalData, + acl: ["*"], + }, + }); + + expect(storeResult.errors).toBeUndefined(); + expect( + storeResult.data.storeMetaEnvelope.metaEnvelope.id, + ).toBeDefined(); + storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id; + + // Retrieve from eVault + const retrieveQuery = ` query GetMetaEnvelope($id: String!) { getMetaEnvelopeById(id: $id) { parsed @@ -84,55 +86,57 @@ describe("eVault Integration", () => { } `; - const retrieveResult = await queryGraphQL(retrieveQuery, { id: storedId }); - expect(retrieveResult.errors).toBeUndefined(); - const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed; - - // Convert back to platform format - const platformData = adapter.fromUniversal("twitter", retrievedData); - }); - - it("should exchange data between different platforms", async () => { - // Register mappings for Platform A (Twitter-like) - adapter.registerMapping("platformA", [ - { sourceField: "post", targetField: "text" }, - { sourceField: "reactions", targetField: "userLikes" }, - { sourceField: "comments", targetField: "interactions" }, - { sourceField: "media", targetField: "image" }, - { - sourceField: "createdAt", - targetField: "dateCreated", - transform: (value: number) => new Date(value).toISOString(), - }, - ]); - - // Register mappings for Platform B (Facebook-like) - adapter.registerMapping("platformB", [ - { sourceField: "content", targetField: "text" }, - { sourceField: "likes", targetField: "userLikes" }, - { sourceField: "responses", targetField: "interactions" }, - { sourceField: "attachment", targetField: "image" }, - { - sourceField: "postedAt", - targetField: "dateCreated", - transform: (value: string) => new Date(value).getTime(), - }, - ]); - - // Create data in Platform A format - const platformAData = { - post: "Cross-platform test post", - reactions: ["user1", "user2"], - comments: ["Great post!", "Thanks for sharing"], - media: "https://example.com/cross-platform.jpg", - createdAt: Date.now(), - }; - - // Convert Platform A data to universal format - const universalData = adapter.toUniversal("platformA", platformAData); - - // Store in eVault - const storeMutation = ` + const retrieveResult = await queryGraphQL(retrieveQuery, { + id: storedId, + }); + expect(retrieveResult.errors).toBeUndefined(); + const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed; + + // Convert back to platform format + const platformData = adapter.fromUniversal("twitter", retrievedData); + }); + + it("should exchange data between different platforms", async () => { + // Register mappings for Platform A (Twitter-like) + adapter.registerMapping("platformA", [ + { sourceField: "post", targetField: "text" }, + { sourceField: "reactions", targetField: "userLikes" }, + { sourceField: "comments", targetField: "interactions" }, + { sourceField: "media", targetField: "image" }, + { + sourceField: "createdAt", + targetField: "dateCreated", + transform: (value: number) => new Date(value).toISOString(), + }, + ]); + + // Register mappings for Platform B (Facebook-like) + adapter.registerMapping("platformB", [ + { sourceField: "content", targetField: "text" }, + { sourceField: "likes", targetField: "userLikes" }, + { sourceField: "responses", targetField: "interactions" }, + { sourceField: "attachment", targetField: "image" }, + { + sourceField: "postedAt", + targetField: "dateCreated", + transform: (value: string) => new Date(value).getTime(), + }, + ]); + + // Create data in Platform A format + const platformAData = { + post: "Cross-platform test post", + reactions: ["user1", "user2"], + comments: ["Great post!", "Thanks for sharing"], + media: "https://example.com/cross-platform.jpg", + createdAt: Date.now(), + }; + + // Convert Platform A data to universal format + const universalData = adapter.toUniversal("platformA", platformAData); + + // Store in eVault + const storeMutation = ` mutation StoreMetaEnvelope($input: MetaEnvelopeInput!) { storeMetaEnvelope(input: $input) { metaEnvelope { @@ -144,20 +148,22 @@ describe("eVault Integration", () => { } `; - const storeResult = await queryGraphQL(storeMutation, { - input: { - ontology: "SocialMediaPost", - payload: universalData, - acl: ["*"], - }, - }); - - expect(storeResult.errors).toBeUndefined(); - expect(storeResult.data.storeMetaEnvelope.metaEnvelope.id).toBeDefined(); - const storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id; - - // Retrieve from eVault - const retrieveQuery = ` + const storeResult = await queryGraphQL(storeMutation, { + input: { + ontology: "SocialMediaPost", + payload: universalData, + acl: ["*"], + }, + }); + + expect(storeResult.errors).toBeUndefined(); + expect( + storeResult.data.storeMetaEnvelope.metaEnvelope.id, + ).toBeDefined(); + const storedId = storeResult.data.storeMetaEnvelope.metaEnvelope.id; + + // Retrieve from eVault + const retrieveQuery = ` query GetMetaEnvelope($id: String!) { getMetaEnvelopeById(id: $id) { parsed @@ -165,45 +171,47 @@ describe("eVault Integration", () => { } `; - const retrieveResult = await queryGraphQL(retrieveQuery, { id: storedId }); - expect(retrieveResult.errors).toBeUndefined(); - const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed; + const retrieveResult = await queryGraphQL(retrieveQuery, { + id: storedId, + }); + expect(retrieveResult.errors).toBeUndefined(); + const retrievedData = retrieveResult.data.getMetaEnvelopeById.parsed; + + // Convert to Platform B format + const platformBData = adapter.fromUniversal("platformB", retrievedData); + + // Verify Platform B data structure + expect(platformBData).toEqual({ + content: platformAData.post, + likes: platformAData.reactions, + responses: platformAData.comments, + attachment: platformAData.media, + postedAt: expect.any(Number), // We expect a timestamp + }); + + // Verify data integrity + expect(platformBData.content).toBe(platformAData.post); + expect(platformBData.likes).toEqual(platformAData.reactions); + expect(platformBData.responses).toEqual(platformAData.comments); + expect(platformBData.attachment).toBe(platformAData.media); + }); - // Convert to Platform B format - const platformBData = adapter.fromUniversal("platformB", retrievedData); + it("should search data in eVault", async () => { + // Register mappings for a platform + adapter.registerMapping("twitter", [ + { sourceField: "tweet", targetField: "text" }, + { sourceField: "likes", targetField: "userLikes" }, + ]); - // Verify Platform B data structure - expect(platformBData).toEqual({ - content: platformAData.post, - likes: platformAData.reactions, - responses: platformAData.comments, - attachment: platformAData.media, - postedAt: expect.any(Number), // We expect a timestamp - }); + // Create and store test data + const twitterData = { + tweet: "Searchable content", + likes: ["@user1"], + }; - // Verify data integrity - expect(platformBData.content).toBe(platformAData.post); - expect(platformBData.likes).toEqual(platformAData.reactions); - expect(platformBData.responses).toEqual(platformAData.comments); - expect(platformBData.attachment).toBe(platformAData.media); - }); - - it("should search data in eVault", async () => { - // Register mappings for a platform - adapter.registerMapping("twitter", [ - { sourceField: "tweet", targetField: "text" }, - { sourceField: "likes", targetField: "userLikes" }, - ]); - - // Create and store test data - const twitterData = { - tweet: "Searchable content", - likes: ["@user1"], - }; - - const universalData = adapter.toUniversal("twitter", twitterData); - - const storeMutation = ` + const universalData = adapter.toUniversal("twitter", twitterData); + + const storeMutation = ` mutation Store($input: MetaEnvelopeInput!) { storeMetaEnvelope(input: $input) { metaEnvelope { @@ -213,16 +221,16 @@ describe("eVault Integration", () => { } `; - await queryGraphQL(storeMutation, { - input: { - ontology: "SocialMediaPost", - payload: universalData, - acl: ["*"], - }, - }); + await queryGraphQL(storeMutation, { + input: { + ontology: "SocialMediaPost", + payload: universalData, + acl: ["*"], + }, + }); - // Search in eVault - const searchQuery = ` + // Search in eVault + const searchQuery = ` query Search($ontology: String!, $term: String!) { searchMetaEnvelopes(ontology: $ontology, term: $term) { id @@ -231,15 +239,15 @@ describe("eVault Integration", () => { } `; - const searchResult = await queryGraphQL(searchQuery, { - ontology: "SocialMediaPost", - term: "Searchable", - }); + const searchResult = await queryGraphQL(searchQuery, { + ontology: "SocialMediaPost", + term: "Searchable", + }); - expect(searchResult.errors).toBeUndefined(); - expect(searchResult.data.searchMetaEnvelopes.length).toBeGreaterThan(0); - expect(searchResult.data.searchMetaEnvelopes[0].parsed.text).toBe( - "Searchable content" - ); - }); + expect(searchResult.errors).toBeUndefined(); + expect(searchResult.data.searchMetaEnvelopes.length).toBeGreaterThan(0); + expect(searchResult.data.searchMetaEnvelopes[0].parsed.text).toBe( + "Searchable content", + ); + }); }); diff --git a/infrastructure/web3-adapter/src/adapter.ts b/infrastructure/web3-adapter/src/adapter.ts index 1a12b4ae..3fbb72bc 100644 --- a/infrastructure/web3-adapter/src/adapter.ts +++ b/infrastructure/web3-adapter/src/adapter.ts @@ -1,59 +1,59 @@ export type FieldMapping = { - sourceField: string; - targetField: string; - transform?: (value: any) => any; + sourceField: string; + targetField: string; + transform?: (value: any) => any; }; export class Web3Adapter { - private mappings: Map; + private mappings: Map; - constructor() { - this.mappings = new Map(); - } - - public registerMapping(platform: string, mappings: FieldMapping[]): void { - this.mappings.set(platform, mappings); - } - - public toUniversal( - platform: string, - data: Record - ): Record { - const mappings = this.mappings.get(platform); - if (!mappings) { - throw new Error(`No mappings found for platform: ${platform}`); + constructor() { + this.mappings = new Map(); } - const result: Record = {}; - for (const mapping of mappings) { - if (data[mapping.sourceField] !== undefined) { - const value = mapping.transform - ? mapping.transform(data[mapping.sourceField]) - : data[mapping.sourceField]; - result[mapping.targetField] = value; - } + public registerMapping(platform: string, mappings: FieldMapping[]): void { + this.mappings.set(platform, mappings); } - return result; - } - public fromUniversal( - platform: string, - data: Record - ): Record { - const mappings = this.mappings.get(platform); - if (!mappings) { - throw new Error(`No mappings found for platform: ${platform}`); + public toUniversal( + platform: string, + data: Record, + ): Record { + const mappings = this.mappings.get(platform); + if (!mappings) { + throw new Error(`No mappings found for platform: ${platform}`); + } + + const result: Record = {}; + for (const mapping of mappings) { + if (data[mapping.sourceField] !== undefined) { + const value = mapping.transform + ? mapping.transform(data[mapping.sourceField]) + : data[mapping.sourceField]; + result[mapping.targetField] = value; + } + } + return result; } - const result: Record = {}; - for (const mapping of mappings) { - if (data[mapping.targetField] !== undefined) { - const value = mapping.transform - ? mapping.transform(data[mapping.targetField]) - : data[mapping.targetField]; - result[mapping.sourceField] = value; - } + public fromUniversal( + platform: string, + data: Record, + ): Record { + const mappings = this.mappings.get(platform); + if (!mappings) { + throw new Error(`No mappings found for platform: ${platform}`); + } + + const result: Record = {}; + for (const mapping of mappings) { + if (data[mapping.targetField] !== undefined) { + const value = mapping.transform + ? mapping.transform(data[mapping.targetField]) + : data[mapping.targetField]; + result[mapping.sourceField] = value; + } + } + return result; } - return result; - } } diff --git a/package.json b/package.json index b9f4a9b2..f28d01b6 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,24 @@ { - "name": "prototype", - "private": true, - "scripts": { - "build": "turbo run build", - "dev": "turbo run dev", - "lint": "turbo run lint", - "check-lint": "turbo run check-lint", - "format": "turbo run format", - "check-format": "turbo run check-format", - "check": "turbo run check", - "check-types": "turbo run check-types", - "dev:evault": "docker compose -f evault.docker-compose.yml up --watch" - }, - "devDependencies": { - "prettier": "^3.5.3", - "turbo": "^2.4.4", - "typescript": "5.8.2" - }, - "packageManager": "pnpm@10.6.5", - "engines": { - "node": ">=18" - } + "name": "prototype", + "private": true, + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev", + "lint": "turbo run lint", + "check-lint": "turbo run check-lint", + "format": "turbo run format", + "check-format": "turbo run check-format", + "check": "turbo run check", + "check-types": "turbo run check-types", + "dev:evault": "docker compose -f evault.docker-compose.yml up --watch" + }, + "devDependencies": { + "turbo": "^2.4.4", + "typescript": "5.8.2", + "@biomejs/biome": "^1.9.4" + }, + "packageManager": "pnpm@10.6.5", + "engines": { + "node": ">=18" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34485d2e..97ddc386 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: .: devDependencies: - prettier: - specifier: ^3.5.3 - version: 3.5.3 + '@biomejs/biome': + specifier: ^1.9.4 + version: 1.9.4 turbo: specifier: ^2.4.4 version: 2.4.4 @@ -20,9 +20,6 @@ importers: infrastructure/eid-wallet: dependencies: - '@biomejs/biome': - specifier: ^1.9.4 - version: 1.9.4 '@hugeicons/core-free-icons': specifier: ^1.0.13 version: 1.0.13 @@ -35,9 +32,18 @@ importers: '@tauri-apps/api': specifier: ^2 version: 2.3.0 + '@tauri-apps/plugin-barcode-scanner': + specifier: ^2.2.0 + version: 2.2.0 + '@tauri-apps/plugin-biometric': + specifier: ^2.2.0 + version: 2.2.0 '@tauri-apps/plugin-opener': specifier: ^2 version: 2.2.6 + '@tauri-apps/plugin-store': + specifier: ^2.2.0 + version: 2.2.0 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -48,6 +54,9 @@ importers: specifier: ^3.0.2 version: 3.0.2 devDependencies: + '@biomejs/biome': + specifier: ^1.9.4 + version: 1.9.4 '@chromatic-com/storybook': specifier: ^3 version: 3.2.6(react@19.0.0)(storybook@8.6.7(prettier@3.5.3)) @@ -267,6 +276,9 @@ importers: specifier: ^11.1.0 version: 11.1.0 devDependencies: + '@biomejs/biome': + specifier: ^1.9.4 + version: 1.9.4 '@ngneat/falso': specifier: ^7.3.0 version: 7.3.0 @@ -1151,8 +1163,8 @@ packages: resolution: {integrity: sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==} engines: {node: '>=12.10.0'} - '@grpc/proto-loader@0.7.14': - resolution: {integrity: sha512-oS0FyK8eGNBJC6aB/qsS4LOxCYQlBniNzp6W8IdjlRVRGs0FOK9dS84OV+kXGaZf8Ozeos8fbUMJUGGzSpOCzQ==} + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} engines: {node: '>=6'} hasBin: true @@ -2033,9 +2045,18 @@ packages: engines: {node: '>= 10'} hasBin: true + '@tauri-apps/plugin-barcode-scanner@2.2.0': + resolution: {integrity: sha512-16AgAjNZGS790KXTW2oq7K0YKUCxLmlDlizN7+pBvIcZzdR3kYzHSST/CYSXkAkYRParyHmE2nMsMvqJFAHKbA==} + + '@tauri-apps/plugin-biometric@2.2.0': + resolution: {integrity: sha512-bxetRUGNGIuH/zMMlZ1VuYG7B0pWA/GkNDyMKmajCC3T3AIgL+siyeorDw+lAcp9KDNnTZ8vSOlxiAcwdbWN4Q==} + '@tauri-apps/plugin-opener@2.2.6': resolution: {integrity: sha512-bSdkuP71ZQRepPOn8BOEdBKYJQvl6+jb160QtJX/i2H9BF6ZySY/kYljh76N2Ne5fJMQRge7rlKoStYQY5Jq1w==} + '@tauri-apps/plugin-store@2.2.0': + resolution: {integrity: sha512-hJTRtuJis4w5fW1dkcgftsYxKXK0+DbAqurZ3CURHG5WkAyyZgbxpeYctw12bbzF9ZbZREXZklPq8mocCC3Sgg==} + '@testcontainers/neo4j@10.24.2': resolution: {integrity: sha512-Bdhg0HPJYpvnp0PxqubbAITrf87pI5I3KxDAhjMzGdELFHgZltRK4NUNRaHs6R3q0V4vnyHD4Hooci9XA2tVgQ==} @@ -3128,8 +3149,8 @@ packages: resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} engines: {node: '>= 8.0'} - dockerode@4.0.5: - resolution: {integrity: sha512-ZPmKSr1k1571Mrh7oIBS/j0AqAccoecY2yH420ni5j1KyNMgnoTh4Nu4FWunh0HZIJmRSmSysJjBIpa/zyWUEA==} + dockerode@4.0.6: + resolution: {integrity: sha512-FbVf3Z8fY/kALB9s+P9epCpWhfi/r0N2DgYYcYpsAUlaTxPjdsitsFobnltb+lyCgAIvf9C+4PSWlTnHlJMf1w==} engines: {node: '>= 8.0'} doctrine@2.1.0: @@ -6907,10 +6928,10 @@ snapshots: '@grpc/grpc-js@1.13.3': dependencies: - '@grpc/proto-loader': 0.7.14 + '@grpc/proto-loader': 0.7.15 '@js-sdsl/ordered-map': 4.4.2 - '@grpc/proto-loader@0.7.14': + '@grpc/proto-loader@0.7.15': dependencies: lodash.camelcase: 4.3.0 long: 5.3.2 @@ -7908,10 +7929,22 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 2.3.1 '@tauri-apps/cli-win32-x64-msvc': 2.3.1 + '@tauri-apps/plugin-barcode-scanner@2.2.0': + dependencies: + '@tauri-apps/api': 2.3.0 + + '@tauri-apps/plugin-biometric@2.2.0': + dependencies: + '@tauri-apps/api': 2.3.0 + '@tauri-apps/plugin-opener@2.2.6': dependencies: '@tauri-apps/api': 2.3.0 + '@tauri-apps/plugin-store@2.2.0': + dependencies: + '@tauri-apps/api': 2.3.0 + '@testcontainers/neo4j@10.24.2': dependencies: testcontainers: 10.24.2 @@ -7922,7 +7955,7 @@ snapshots: '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.26.2 - '@babel/runtime': 7.27.0 + '@babel/runtime': 7.26.10 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -9309,11 +9342,11 @@ snapshots: transitivePeerDependencies: - supports-color - dockerode@4.0.5: + dockerode@4.0.6: dependencies: '@balena/dockerignore': 1.0.2 '@grpc/grpc-js': 1.13.3 - '@grpc/proto-loader': 0.7.14 + '@grpc/proto-loader': 0.7.15 docker-modem: 5.0.6 protobufjs: 7.5.0 tar-fs: 2.1.2 @@ -9679,7 +9712,7 @@ snapshots: eslint-scope: 7.2.2 eslint-utils: 3.0.0(eslint@8.4.1) eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + espree: 9.2.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -11607,7 +11640,8 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.5.3: {} + prettier@3.5.3: + optional: true pretty-format@27.5.1: dependencies: @@ -12380,7 +12414,7 @@ snapshots: byline: 5.0.0 debug: 4.4.0(supports-color@5.5.0) docker-compose: 0.24.8 - dockerode: 4.0.5 + dockerode: 4.0.6 get-port: 7.1.0 proper-lockfile: 4.1.2 properties-reader: 2.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 47a3d605..74f12989 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,7 +4,10 @@ packages: - platforms/* - infrastructure/* onlyBuiltDependencies: - - "@biomejs/biome" + - '@biomejs/biome' + - cpu-features - esbuild - msw + - protobufjs + - ssh2 - svelte-preprocess