From 20fa5c5adc00992758d434dc4e234e1d38b92751 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Sat, 27 Sep 2025 13:59:47 -0700 Subject: [PATCH] [SELF-700] feat: add mock document generator demo (#995) * feat: add mock document generator demo * feat: add mock document generator * fixes * chore: refresh workflow cache * update lock * build * updates * more fixes * code rabbit feedback * compiles * save wip * updates * merge with dev and fixes * fix: align hoisting and demo Jest resolver (#1003) * chore(app): map common src paths and declare svg flag module * ci fix * update lock * save wip * chore: address yarn lock issues (#1004) * address yarn lock issues * fix postinstall * fix ci * use metro js proxy * android build working for /app * save wip * fix merge * pipeline fixes * format * fix pipelines * bump limit and split * fix pipeline issues * chore: decouple demo app build (#1013) * chore: decouple demo app build * chore: move demo app to workspace * chore: unpublish demo workspace * fix mobile sdk tests * updates * remove polyfills * update merge * update resolutions * update resolutions * fix merge * fix paths * save wip * save wip fixes rd2 * working android * update lock * save wip ios building * fix merge * readd public key * fixes * ci fixes * fixes * fix web building * fix ci * fix tests * update lock * fix ci rd2 * formatting and fix ci * fix * finalize ci fixes * fix tests and metro config paths for building * save wip * install missing package for pipeline * fix wip app building * wip react config * save working emulator compile * first round of pr fixes and feedback * clean up demo app artifacts from sdk * Add Gradle wrapper files for mobile-sdk-demo Android build - Added gradlew, gradlew.bat, and gradle/wrapper/ directory - Updated .gitignore to allow committing Gradle wrapper files - Fixes Android build error: spawn ./gradlew ENOENT * codex feedback and fixes * fix tests * file renames * revert back to dev * add types * coderabbit fixes * fix tests * fix tests * fix test * fixes * fix wip coderabbit issues * coderabbit suggestions rd 2 * fix ci pipelines and addresss warnings * cr fixes * convert kebab to camelCase * save wip fixes * update reinstall and lock files * fixes * remove file * fix lint * fix polyfill fallback issues * ensure that mock document is not on ofac list * prettier --- .cursorignore | 1 - .github/workflows/mobile-e2e.yml | 4 +- .github/workflows/mobile-sdk-demo-ci.yml | 11 +- .gitignore | 1 + .yarnrc.yml | 1 + app/Gemfile.lock | 10 +- app/declarations.d.ts | 2 + app/ios/Podfile | 23 +- app/ios/Podfile.lock | 365 +++++++------ app/ios/Self.xcodeproj/project.pbxproj | 74 +-- app/jest.config.cjs | 8 +- app/jest.setup.js | 299 ++++++++++ app/metro.config.cjs | 474 +++++++++------- app/package.json | 22 +- ...imports.test.cjs => aliasImports.test.cjs} | 0 ...e-ci.test.cjs => bundleAnalyzeCi.test.cjs} | 0 ...uild.test.cjs => cleanupIosBuild.test.cjs} | 0 ....test.cjs => mobileDeployConfirm.test.cjs} | 0 ...cjs => mobileDeployConfirmModule.test.cjs} | 0 ...-shaking.test.cjs => treeShaking.test.cjs} | 0 app/src/components/homeScreen/idCard.tsx | 9 +- app/src/hooks/useMockDataForm.ts | 4 +- app/src/mocks/nfcScanner.ts | 14 + app/src/providers/passportDataProvider.tsx | 6 +- .../document/DocumentNFCScanScreen.tsx | 4 +- .../document/DocumentNFCScanScreen.web.tsx | 4 +- .../document/aadhaar/AadhaarUploadScreen.tsx | 2 +- app/src/screens/home/HomeScreen.tsx | 1 - app/src/screens/home/IdDetailsScreen.tsx | 1 - .../screens/home/ProofHistoryDetailScreen.tsx | 4 +- app/src/screens/home/ProofHistoryList.tsx | 4 +- app/src/screens/home/ProofHistoryScreen.tsx | 4 +- .../prove/ProofRequestStatusScreen.tsx | 2 +- app/src/screens/prove/ProveScreen.tsx | 2 +- .../screens/prove/QRCodeViewFinderScreen.tsx | 4 - .../recovery/AccountRecoveryChoiceScreen.tsx | 9 +- .../recovery/RecoverWithPhraseScreen.tsx | 6 +- app/src/screens/system/Loading.tsx | 4 +- app/src/stores/database.ts | 8 +- app/src/stores/database.web.ts | 8 +- app/src/stores/proofHistoryStore.ts | 4 +- .../stores/{proof-types.ts => proofTypes.ts} | 0 ...ry-iso-3-to-2.d.ts => countryIso3To2.d.ts} | 0 .../src/types/elliptic.d.ts | 6 +- app/src/types/png.d.ts | 6 +- ...er.d.ts => reactNativePassportReader.d.ts} | 0 app/src/types/svg.d.ts | 4 +- app/src/utils/proving/provingMachine.ts | 22 +- app/tests/src/stores/database.test.ts | 2 +- .../src/stores/proofHistoryStore.test.ts | 2 +- .../provingMachine.startFetchingData.test.ts | 48 +- .../utils/proving/provingMachine.test.ts | 24 +- app/tsconfig.json | 12 +- app/vite.config.ts | 26 +- app/web/index.html | 16 + .../babel.config.js => babel.config.js | 0 common/index.ts | 18 + common/package.json | 1 + common/src/polyfills/crypto.ts | 177 ++++++ common/src/types/globals.d.ts | 13 + common/src/types/index.ts | 8 +- common/src/utils/bytes.ts | 2 +- .../src/utils/certificate_parsing/curves.ts | 2 +- common/src/utils/index.ts | 25 +- common/tests/cryptoHash.test.ts | 56 ++ common/tests/cryptoHmac.test.ts | 86 +++ package.json | 40 +- packages/mobile-sdk-alpha/.eslintignore | 1 - packages/mobile-sdk-alpha/.eslintrc.cjs | 9 +- packages/mobile-sdk-alpha/.prettierignore | 2 - packages/mobile-sdk-alpha/README.md | 19 - packages/mobile-sdk-alpha/demo-app/App.tsx | 37 -- .../demo-app/android/app/build.gradle | 119 ---- .../main/java/com/selfdemoapp/MainActivity.kt | 28 - .../java/com/selfdemoapp/MainApplication.kt | 39 -- .../demo-app/android/build.gradle | 21 - .../demo-app/android/settings.gradle | 5 - .../demo-app/metro.config.cjs | 30 - .../mobile-sdk-alpha/demo-app/package.json | 37 -- packages/mobile-sdk-alpha/package.json | 47 +- packages/mobile-sdk-alpha/src/browser.ts | 2 +- packages/mobile-sdk-alpha/src/client.ts | 2 +- .../components/screens/NFCScannerScreen.tsx | 45 +- .../screens/PassportCameraScreen.tsx | 46 +- .../src/components/screens/QRCodeScreen.tsx | 61 ++- .../mobile-sdk-alpha/src/documents/utils.ts | 16 +- packages/mobile-sdk-alpha/src/index.ts | 7 +- .../mobile-sdk-alpha/src/mock/generator.ts | 5 +- ...-inputs.ts => generateDisclosureInputs.ts} | 5 +- .../src/stores/protocolStore.ts | 6 +- .../src/stores/selfAppStore.tsx | 4 +- packages/mobile-sdk-alpha/src/types/events.ts | 2 +- packages/mobile-sdk-alpha/src/types/public.ts | 2 +- .../src/validation/document.ts | 5 +- .../{client-mrz.test.ts => clientMrz.test.ts} | 0 .../tests/documents/utils.test.ts | 3 +- .../mobile-sdk-alpha/tests/entry.test.tsx | 1 - .../tests/mock/generator.test.ts | 27 +- ...st.ts => generateDisclosureInputs.test.ts} | 2 +- .../tests/validation/document.test.ts | 4 +- packages/mobile-sdk-alpha/tsconfig.json | 13 +- packages/mobile-sdk-alpha/tsup.config.ts | 38 +- packages/mobile-sdk-alpha/vitest.config.ts | 9 +- packages/mobile-sdk-demo/.eslintrc.cjs | 38 ++ .../demo-app => mobile-sdk-demo}/.gitignore | 3 - packages/mobile-sdk-demo/.prettierrc | 20 + packages/mobile-sdk-demo/App.tsx | 203 +++++++ packages/mobile-sdk-demo/README.md | 53 ++ .../__tests__/App.test.tsx | 17 +- .../__tests__/cryptoPolyfills.test.ts | 2 +- .../mobile-sdk-demo/android/app/build.gradle | 53 ++ .../android/app/debug.keystore | Bin .../android/app/proguard-rules.pro | 0 .../android/app/src/debug/AndroidManifest.xml | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../java/com/selfxyz/demoapp/MainActivity.kt | 24 + .../com/selfxyz/demoapp/MainApplication.kt | 46 ++ .../res/drawable/rn_edit_text_material.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../app/src/main/res/values/strings.xml | 0 .../app/src/main/res/values/styles.xml | 0 packages/mobile-sdk-demo/android/build.gradle | 40 ++ .../android/gradle.properties | 3 + .../android/gradle/libs.versions.toml | 20 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43583 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + packages/mobile-sdk-demo/android/gradlew | 252 +++++++++ packages/mobile-sdk-demo/android/gradlew.bat | 99 ++++ .../mobile-sdk-demo/android/settings.gradle | 8 + .../demo-app => mobile-sdk-demo}/app.json | 0 .../babel.config.js} | 7 +- packages/mobile-sdk-demo/crypto-polyfill.js | 56 ++ .../demo-app => mobile-sdk-demo}/index.js | 0 .../ios/.xcode.env | 0 .../demo-app => mobile-sdk-demo}/ios/Podfile | 8 +- .../ios/Podfile.lock | 64 ++- .../ios/SelfDemoApp.xcodeproj/project.pbxproj | 0 .../xcschemes/SelfDemoApp.xcscheme | 0 .../xcschemes/TempProject.xcscheme | 0 .../contents.xcworkspacedata | 0 .../ios/SelfDemoApp/AppDelegate.swift | 7 +- .../AppIcon.appiconset/Contents.json | 0 .../SelfDemoApp/Images.xcassets/Contents.json | 0 .../ios/SelfDemoApp/Info-Debug.plist | 0 .../ios/SelfDemoApp/Info.plist | 0 .../ios/SelfDemoApp/LaunchScreen.storyboard | 0 .../ios/SelfDemoApp/PrivacyInfo.xcprivacy | 9 +- .../jest.config.cjs | 6 + packages/mobile-sdk-demo/jest.setup.js | 163 ++++++ packages/mobile-sdk-demo/metro.config.cjs | 94 ++++ packages/mobile-sdk-demo/package.json | 66 +++ .../scripts/bundle-analyze-ci.cjs | 98 ++++ .../mobile-sdk-demo/src/DocumentCamera.tsx | 81 +++ .../mobile-sdk-demo/src/DocumentNFCScan.tsx | 82 +++ .../src/DocumentOnboarding.tsx | 82 +++ packages/mobile-sdk-demo/src/GenerateMock.tsx | 163 ++++++ packages/mobile-sdk-demo/src/ProveQRCode.tsx | 114 ++++ .../mobile-sdk-demo/src/QRCodeViewFinder.tsx | 82 +++ .../mobile-sdk-demo/src/RegisterDocument.tsx | 114 ++++ .../src/polyfills/cryptoPolyfill.js | 13 + .../src/utils/ethers.ts | 1 + .../tests/cryptoPolyfill.test.js | 251 +++++++++ .../tsconfig.json | 2 +- .../types/reactNativePickerPicker.d.ts} | 5 +- scripts/check-duplicate-headers.cjs | 29 +- scripts/check-license-headers.mjs | 21 +- scripts/lint-headers.cjs | 37 ++ ....test.mjs => checkLicenseHeaders.test.mjs} | 0 ...test.mjs => checkPackageVersions.test.mjs} | 0 sdk/core/package.json | 2 +- ...-errors.test.ts => verifierErrors.test.ts} | 0 sdk/qrcode-angular/package.json | 18 +- sdk/qrcode/package.json | 2 +- yarn.lock | 511 +++++------------- 183 files changed, 4316 insertions(+), 1454 deletions(-) rename app/scripts/tests/{alias-imports.test.cjs => aliasImports.test.cjs} (100%) rename app/scripts/tests/{bundle-analyze-ci.test.cjs => bundleAnalyzeCi.test.cjs} (100%) rename app/scripts/tests/{cleanup-ios-build.test.cjs => cleanupIosBuild.test.cjs} (100%) rename app/scripts/tests/{mobile-deploy-confirm.test.cjs => mobileDeployConfirm.test.cjs} (100%) rename app/scripts/tests/{mobile-deploy-confirm-module.test.cjs => mobileDeployConfirmModule.test.cjs} (100%) rename app/scripts/tests/{tree-shaking.test.cjs => treeShaking.test.cjs} (100%) create mode 100644 app/src/mocks/nfcScanner.ts rename app/src/stores/{proof-types.ts => proofTypes.ts} (100%) rename app/src/types/{country-iso-3-to-2.d.ts => countryIso3To2.d.ts} (100%) rename packages/mobile-sdk-alpha/demo-app/src/ProveQRCode.tsx => app/src/types/elliptic.d.ts (63%) rename app/src/types/{react-native-passport-reader.d.ts => reactNativePassportReader.d.ts} (100%) rename packages/mobile-sdk-alpha/demo-app/babel.config.js => babel.config.js (100%) create mode 100644 common/src/polyfills/crypto.ts create mode 100644 common/src/types/globals.d.ts create mode 100644 common/tests/cryptoHash.test.ts create mode 100644 common/tests/cryptoHmac.test.ts delete mode 100644 packages/mobile-sdk-alpha/demo-app/App.tsx delete mode 100644 packages/mobile-sdk-alpha/demo-app/android/app/build.gradle delete mode 100644 packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainActivity.kt delete mode 100644 packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainApplication.kt delete mode 100644 packages/mobile-sdk-alpha/demo-app/android/build.gradle delete mode 100644 packages/mobile-sdk-alpha/demo-app/android/settings.gradle delete mode 100644 packages/mobile-sdk-alpha/demo-app/metro.config.cjs delete mode 100644 packages/mobile-sdk-alpha/demo-app/package.json rename packages/mobile-sdk-alpha/src/processing/{generate-disclosure-inputs.ts => generateDisclosureInputs.ts} (86%) rename packages/mobile-sdk-alpha/tests/{client-mrz.test.ts => clientMrz.test.ts} (100%) rename packages/mobile-sdk-alpha/tests/processing/{generate-disclosure-inputs.test.ts => generateDisclosureInputs.test.ts} (99%) create mode 100644 packages/mobile-sdk-demo/.eslintrc.cjs rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/.gitignore (91%) create mode 100644 packages/mobile-sdk-demo/.prettierrc create mode 100644 packages/mobile-sdk-demo/App.tsx create mode 100644 packages/mobile-sdk-demo/README.md rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/__tests__/App.test.tsx (50%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/__tests__/cryptoPolyfills.test.ts (99%) create mode 100644 packages/mobile-sdk-demo/android/app/build.gradle rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/debug.keystore (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/proguard-rules.pro (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/debug/AndroidManifest.xml (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/AndroidManifest.xml (100%) create mode 100644 packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainActivity.kt create mode 100644 packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainApplication.kt rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/drawable/rn_edit_text_material.xml (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/values/strings.xml (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/app/src/main/res/values/styles.xml (100%) create mode 100644 packages/mobile-sdk-demo/android/build.gradle rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/android/gradle.properties (95%) create mode 100644 packages/mobile-sdk-demo/android/gradle/libs.versions.toml create mode 100644 packages/mobile-sdk-demo/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 packages/mobile-sdk-demo/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 packages/mobile-sdk-demo/android/gradlew create mode 100644 packages/mobile-sdk-demo/android/gradlew.bat create mode 100644 packages/mobile-sdk-demo/android/settings.gradle rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/app.json (100%) rename packages/{mobile-sdk-alpha/demo-app/src/GenerateMock.tsx => mobile-sdk-demo/babel.config.js} (62%) create mode 100644 packages/mobile-sdk-demo/crypto-polyfill.js rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/index.js (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/.xcode.env (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/Podfile (80%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/Podfile.lock (95%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp.xcodeproj/project.pbxproj (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/SelfDemoApp.xcscheme (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/TempProject.xcscheme (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp.xcworkspace/contents.xcworkspacedata (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp/AppDelegate.swift (88%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp/Images.xcassets/AppIcon.appiconset/Contents.json (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp/Images.xcassets/Contents.json (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp/Info-Debug.plist (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp/Info.plist (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp/LaunchScreen.storyboard (100%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/ios/SelfDemoApp/PrivacyInfo.xcprivacy (97%) rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/jest.config.cjs (53%) create mode 100644 packages/mobile-sdk-demo/jest.setup.js create mode 100644 packages/mobile-sdk-demo/metro.config.cjs create mode 100644 packages/mobile-sdk-demo/package.json create mode 100644 packages/mobile-sdk-demo/scripts/bundle-analyze-ci.cjs create mode 100644 packages/mobile-sdk-demo/src/DocumentCamera.tsx create mode 100644 packages/mobile-sdk-demo/src/DocumentNFCScan.tsx create mode 100644 packages/mobile-sdk-demo/src/DocumentOnboarding.tsx create mode 100644 packages/mobile-sdk-demo/src/GenerateMock.tsx create mode 100644 packages/mobile-sdk-demo/src/ProveQRCode.tsx create mode 100644 packages/mobile-sdk-demo/src/QRCodeViewFinder.tsx create mode 100644 packages/mobile-sdk-demo/src/RegisterDocument.tsx create mode 100644 packages/mobile-sdk-demo/src/polyfills/cryptoPolyfill.js rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/src/utils/ethers.ts (99%) create mode 100644 packages/mobile-sdk-demo/tests/cryptoPolyfill.test.js rename packages/{mobile-sdk-alpha/demo-app => mobile-sdk-demo}/tsconfig.json (68%) rename packages/{mobile-sdk-alpha/demo-app/src/RegisterDocument.tsx => mobile-sdk-demo/types/reactNativePickerPicker.d.ts} (59%) create mode 100644 scripts/lint-headers.cjs rename scripts/tests/{check-license-headers.test.mjs => checkLicenseHeaders.test.mjs} (100%) rename scripts/tests/{check-package-versions.test.mjs => checkPackageVersions.test.mjs} (100%) rename sdk/core/tests/{verifier-errors.test.ts => verifierErrors.test.ts} (100%) diff --git a/.cursorignore b/.cursorignore index a0bf475fc..fd484e738 100644 --- a/.cursorignore +++ b/.cursorignore @@ -244,7 +244,6 @@ app/src/assets/animations/*.json app/src/assets/fonts/ # Yarn configuration -.yarnrc.yml **/.yarn/ # Certificate generation config diff --git a/.github/workflows/mobile-e2e.yml b/.github/workflows/mobile-e2e.yml index a976ff56a..2bdb5de48 100644 --- a/.github/workflows/mobile-e2e.yml +++ b/.github/workflows/mobile-e2e.yml @@ -107,7 +107,7 @@ jobs: - name: Build dependencies (outside emulator) run: | echo "Building dependencies..." - yarn workspace @selfxyz/mobile-app run build:deps --silent || { echo "❌ Dependency build failed"; exit 1; } + yarn workspace @selfxyz/mobile-app run build:deps || { echo "❌ Dependency build failed"; exit 1; } echo "✅ Dependencies built successfully" - name: Clone android-passport-reader uses: ./.github/actions/clone-android-passport-reader @@ -293,7 +293,7 @@ jobs: - name: Build dependencies (outside main flow) run: | echo "Building dependencies..." - yarn workspace @selfxyz/mobile-app run build:deps --silent || { echo "❌ Dependency build failed"; exit 1; } + yarn workspace @selfxyz/mobile-app run build:deps || { echo "❌ Dependency build failed"; exit 1; } echo "✅ Dependencies built successfully" - name: Install iOS dependencies run: | diff --git a/.github/workflows/mobile-sdk-demo-ci.yml b/.github/workflows/mobile-sdk-demo-ci.yml index c53e6da9a..601622861 100644 --- a/.github/workflows/mobile-sdk-demo-ci.yml +++ b/.github/workflows/mobile-sdk-demo-ci.yml @@ -3,7 +3,7 @@ name: Mobile SDK Demo CI on: pull_request: paths: - - "packages/mobile-sdk-alpha/demo-app/**" + - "packages/mobile-sdk-demo/**" - ".github/workflows/mobile-sdk-demo-ci.yml" - ".github/actions/**" @@ -18,9 +18,14 @@ jobs: node-version-file: .nvmrc - name: Install Dependencies uses: ./.github/actions/yarn-install + - name: Build dependencies (topological) + shell: bash + run: | + # Build demo app and all its transitive workspace deps in the correct order + yarn workspaces foreach -R -t --from mobile-sdk-demo run build - name: Run demo app tests run: | - yarn workspace demo-app test + yarn workspace mobile-sdk-demo test - name: Build demo app run: | - yarn workspace demo-app build + yarn build:demo diff --git a/.gitignore b/.gitignore index e41461e97..77a53455a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ showcase output/* *.tsbuildinfo .yarnrc.yml +.giga/tasks/* package-lock.json # CI-generated tarballs (don't commit these!) diff --git a/.yarnrc.yml b/.yarnrc.yml index 334e9d608..8ca323607 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,4 +1,5 @@ nodeLinker: node-modules +nmHoistingLimits: workspaces enableGlobalCache: true enableScripts: true checksumBehavior: "update" diff --git a/app/Gemfile.lock b/app/Gemfile.lock index 511c88750..f85fa2570 100644 --- a/app/Gemfile.lock +++ b/app/Gemfile.lock @@ -25,8 +25,8 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.4.0) - aws-partitions (1.1161.0) - aws-sdk-core (3.232.0) + aws-partitions (1.1166.0) + aws-sdk-core (3.233.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -34,10 +34,10 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.112.0) + aws-sdk-kms (1.113.0) aws-sdk-core (~> 3, >= 3.231.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.199.0) + aws-sdk-s3 (1.199.1) aws-sdk-core (~> 3, >= 3.231.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -225,7 +225,7 @@ GEM i18n (1.14.7) concurrent-ruby (~> 1.0) jmespath (1.6.2) - json (2.13.2) + json (2.15.0) jwt (2.10.2) base64 logger (1.7.0) diff --git a/app/declarations.d.ts b/app/declarations.d.ts index c5f8149be..c62b3c171 100644 --- a/app/declarations.d.ts +++ b/app/declarations.d.ts @@ -19,3 +19,5 @@ declare module '*.svg' { const content: React.FC; export default content; } + +declare module 'react-native-svg-circle-country-flags'; diff --git a/app/ios/Podfile b/app/ios/Podfile index f859c85c2..8aa6f7023 100755 --- a/app/ios/Podfile +++ b/app/ios/Podfile @@ -27,16 +27,16 @@ if linkage != nil end def using_https_git_auth? - begin - # backticks run command in shell and capture stdout, 2>&1 captures stderr as well - auth_data = `gh auth status 2>&1` - auth_data.include?('Logged in to github.com account') && - auth_data.include?('Git operations protocol: https') - rescue => e - puts 'gh auth status failed, assuming no HTTPS auth -- will try SSH' - false - end + begin + # backticks run command in shell and capture stdout, 2>&1 captures stderr as well + auth_data = `gh auth status 2>&1` + auth_data.include?("Logged in to github.com account") && + auth_data.include?("Git operations protocol: https") + rescue => e + puts "gh auth status failed, assuming no HTTPS auth -- will try SSH" + false end +end target "Self" do config = use_native_modules! @@ -72,7 +72,7 @@ target "Self" do pod "lottie-ios" pod "SwiftQRScanner", :git => "https://github.com/vinodiOS/SwiftQRScanner" pod "Mixpanel-swift", "~> 5.0.0" - pod "RNReactNativeHapticFeedback", :path => "../../node_modules/react-native-haptic-feedback", :modular_headers => true + # RNReactNativeHapticFeedback is handled by autolinking use_react_native!( :path => config[:reactNativePath], @@ -85,7 +85,6 @@ target "Self" do pod "Firebase", :modular_headers => true pod "FirebaseCore", :modular_headers => true pod "FirebaseCoreInternal", :modular_headers => true - pod "FirebaseAnalytics", :modular_headers => true pod "GoogleUtilities", :modular_headers => true pod "FirebaseMessaging" @@ -165,7 +164,7 @@ target "Self" do if File.exist?(qkCutoutView) # Ensure the file is writable system("chmod u+w #{qkCutoutView}") - + text = File.read(qkCutoutView) # Only modify if the line exists and is not already commented if text.include?("addBorderAroundCutout()") && !text.include?("// addBorderAroundCutout()") diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 198b79c00..b8e7323ed 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -2125,109 +2125,108 @@ PODS: - Yoga (0.0.0) DEPENDENCIES: - - boost (from `../../node_modules/react-native/third-party-podspecs/boost.podspec`) - - DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - fast_float (from `../../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - - FBLazyVector (from `../../node_modules/react-native/Libraries/FBLazyVector`) + - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`) + - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - Firebase - - FirebaseAnalytics - FirebaseCore - FirebaseCoreInternal - FirebaseMessaging - - fmt (from `../../node_modules/react-native/third-party-podspecs/fmt.podspec`) - - glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`) + - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) + - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - GoogleUtilities - - hermes-engine (from `../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - lottie-ios - - lottie-react-native (from `../../node_modules/lottie-react-native`) + - lottie-react-native (from `../node_modules/lottie-react-native`) - Mixpanel-swift (~> 5.0.0) - - "mobile-sdk-alpha (from `../../node_modules/@selfxyz/mobile-sdk-alpha`)" + - "mobile-sdk-alpha (from `../node_modules/@selfxyz/mobile-sdk-alpha`)" - "NFCPassportReader (from `git@github.com:selfxyz/NFCPassportReader.git`, commit `9eff7c4e3a9037fdc1e03301584e0d5dcf14d76b`)" - QKMRZScanner - - RCT-Folly (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCT-Folly/Fabric (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCTDeprecation (from `../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - - RCTRequired (from `../../node_modules/react-native/Libraries/Required`) - - RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`) - - React (from `../../node_modules/react-native/`) - - React-callinvoker (from `../../node_modules/react-native/ReactCommon/callinvoker`) - - React-Core (from `../../node_modules/react-native/`) - - React-Core/RCTWebSocket (from `../../node_modules/react-native/`) - - React-CoreModules (from `../../node_modules/react-native/React/CoreModules`) - - React-cxxreact (from `../../node_modules/react-native/ReactCommon/cxxreact`) - - React-debug (from `../../node_modules/react-native/ReactCommon/react/debug`) - - React-defaultsnativemodule (from `../../node_modules/react-native/ReactCommon/react/nativemodule/defaults`) - - React-domnativemodule (from `../../node_modules/react-native/ReactCommon/react/nativemodule/dom`) - - React-Fabric (from `../../node_modules/react-native/ReactCommon`) - - React-FabricComponents (from `../../node_modules/react-native/ReactCommon`) - - React-FabricImage (from `../../node_modules/react-native/ReactCommon`) - - React-featureflags (from `../../node_modules/react-native/ReactCommon/react/featureflags`) - - React-featureflagsnativemodule (from `../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`) - - React-graphics (from `../../node_modules/react-native/ReactCommon/react/renderer/graphics`) - - React-hermes (from `../../node_modules/react-native/ReactCommon/hermes`) - - React-idlecallbacksnativemodule (from `../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) - - React-ImageManager (from `../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) - - React-jserrorhandler (from `../../node_modules/react-native/ReactCommon/jserrorhandler`) - - React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`) - - React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`) - - React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector-modern`) - - React-jsitracing (from `../../node_modules/react-native/ReactCommon/hermes/executor/`) - - React-logger (from `../../node_modules/react-native/ReactCommon/logger`) - - React-Mapbuffer (from `../../node_modules/react-native/ReactCommon`) - - React-microtasksnativemodule (from `../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - - react-native-app-auth (from `../../node_modules/react-native-app-auth`) - - react-native-biometrics (from `../../node_modules/react-native-biometrics`) - - "react-native-blur (from `../../node_modules/@react-native-community/blur`)" - - react-native-cloud-storage (from `../../node_modules/react-native-cloud-storage`) - - react-native-get-random-values (from `../../node_modules/react-native-get-random-values`) - - "react-native-netinfo (from `../../node_modules/@react-native-community/netinfo`)" - - react-native-nfc-manager (from `../../node_modules/react-native-nfc-manager`) - - react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`) - - react-native-sqlite-storage (from `../../node_modules/react-native-sqlite-storage`) - - React-nativeconfig (from `../../node_modules/react-native/ReactCommon`) - - React-NativeModulesApple (from `../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - - React-perflogger (from `../../node_modules/react-native/ReactCommon/reactperflogger`) - - React-performancetimeline (from `../../node_modules/react-native/ReactCommon/react/performance/timeline`) - - React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`) - - React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`) - - React-RCTAppDelegate (from `../../node_modules/react-native/Libraries/AppDelegate`) - - React-RCTBlob (from `../../node_modules/react-native/Libraries/Blob`) - - React-RCTFabric (from `../../node_modules/react-native/React`) - - React-RCTImage (from `../../node_modules/react-native/Libraries/Image`) - - React-RCTLinking (from `../../node_modules/react-native/Libraries/LinkingIOS`) - - React-RCTNetwork (from `../../node_modules/react-native/Libraries/Network`) - - React-RCTSettings (from `../../node_modules/react-native/Libraries/Settings`) - - React-RCTText (from `../../node_modules/react-native/Libraries/Text`) - - React-RCTVibration (from `../../node_modules/react-native/Libraries/Vibration`) - - React-rendererconsistency (from `../../node_modules/react-native/ReactCommon/react/renderer/consistency`) - - React-rendererdebug (from `../../node_modules/react-native/ReactCommon/react/renderer/debug`) - - React-rncore (from `../../node_modules/react-native/ReactCommon`) - - React-RuntimeApple (from `../../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) - - React-RuntimeCore (from `../../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimeexecutor (from `../../node_modules/react-native/ReactCommon/runtimeexecutor`) - - React-RuntimeHermes (from `../../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimescheduler (from `../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - - React-timing (from `../../node_modules/react-native/ReactCommon/react/timing`) - - React-utils (from `../../node_modules/react-native/ReactCommon/react/utils`) + - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) + - RCTRequired (from `../node_modules/react-native/Libraries/Required`) + - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Core (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`) + - React-defaultsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/defaults`) + - React-domnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/dom`) + - React-Fabric (from `../node_modules/react-native/ReactCommon`) + - React-FabricComponents (from `../node_modules/react-native/ReactCommon`) + - React-FabricImage (from `../node_modules/react-native/ReactCommon`) + - React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`) + - React-featureflagsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`) + - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) + - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) + - React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) + - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) + - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) + - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) + - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`) + - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) + - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) + - react-native-app-auth (from `../node_modules/react-native-app-auth`) + - react-native-biometrics (from `../node_modules/react-native-biometrics`) + - "react-native-blur (from `../node_modules/@react-native-community/blur`)" + - react-native-cloud-storage (from `../node_modules/react-native-cloud-storage`) + - react-native-get-random-values (from `../node_modules/react-native-get-random-values`) + - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" + - react-native-nfc-manager (from `../node_modules/react-native-nfc-manager`) + - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) + - react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`) + - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) + - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) + - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) + - React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`) + - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) + - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) + - React-RCTFabric (from `../node_modules/react-native/React`) + - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) + - React-rendererconsistency (from `../node_modules/react-native/ReactCommon/react/renderer/consistency`) + - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`) + - React-rncore (from `../node_modules/react-native/ReactCommon`) + - React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) + - React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) + - React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) + - React-timing (from `../node_modules/react-native/ReactCommon/react/timing`) + - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCodegen (from `build/generated/ios`) - - ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`) - - "RNCAsyncStorage (from `../../node_modules/@react-native-async-storage/async-storage`)" - - "RNCClipboard (from `../../node_modules/@react-native-clipboard/clipboard`)" - - RNDeviceInfo (from `../../node_modules/react-native-device-info`) - - "RNFBApp (from `../../node_modules/@react-native-firebase/app`)" - - "RNFBMessaging (from `../../node_modules/@react-native-firebase/messaging`)" - - "RNFBRemoteConfig (from `../../node_modules/@react-native-firebase/remote-config`)" - - RNGestureHandler (from `../../node_modules/react-native-gesture-handler`) - - RNKeychain (from `../../node_modules/react-native-keychain`) - - RNLocalize (from `../../node_modules/react-native-localize`) - - RNReactNativeHapticFeedback (from `../../node_modules/react-native-haptic-feedback`) - - RNScreens (from `../../node_modules/react-native-screens`) - - "RNSentry (from `../../node_modules/@sentry/react-native`)" - - RNSVG (from `../../node_modules/react-native-svg`) - - "segment-analytics-react-native (from `../../node_modules/@segment/analytics-react-native`)" - - "sovran-react-native (from `../../node_modules/@segment/sovran-react-native`)" + - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" + - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" + - RNDeviceInfo (from `../node_modules/react-native-device-info`) + - "RNFBApp (from `../node_modules/@react-native-firebase/app`)" + - "RNFBMessaging (from `../node_modules/@react-native-firebase/messaging`)" + - "RNFBRemoteConfig (from `../node_modules/@react-native-firebase/remote-config`)" + - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) + - RNKeychain (from `../node_modules/react-native-keychain`) + - RNLocalize (from `../node_modules/react-native-localize`) + - RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`) + - RNScreens (from `../node_modules/react-native-screens`) + - "RNSentry (from `../node_modules/@sentry/react-native`)" + - RNSVG (from `../node_modules/react-native-svg`) + - "segment-analytics-react-native (from `../node_modules/@segment/analytics-react-native`)" + - "sovran-react-native (from `../node_modules/@segment/sovran-react-native`)" - SwiftQRScanner (from `https://github.com/vinodiOS/SwiftQRScanner`) - - Yoga (from `../../node_modules/react-native/ReactCommon/yoga`) + - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: trunk: @@ -2259,191 +2258,191 @@ SPEC REPOS: EXTERNAL SOURCES: boost: - :podspec: "../../node_modules/react-native/third-party-podspecs/boost.podspec" + :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" DoubleConversion: - :podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" fast_float: - :podspec: "../../node_modules/react-native/third-party-podspecs/fast_float.podspec" + :podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec" FBLazyVector: - :path: "../../node_modules/react-native/Libraries/FBLazyVector" + :path: "../node_modules/react-native/Libraries/FBLazyVector" fmt: - :podspec: "../../node_modules/react-native/third-party-podspecs/fmt.podspec" + :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" glog: - :podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec" + :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: - :podspec: "../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" + :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" :tag: hermes-2024-11-12-RNv0.76.2-5b4aa20c719830dcf5684832b89a6edb95ac3d64 lottie-react-native: - :path: "../../node_modules/lottie-react-native" + :path: "../node_modules/lottie-react-native" mobile-sdk-alpha: - :path: "../../node_modules/@selfxyz/mobile-sdk-alpha" + :path: "../node_modules/@selfxyz/mobile-sdk-alpha" NFCPassportReader: :commit: 9eff7c4e3a9037fdc1e03301584e0d5dcf14d76b :git: "git@github.com:selfxyz/NFCPassportReader.git" RCT-Folly: - :podspec: "../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: - :path: "../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" + :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" RCTRequired: - :path: "../../node_modules/react-native/Libraries/Required" + :path: "../node_modules/react-native/Libraries/Required" RCTTypeSafety: - :path: "../../node_modules/react-native/Libraries/TypeSafety" + :path: "../node_modules/react-native/Libraries/TypeSafety" React: - :path: "../../node_modules/react-native/" + :path: "../node_modules/react-native/" React-callinvoker: - :path: "../../node_modules/react-native/ReactCommon/callinvoker" + :path: "../node_modules/react-native/ReactCommon/callinvoker" React-Core: - :path: "../../node_modules/react-native/" + :path: "../node_modules/react-native/" React-CoreModules: - :path: "../../node_modules/react-native/React/CoreModules" + :path: "../node_modules/react-native/React/CoreModules" React-cxxreact: - :path: "../../node_modules/react-native/ReactCommon/cxxreact" + :path: "../node_modules/react-native/ReactCommon/cxxreact" React-debug: - :path: "../../node_modules/react-native/ReactCommon/react/debug" + :path: "../node_modules/react-native/ReactCommon/react/debug" React-defaultsnativemodule: - :path: "../../node_modules/react-native/ReactCommon/react/nativemodule/defaults" + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/defaults" React-domnativemodule: - :path: "../../node_modules/react-native/ReactCommon/react/nativemodule/dom" + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/dom" React-Fabric: - :path: "../../node_modules/react-native/ReactCommon" + :path: "../node_modules/react-native/ReactCommon" React-FabricComponents: - :path: "../../node_modules/react-native/ReactCommon" + :path: "../node_modules/react-native/ReactCommon" React-FabricImage: - :path: "../../node_modules/react-native/ReactCommon" + :path: "../node_modules/react-native/ReactCommon" React-featureflags: - :path: "../../node_modules/react-native/ReactCommon/react/featureflags" + :path: "../node_modules/react-native/ReactCommon/react/featureflags" React-featureflagsnativemodule: - :path: "../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags" + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/featureflags" React-graphics: - :path: "../../node_modules/react-native/ReactCommon/react/renderer/graphics" + :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" React-hermes: - :path: "../../node_modules/react-native/ReactCommon/hermes" + :path: "../node_modules/react-native/ReactCommon/hermes" React-idlecallbacksnativemodule: - :path: "../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks" + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks" React-ImageManager: - :path: "../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" + :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" React-jserrorhandler: - :path: "../../node_modules/react-native/ReactCommon/jserrorhandler" + :path: "../node_modules/react-native/ReactCommon/jserrorhandler" React-jsi: - :path: "../../node_modules/react-native/ReactCommon/jsi" + :path: "../node_modules/react-native/ReactCommon/jsi" React-jsiexecutor: - :path: "../../node_modules/react-native/ReactCommon/jsiexecutor" + :path: "../node_modules/react-native/ReactCommon/jsiexecutor" React-jsinspector: - :path: "../../node_modules/react-native/ReactCommon/jsinspector-modern" + :path: "../node_modules/react-native/ReactCommon/jsinspector-modern" React-jsitracing: - :path: "../../node_modules/react-native/ReactCommon/hermes/executor/" + :path: "../node_modules/react-native/ReactCommon/hermes/executor/" React-logger: - :path: "../../node_modules/react-native/ReactCommon/logger" + :path: "../node_modules/react-native/ReactCommon/logger" React-Mapbuffer: - :path: "../../node_modules/react-native/ReactCommon" + :path: "../node_modules/react-native/ReactCommon" React-microtasksnativemodule: - :path: "../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" react-native-app-auth: - :path: "../../node_modules/react-native-app-auth" + :path: "../node_modules/react-native-app-auth" react-native-biometrics: - :path: "../../node_modules/react-native-biometrics" + :path: "../node_modules/react-native-biometrics" react-native-blur: - :path: "../../node_modules/@react-native-community/blur" + :path: "../node_modules/@react-native-community/blur" react-native-cloud-storage: - :path: "../../node_modules/react-native-cloud-storage" + :path: "../node_modules/react-native-cloud-storage" react-native-get-random-values: - :path: "../../node_modules/react-native-get-random-values" + :path: "../node_modules/react-native-get-random-values" react-native-netinfo: - :path: "../../node_modules/@react-native-community/netinfo" + :path: "../node_modules/@react-native-community/netinfo" react-native-nfc-manager: - :path: "../../node_modules/react-native-nfc-manager" + :path: "../node_modules/react-native-nfc-manager" react-native-safe-area-context: - :path: "../../node_modules/react-native-safe-area-context" + :path: "../node_modules/react-native-safe-area-context" react-native-sqlite-storage: - :path: "../../node_modules/react-native-sqlite-storage" + :path: "../node_modules/react-native-sqlite-storage" React-nativeconfig: - :path: "../../node_modules/react-native/ReactCommon" + :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: - :path: "../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" React-perflogger: - :path: "../../node_modules/react-native/ReactCommon/reactperflogger" + :path: "../node_modules/react-native/ReactCommon/reactperflogger" React-performancetimeline: - :path: "../../node_modules/react-native/ReactCommon/react/performance/timeline" + :path: "../node_modules/react-native/ReactCommon/react/performance/timeline" React-RCTActionSheet: - :path: "../../node_modules/react-native/Libraries/ActionSheetIOS" + :path: "../node_modules/react-native/Libraries/ActionSheetIOS" React-RCTAnimation: - :path: "../../node_modules/react-native/Libraries/NativeAnimation" + :path: "../node_modules/react-native/Libraries/NativeAnimation" React-RCTAppDelegate: - :path: "../../node_modules/react-native/Libraries/AppDelegate" + :path: "../node_modules/react-native/Libraries/AppDelegate" React-RCTBlob: - :path: "../../node_modules/react-native/Libraries/Blob" + :path: "../node_modules/react-native/Libraries/Blob" React-RCTFabric: - :path: "../../node_modules/react-native/React" + :path: "../node_modules/react-native/React" React-RCTImage: - :path: "../../node_modules/react-native/Libraries/Image" + :path: "../node_modules/react-native/Libraries/Image" React-RCTLinking: - :path: "../../node_modules/react-native/Libraries/LinkingIOS" + :path: "../node_modules/react-native/Libraries/LinkingIOS" React-RCTNetwork: - :path: "../../node_modules/react-native/Libraries/Network" + :path: "../node_modules/react-native/Libraries/Network" React-RCTSettings: - :path: "../../node_modules/react-native/Libraries/Settings" + :path: "../node_modules/react-native/Libraries/Settings" React-RCTText: - :path: "../../node_modules/react-native/Libraries/Text" + :path: "../node_modules/react-native/Libraries/Text" React-RCTVibration: - :path: "../../node_modules/react-native/Libraries/Vibration" + :path: "../node_modules/react-native/Libraries/Vibration" React-rendererconsistency: - :path: "../../node_modules/react-native/ReactCommon/react/renderer/consistency" + :path: "../node_modules/react-native/ReactCommon/react/renderer/consistency" React-rendererdebug: - :path: "../../node_modules/react-native/ReactCommon/react/renderer/debug" + :path: "../node_modules/react-native/ReactCommon/react/renderer/debug" React-rncore: - :path: "../../node_modules/react-native/ReactCommon" + :path: "../node_modules/react-native/ReactCommon" React-RuntimeApple: - :path: "../../node_modules/react-native/ReactCommon/react/runtime/platform/ios" + :path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios" React-RuntimeCore: - :path: "../../node_modules/react-native/ReactCommon/react/runtime" + :path: "../node_modules/react-native/ReactCommon/react/runtime" React-runtimeexecutor: - :path: "../../node_modules/react-native/ReactCommon/runtimeexecutor" + :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" React-RuntimeHermes: - :path: "../../node_modules/react-native/ReactCommon/react/runtime" + :path: "../node_modules/react-native/ReactCommon/react/runtime" React-runtimescheduler: - :path: "../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" + :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" React-timing: - :path: "../../node_modules/react-native/ReactCommon/react/timing" + :path: "../node_modules/react-native/ReactCommon/react/timing" React-utils: - :path: "../../node_modules/react-native/ReactCommon/react/utils" + :path: "../node_modules/react-native/ReactCommon/react/utils" ReactCodegen: :path: build/generated/ios ReactCommon: - :path: "../../node_modules/react-native/ReactCommon" + :path: "../node_modules/react-native/ReactCommon" RNCAsyncStorage: - :path: "../../node_modules/@react-native-async-storage/async-storage" + :path: "../node_modules/@react-native-async-storage/async-storage" RNCClipboard: - :path: "../../node_modules/@react-native-clipboard/clipboard" + :path: "../node_modules/@react-native-clipboard/clipboard" RNDeviceInfo: - :path: "../../node_modules/react-native-device-info" + :path: "../node_modules/react-native-device-info" RNFBApp: - :path: "../../node_modules/@react-native-firebase/app" + :path: "../node_modules/@react-native-firebase/app" RNFBMessaging: - :path: "../../node_modules/@react-native-firebase/messaging" + :path: "../node_modules/@react-native-firebase/messaging" RNFBRemoteConfig: - :path: "../../node_modules/@react-native-firebase/remote-config" + :path: "../node_modules/@react-native-firebase/remote-config" RNGestureHandler: - :path: "../../node_modules/react-native-gesture-handler" + :path: "../node_modules/react-native-gesture-handler" RNKeychain: - :path: "../../node_modules/react-native-keychain" + :path: "../node_modules/react-native-keychain" RNLocalize: - :path: "../../node_modules/react-native-localize" + :path: "../node_modules/react-native-localize" RNReactNativeHapticFeedback: - :path: "../../node_modules/react-native-haptic-feedback" + :path: "../node_modules/react-native-haptic-feedback" RNScreens: - :path: "../../node_modules/react-native-screens" + :path: "../node_modules/react-native-screens" RNSentry: - :path: "../../node_modules/@sentry/react-native" + :path: "../node_modules/@sentry/react-native" RNSVG: - :path: "../../node_modules/react-native-svg" + :path: "../node_modules/react-native-svg" segment-analytics-react-native: - :path: "../../node_modules/@segment/analytics-react-native" + :path: "../node_modules/@segment/analytics-react-native" sovran-react-native: - :path: "../../node_modules/@segment/sovran-react-native" + :path: "../node_modules/@segment/sovran-react-native" SwiftQRScanner: :git: https://github.com/vinodiOS/SwiftQRScanner Yoga: - :path: "../../node_modules/react-native/ReactCommon/yoga" + :path: "../node_modules/react-native/ReactCommon/yoga" CHECKOUT OPTIONS: NFCPassportReader: @@ -2549,7 +2548,7 @@ SPEC CHECKSUMS: React-runtimescheduler: a352af9ab3939273ee0e02650cfc1c8ee6e4d0c9 React-timing: a693c531e5627dcc200fc7286cbbebf73d73469d React-utils: 59c5bbbc0e72be22c9d6eceb40afadf9be872819 - ReactCodegen: 1ab18f5b89b9922f298e9216c7347d26ef5b6408 + ReactCodegen: 049be6309e06c1027544819670913680f2029b8e ReactCommon: b2eb96a61b826ff327a773a74357b302cf6da678 RNCAsyncStorage: 0003b916f1a69fe2d20b7910e0d08da3d32c7bd6 RNCClipboard: a4827e134e4774e97fa86f7f986694dd89320f13 @@ -2572,6 +2571,6 @@ SPEC CHECKSUMS: SwiftyTesseract: 1f3d96668ae92dc2208d9842c8a59bea9fad2cbb Yoga: 1259c7a8cbaccf7b4c3ddf8ee36ca11be9dee407 -PODFILE CHECKSUM: 73185dc21f929943e88451fb856faed1b6a0c8fd +PODFILE CHECKSUM: b5f11f935be22fce84c5395aaa203b50427a79aa COCOAPODS: 1.16.2 diff --git a/app/ios/Self.xcodeproj/project.pbxproj b/app/ios/Self.xcodeproj/project.pbxproj index f539ba306..8b14b73cb 100644 --- a/app/ios/Self.xcodeproj/project.pbxproj +++ b/app/ios/Self.xcodeproj/project.pbxproj @@ -24,10 +24,10 @@ 1686F0E02C500FBD00841CDE /* QRScannerBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 1686F0DF2C500FBD00841CDE /* QRScannerBridge.m */; }; 16E6646E2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E6646D2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift */; }; 16E884A52C5BD764003B7125 /* passport.json in Resources */ = {isa = PBXBuildFile; fileRef = 16E884A42C5BD764003B7125 /* passport.json */; }; - 55C24BC923F6E713F2A4F406 /* Pods_Self.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2256518ADB2DF783582D66DF /* Pods_Self.framework */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 905B70052A72767900AFA232 /* PassportReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 905B70042A72767900AFA232 /* PassportReader.swift */; }; 905B70072A72774000AFA232 /* PassportReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 905B70062A72774000AFA232 /* PassportReader.m */; }; + 97E31F23A5A11A2C115FE2BB /* Pods_Self.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0823092D57FC544FD63682A /* Pods_Self.framework */; }; AE6147EC2DC95A8D00445C0F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AE6147EB2DC95A8D00445C0F /* GoogleService-Info.plist */; }; B49D2B112E28AA7900946F64 /* IBMPlexMono-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = B49D2B102E28AA7900946F64 /* IBMPlexMono-Regular.otf */; }; BF1044812DD53540009B3688 /* LiveMRZScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1044802DD53540009B3688 /* LiveMRZScannerView.swift */; }; @@ -65,15 +65,13 @@ 169349842CC694DA00166F21 /* OpenPassportDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = OpenPassportDebug.entitlements; path = OpenPassport/OpenPassportDebug.entitlements; sourceTree = ""; }; 16E6646D2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QKMRZScannerViewRepresentable.swift; sourceTree = ""; }; 16E884A42C5BD764003B7125 /* passport.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = passport.json; sourceTree = ""; }; - 2256518ADB2DF783582D66DF /* Pods_Self.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Self.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 55F05768F4B86745D6908F70 /* Pods-Self.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Self.debug.xcconfig"; path = "Target Support Files/Pods-Self/Pods-Self.debug.xcconfig"; sourceTree = ""; }; 7E5C3CEF7EDA4871B3D0EBE1 /* Advercase-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Advercase-Regular.otf"; path = "../src/assets/fonts/Advercase-Regular.otf"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = OpenPassport/LaunchScreen.storyboard; sourceTree = ""; }; + 823EAA08DBB5F61225E922CA /* Pods-Self.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Self.release.xcconfig"; path = "Target Support Files/Pods-Self/Pods-Self.release.xcconfig"; sourceTree = ""; }; 905B70042A72767900AFA232 /* PassportReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportReader.swift; sourceTree = ""; }; 905B70062A72774000AFA232 /* PassportReader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PassportReader.m; sourceTree = ""; }; 905B70082A729CD400AFA232 /* OpenPassport.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = OpenPassport.entitlements; path = OpenPassport/OpenPassport.entitlements; sourceTree = ""; }; 9BF744D9A73A4BAC96EC569A /* DINOT-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "DINOT-Medium.otf"; path = "../src/assets/fonts/DINOT-Medium.otf"; sourceTree = ""; }; - AC3CAAD43E2B9ECF07DACA0B /* Pods-Self.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Self.release.xcconfig"; path = "Target Support Files/Pods-Self/Pods-Self.release.xcconfig"; sourceTree = ""; }; AE6147EB2DC95A8D00445C0F /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = ""; }; B49D2B102E28AA7900946F64 /* IBMPlexMono-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "IBMPlexMono-Regular.otf"; path = "../src/assets/fonts/IBMPlexMono-Regular.otf"; sourceTree = SOURCE_ROOT; }; BF1044802DD53540009B3688 /* LiveMRZScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveMRZScannerView.swift; sourceTree = ""; }; @@ -82,7 +80,9 @@ BF6F0D542E38ED81008EA85C /* SelfAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfAnalytics.swift; sourceTree = ""; }; BFBA0C762E339D2B00E82A52 /* NativeLoggerBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeLoggerBridge.swift; sourceTree = ""; }; BFBA0C782E33A01F00E82A52 /* NativeLoggerBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NativeLoggerBridge.m; sourceTree = ""; }; + C0823092D57FC544FD63682A /* Pods_Self.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Self.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E56E082698598B41447667BB /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = OpenPassport/PrivacyInfo.xcprivacy; sourceTree = ""; }; + E67B3FF985359E36919C9E20 /* Pods-Self.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Self.debug.xcconfig"; path = "Target Support Files/Pods-Self/Pods-Self.debug.xcconfig"; sourceTree = ""; }; E9F9A99A2D57FE2900E1362E /* PassportOCRViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PassportOCRViewManager.m; sourceTree = ""; }; E9F9A99B2D57FE2900E1362E /* PassportOCRViewManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportOCRViewManager.swift; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -94,7 +94,7 @@ buildActionMask = 2147483647; files = ( 1608339F2D5CF89D00056417 /* AudioToolbox.framework in Frameworks */, - 55C24BC923F6E713F2A4F406 /* Pods_Self.framework in Frameworks */, + 97E31F23A5A11A2C115FE2BB /* Pods_Self.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -146,7 +146,7 @@ children = ( 1608339E2D5CF89D00056417 /* AudioToolbox.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 2256518ADB2DF783582D66DF /* Pods_Self.framework */, + C0823092D57FC544FD63682A /* Pods_Self.framework */, ); name = Frameworks; sourceTree = ""; @@ -194,8 +194,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - 55F05768F4B86745D6908F70 /* Pods-Self.debug.xcconfig */, - AC3CAAD43E2B9ECF07DACA0B /* Pods-Self.release.xcconfig */, + E67B3FF985359E36919C9E20 /* Pods-Self.debug.xcconfig */, + 823EAA08DBB5F61225E922CA /* Pods-Self.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -207,15 +207,15 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Self" */; buildPhases = ( - 2FCD11E7A379F3FBFB096963 /* [CP] Check Pods Manifest.lock */, + 3B4EFEB78A7250232578EEBD /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 7EA8B5D35C013E0BEB505C3F /* [CP] Embed Pods Frameworks */, - 03955A0A8B5264C6E4546CCD /* [CP] Copy Pods Resources */, - 674D4E1D44AD675789D32C9F /* [CP-User] [RNFB] Core Configuration */, + E88323604DFAAA92032925C9 /* [CP] Embed Pods Frameworks */, + 12530D65E915E2C915FB4D42 /* [CP] Copy Pods Resources */, + ED88188A58B7D0764449DC88 /* [CP-User] [RNFB] Core Configuration */, ); buildRules = ( ); @@ -294,7 +294,7 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 03955A0A8B5264C6E4546CCD /* [CP] Copy Pods Resources */ = { + 12530D65E915E2C915FB4D42 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -311,7 +311,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Self/Pods-Self-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 2FCD11E7A379F3FBFB096963 /* [CP] Check Pods Manifest.lock */ = { + 3B4EFEB78A7250232578EEBD /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -333,20 +333,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 674D4E1D44AD675789D32C9F /* [CP-User] [RNFB] Core Configuration */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", - ); - name = "[CP-User] [RNFB] Core Configuration"; - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; - }; - 7EA8B5D35C013E0BEB505C3F /* [CP] Embed Pods Frameworks */ = { + E88323604DFAAA92032925C9 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -363,6 +350,19 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Self/Pods-Self-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + ED88188A58B7D0764449DC88 /* [CP-User] [RNFB] Core Configuration */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + name = "[CP-User] [RNFB] Core Configuration"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + }; FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -420,7 +420,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 55F05768F4B86745D6908F70 /* Pods-Self.debug.xcconfig */; + baseConfigurationReference = E67B3FF985359E36919C9E20 /* Pods-Self.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -563,7 +563,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AC3CAAD43E2B9ECF07DACA0B /* Pods-Self.release.xcconfig */; + baseConfigurationReference = 823EAA08DBB5F61225E922CA /* Pods-Self.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -789,8 +789,11 @@ "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", ); - OTHER_LDFLAGS = "$(inherited) "; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native"; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; USE_HERMES = true; @@ -879,8 +882,11 @@ "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", ); - OTHER_LDFLAGS = "$(inherited) "; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native"; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; USE_HERMES = true; diff --git a/app/jest.config.cjs b/app/jest.config.cjs index 2bfd94433..872a0ae00 100644 --- a/app/jest.config.cjs +++ b/app/jest.config.cjs @@ -22,9 +22,13 @@ module.exports = { '^@selfxyz/mobile-sdk-alpha/(.*)$': '/../packages/mobile-sdk-alpha/dist/cjs/$1.cjs', // Fix snarkjs resolution for @anon-aadhaar/core - '^snarkjs$': '/../node_modules/snarkjs/build/main.cjs', + '^snarkjs$': '/../circuits/node_modules/snarkjs/build/main.cjs', // Fix ffjavascript resolution for snarkjs dependencies - '^ffjavascript$': '/../node_modules/ffjavascript/build/main.cjs', + '^ffjavascript$': + '/../circuits/node_modules/ffjavascript/build/main.cjs', + // Fix @anon-aadhaar/core resolution + '^@anon-aadhaar/core$': + '/../common/node_modules/@anon-aadhaar/core/dist/index.js', }, globals: { 'ts-jest': { diff --git a/app/jest.setup.js b/app/jest.setup.js index 75c606686..36d1c068b 100644 --- a/app/jest.setup.js +++ b/app/jest.setup.js @@ -13,6 +13,305 @@ jest.mock( { virtual: true }, ); +// Mock React Native bridge config for mobile-sdk-alpha components +global.__fbBatchedBridgeConfig = { + messageQueue: { + SPY_MODE: false, + }, + remoteModuleConfig: [], +}; + +// Set up global React Native test environment +global.__DEV__ = true; + +// Mock TurboModuleRegistry to provide required native modules for BOTH main app and mobile-sdk-alpha +jest.mock('react-native/Libraries/TurboModule/TurboModuleRegistry', () => ({ + getEnforcing: jest.fn(name => { + if (name === 'PlatformConstants') { + return { + getConstants: () => ({ + reactNativeVersion: { major: 0, minor: 76, patch: 9 }, + forceTouchAvailable: false, + osVersion: '14.0', + systemName: 'iOS', + interfaceIdiom: 'phone', + Dimensions: { + window: { width: 375, height: 667, scale: 2 }, + screen: { width: 375, height: 667, scale: 2 }, + }, + }), + }; + } + if (name === 'SettingsManager') { + return { + getConstants: () => ({}), + }; + } + if (name === 'DeviceInfo') { + return { + getConstants: () => ({ + Dimensions: { + window: { width: 375, height: 667, scale: 2 }, + screen: { width: 375, height: 667, scale: 2 }, + }, + }), + }; + } + return { + getConstants: () => ({}), + }; + }), + get: jest.fn(() => null), +})); + +// Mock the mobile-sdk-alpha's React Native instance separately +jest.mock( + '../packages/mobile-sdk-alpha/node_modules/react-native/Libraries/TurboModule/TurboModuleRegistry', + () => ({ + getEnforcing: jest.fn(name => { + if (name === 'PlatformConstants') { + return { + getConstants: () => ({ + reactNativeVersion: { major: 0, minor: 76, patch: 9 }, + forceTouchAvailable: false, + osVersion: '14.0', + systemName: 'iOS', + interfaceIdiom: 'phone', + Dimensions: { + window: { width: 375, height: 667, scale: 2 }, + screen: { width: 375, height: 667, scale: 2 }, + }, + }), + }; + } + return { + getConstants: () => ({}), + }; + }), + get: jest.fn(() => null), + }), + { virtual: true }, +); + +// Mock mobile-sdk-alpha's Dimensions module +jest.mock( + '../packages/mobile-sdk-alpha/node_modules/react-native/Libraries/Utilities/Dimensions', + () => ({ + getConstants: jest.fn(() => ({ + window: { width: 375, height: 667, scale: 2 }, + screen: { width: 375, height: 667, scale: 2 }, + })), + set: jest.fn(), + get: jest.fn(() => ({ + window: { width: 375, height: 667, scale: 2 }, + screen: { width: 375, height: 667, scale: 2 }, + })), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }), + { virtual: true }, +); + +// Mock mobile-sdk-alpha's PixelRatio module +jest.mock( + '../packages/mobile-sdk-alpha/node_modules/react-native/Libraries/Utilities/PixelRatio', + () => ({ + get: jest.fn(() => 2), + getFontScale: jest.fn(() => 1), + getPixelSizeForLayoutSize: jest.fn(layoutSize => layoutSize * 2), + roundToNearestPixel: jest.fn(layoutSize => Math.round(layoutSize * 2) / 2), + startDetecting: jest.fn(), + }), + { virtual: true }, +); + +// Mock mobile-sdk-alpha's StyleSheet module directly +jest.mock( + '../packages/mobile-sdk-alpha/node_modules/react-native/Libraries/StyleSheet/StyleSheet', + () => ({ + create: jest.fn(styles => styles), + flatten: jest.fn(style => style), + hairlineWidth: 1, + absoluteFillObject: { + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + }, + roundToNearestPixel: jest.fn(layoutSize => Math.round(layoutSize * 2) / 2), + }), + { virtual: true }, +); + +// Mock NativeDeviceInfo specs for both main app and mobile-sdk-alpha +jest.mock('react-native/src/private/specs/modules/NativeDeviceInfo', () => ({ + getConstants: jest.fn(() => ({ + Dimensions: { + window: { width: 375, height: 667, scale: 2 }, + screen: { width: 375, height: 667, scale: 2 }, + }, + })), +})); + +// Mock react-native-gesture-handler to prevent getConstants errors +jest.mock('react-native-gesture-handler', () => { + const RN = jest.requireActual('react-native'); + return { + ...jest.requireActual('react-native-gesture-handler/jestSetup'), + GestureHandlerRootView: ({ children }) => children, + ScrollView: RN.ScrollView, + TouchableOpacity: RN.TouchableOpacity, + TouchableHighlight: RN.TouchableHighlight, + FlatList: RN.FlatList, + }; +}); + +// Mock NativeEventEmitter to prevent null argument errors +jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter', () => { + return class MockNativeEventEmitter { + constructor(nativeModule) { + // Accept any nativeModule argument (including null/undefined) + this.nativeModule = nativeModule; + } + + addListener = jest.fn(); + removeListener = jest.fn(); + removeAllListeners = jest.fn(); + emit = jest.fn(); + }; +}); + +// Mock problematic mobile-sdk-alpha components that use React Native StyleSheet +jest.mock('@selfxyz/mobile-sdk-alpha', () => ({ + NFCScannerScreen: jest.fn(() => null), + SelfClientProvider: jest.fn(({ children }) => children), + useSelfClient: jest.fn(() => { + // Create a consistent mock instance for memoization testing + if (!global.mockSelfClientInstance) { + global.mockSelfClientInstance = { + // Mock selfClient object with common methods + connect: jest.fn(), + disconnect: jest.fn(), + isConnected: false, + extractMRZInfo: jest.fn(mrzString => { + // Mock extractMRZInfo with realistic behavior + if (!mrzString || typeof mrzString !== 'string') { + throw new Error('Invalid MRZ string provided'); + } + + // Valid MRZ example from the test + if (mrzString.includes('L898902C3')) { + return { + documentNumber: 'L898902C3', + validation: { + overall: true, + }, + // Add other expected MRZ fields + firstName: 'ANNA', + lastName: 'ERIKSSON', + nationality: 'UTO', + dateOfBirth: '740812', + sex: 'F', + expirationDate: '120415', + }; + } + + // For malformed/invalid MRZ strings, throw an error + throw new Error('Invalid MRZ format'); + }), + trackEvent: jest.fn(), + }; + } + return global.mockSelfClientInstance; + }), + createSelfClient: jest.fn(() => ({ + // Mock createSelfClient return value + connect: jest.fn(), + disconnect: jest.fn(), + isConnected: false, + extractMRZInfo: jest.fn(mrzString => { + // Mock extractMRZInfo with realistic behavior + if (!mrzString || typeof mrzString !== 'string') { + throw new Error('Invalid MRZ string provided'); + } + + // Valid MRZ example from the test + if (mrzString.includes('L898902C3')) { + return { + documentNumber: 'L898902C3', + validation: { + overall: true, + }, + // Add other expected MRZ fields + firstName: 'ANNA', + lastName: 'ERIKSSON', + nationality: 'UTO', + dateOfBirth: '740812', + sex: 'F', + expirationDate: '120415', + }; + } + + // For malformed/invalid MRZ strings, throw an error + throw new Error('Invalid MRZ format'); + }), + trackEvent: jest.fn(), + })), + createListenersMap: jest.fn(() => ({ + // Mock createListenersMap return value + map: new Map(), + addListener: jest.fn(), + removeListener: jest.fn(), + })), + isPassportDataValid: jest.fn((data, callbacks) => { + // Mock validation function with realistic behavior + if (!data || !data.passportMetadata) { + // Call appropriate callbacks for missing data + if (callbacks?.onPassportMetadataNull) { + callbacks.onPassportMetadataNull(); + } + return false; + } + // Return true for valid data, false for invalid + return data.valid !== false; + }), + SdkEvents: { + // Mock SDK events object + PROVING_PASSPORT_DATA_NOT_FOUND: 'PROVING_PASSPORT_DATA_NOT_FOUND', + PROVING_STARTED: 'PROVING_STARTED', + PROVING_COMPLETED: 'PROVING_COMPLETED', + PROVING_FAILED: 'PROVING_FAILED', + // Add other events as needed + }, + // Add other components and hooks as needed +})); + +// Mock Sentry to prevent NativeModule.getConstants errors +jest.mock('@sentry/react-native', () => ({ + addBreadcrumb: jest.fn(), + captureException: jest.fn(), + captureFeedback: jest.fn(), + captureMessage: jest.fn(), + setContext: jest.fn(), + setExtra: jest.fn(), + setTag: jest.fn(), + setUser: jest.fn(), + init: jest.fn(), + wrap: jest.fn(component => component), + withScope: jest.fn(callback => { + // Mock scope object + const scope = { + setLevel: jest.fn(), + setTag: jest.fn(), + setExtra: jest.fn(), + setContext: jest.fn(), + setUser: jest.fn(), + }; + callback(scope); + }), +})); + jest.mock('@env', () => ({ ENABLE_DEBUG_LOGS: 'false', MIXPANEL_NFC_PROJECT_TOKEN: 'test-token', diff --git a/app/metro.config.cjs b/app/metro.config.cjs index 9927d8bc4..df687f52a 100644 --- a/app/metro.config.cjs +++ b/app/metro.config.cjs @@ -4,205 +4,34 @@ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); const path = require('node:path'); +const findYarnWorkspaceRoot = require('find-yarn-workspace-root'); const defaultConfig = getDefaultConfig(__dirname); const { assetExts, sourceExts } = defaultConfig.resolver; -const monorepoRoot = path.resolve(__dirname, '../'); -const commonPath = path.join(__dirname, '/../common'); -const sdkAlphaPath = path.join(__dirname, '/../packages/mobile-sdk-alpha'); -const trueMonorepoNodeModules = path.resolve(__dirname, '../node_modules'); -const extraNodeModules = { - stream: require.resolve('stream-browserify'), - buffer: require.resolve('buffer'), - util: require.resolve('util'), - assert: require.resolve('assert'), - '@babel/runtime': path.join(trueMonorepoNodeModules, '@babel/runtime'), - // Pin React and React Native to monorepo root - react: path.join(trueMonorepoNodeModules, 'react'), - 'react-native': path.join(trueMonorepoNodeModules, 'react-native'), - '@': path.join(__dirname, 'src'), - '@selfxyz/common': path.resolve(commonPath, 'dist'), - '@selfxyz/mobile-sdk-alpha': path.resolve(sdkAlphaPath, 'dist'), - '@selfxyz/mobile-sdk-alpha/constants/analytics': path.resolve( - sdkAlphaPath, - 'dist/esm/constants/analytics.js', - ), - '@selfxyz/mobile-sdk-alpha/stores': path.resolve( - sdkAlphaPath, - 'dist/esm/stores.js', - ), - // Main exports - '@selfxyz/common/utils': path.resolve( - commonPath, - 'dist/esm/src/utils/index.js', - ), - '@selfxyz/common/types': path.resolve( - commonPath, - 'dist/esm/src/types/index.js', - ), - '@selfxyz/common/constants': path.resolve( - commonPath, - 'dist/esm/src/constants/index.js', - ), - // Constants subpaths - '@selfxyz/common/constants/countries': path.resolve( - commonPath, - 'dist/esm/src/constants/countries.js', - ), - '@selfxyz/common/constants/vkey': path.resolve( - commonPath, - 'dist/esm/src/constants/vkey.js', - ), - '@selfxyz/common/constants/skiPem': path.resolve( - commonPath, - 'dist/esm/src/constants/skiPem.js', - ), - '@selfxyz/common/constants/mockCerts': path.resolve( - commonPath, - 'dist/esm/src/constants/mockCertificates.js', - ), - '@selfxyz/common/constants/hashes': path.resolve( - commonPath, - 'dist/esm/src/constants/sampleDataHashes.js', - ), - // Utils subpaths - '@selfxyz/common/utils/hash': path.resolve( - commonPath, - 'dist/esm/src/utils/hash.js', - ), - '@selfxyz/common/utils/attest': path.resolve( - commonPath, - 'dist/esm/src/utils/attest.js', - ), - '@selfxyz/common/utils/bytes': path.resolve( - commonPath, - 'dist/esm/src/utils/bytes.js', - ), - '@selfxyz/common/utils/trees': path.resolve( - commonPath, - 'dist/esm/src/utils/trees.js', - ), - '@selfxyz/common/utils/scope': path.resolve( - commonPath, - 'dist/esm/src/utils/scope.js', - ), - '@selfxyz/common/utils/proving': path.resolve( - commonPath, - 'dist/esm/src/utils/proving.js', - ), - '@selfxyz/common/utils/appType': path.resolve( - commonPath, - 'dist/esm/src/utils/appType.js', - ), - '@selfxyz/common/utils/date': path.resolve( - commonPath, - 'dist/esm/src/utils/date.js', - ), - '@selfxyz/common/utils/arrays': path.resolve( - commonPath, - 'dist/esm/src/utils/arrays.js', - ), - '@selfxyz/common/utils/passports': path.resolve( - commonPath, - 'dist/esm/src/utils/passports/index.js', - ), - '@selfxyz/common/utils/passportFormat': path.resolve( - commonPath, - 'dist/esm/src/utils/passports/format.js', - ), - '@selfxyz/common/utils/passports/validate': path.resolve( - commonPath, - 'dist/esm/src/utils/passports/validate.js', - ), - '@selfxyz/common/utils/passportMock': path.resolve( - commonPath, - 'dist/esm/src/utils/passports/mock.js', - ), - '@selfxyz/common/utils/passportDg1': path.resolve( - commonPath, - 'dist/esm/src/utils/passports/dg1.js', - ), - '@selfxyz/common/utils/certificates': path.resolve( - commonPath, - 'dist/esm/src/utils/certificate_parsing/index.js', - ), - '@selfxyz/common/utils/elliptic': path.resolve( - commonPath, - 'dist/esm/src/utils/certificate_parsing/elliptic.js', - ), - '@selfxyz/common/utils/curves': path.resolve( - commonPath, - 'dist/esm/src/utils/certificate_parsing/curves.js', - ), - '@selfxyz/common/utils/oids': path.resolve( - commonPath, - 'dist/esm/src/utils/certificate_parsing/oids.js', - ), - '@selfxyz/common/utils/circuits': path.resolve( - commonPath, - 'dist/esm/src/utils/circuits/index.js', - ), - '@selfxyz/common/utils/circuitNames': path.resolve( - commonPath, - 'dist/esm/src/utils/circuits/circuitsName.js', - ), - '@selfxyz/common/utils/circuitFormat': path.resolve( - commonPath, - 'dist/esm/src/utils/circuits/formatOutputs.js', - ), - '@selfxyz/common/utils/uuid': path.resolve( - commonPath, - 'dist/esm/src/utils/circuits/uuid.js', - ), - '@selfxyz/common/utils/contracts': path.resolve( - commonPath, - 'dist/esm/src/utils/contracts/index.js', - ), - '@selfxyz/common/utils/sanctions': path.resolve( - commonPath, - 'dist/esm/src/utils/contracts/forbiddenCountries.js', - ), - '@selfxyz/common/utils/csca': path.resolve( - commonPath, - 'dist/esm/src/utils/csca.js', - ), - '@selfxyz/common/utils/ofac': path.resolve( - commonPath, - 'dist/esm/src/utils/ofac.js', - ), - // Types subpaths - '@selfxyz/common/types/passport': path.resolve( - commonPath, - 'dist/esm/src/types/passport.js', - ), - '@selfxyz/common/types/app': path.resolve( - commonPath, - 'dist/esm/src/types/app.js', - ), - '@selfxyz/common/types/certificates': path.resolve( - commonPath, - 'dist/esm/src/types/certificates.js', - ), - '@selfxyz/common/types/circuits': path.resolve( - commonPath, - 'dist/esm/src/types/circuits.js', - ), -}; -const watchFolders = [ - path.resolve(commonPath), - trueMonorepoNodeModules, - path.join(__dirname, 'src'), - path.resolve(sdkAlphaPath), -]; +const projectRoot = __dirname; +const workspaceRoot = + findYarnWorkspaceRoot(__dirname) || path.resolve(__dirname, '..'); /** - * Metro configuration - * https://facebook.github.io/metro/docs/configuration + * Modern Metro configuration using native workspace capabilities + * Eliminates need for manual symlink management through: + * - enableGlobalPackages: Automatic workspace package discovery + * - unstable_enablePackageExports: Native subpath import support + * - unstable_enableSymlinks: Optional symlink resolution * * @type {import('metro-config').MetroConfig} */ const config = { + projectRoot, + + watchFolders: [ + workspaceRoot, // Watch entire workspace root for changes + path.resolve(workspaceRoot, 'common'), + path.resolve(workspaceRoot, 'packages/mobile-sdk-alpha'), + path.resolve(projectRoot, 'node_modules'), // Watch app's node_modules for custom resolved modules + ], + transformer: { babelTransformerPath: require.resolve( 'react-native-svg-transformer/react-native', @@ -210,27 +39,247 @@ const config = { disableImportExportTransform: true, inlineRequires: true, }, + resolver: { - extraNodeModules, - nodeModulesPaths: [ - path.resolve(__dirname, 'node_modules'), // App's own node_modules - path.resolve(monorepoRoot, 'node_modules'), // Monorepo root node_modules - trueMonorepoNodeModules, - // Add paths to other package workspaces if needed + // Prevent Haste module naming collisions from duplicate package.json files + blockList: [ + // Ignore built package.json files to prevent Haste collisions + /.*\/dist\/package\.json$/, + /.*\/dist\/esm\/package\.json$/, + /.*\/dist\/cjs\/package\.json$/, + /.*\/build\/package\.json$/, + // Prevent duplicate React/React Native - block workspace root versions and use app's versions + // Use precise regex patterns to avoid blocking packages like react-native-get-random-values + new RegExp( + `^${workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/node_modules/react(/|$)`, + ), + new RegExp( + `^${workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/node_modules/react-dom(/|$)`, + ), + new RegExp( + `^${workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/node_modules/react-native(/|$)`, + ), + new RegExp( + `^${workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/node_modules/scheduler(/|$)`, + ), + new RegExp('packages/mobile-sdk-alpha/node_modules/react(/|$)'), + new RegExp('packages/mobile-sdk-alpha/node_modules/react-dom(/|$)'), + new RegExp('packages/mobile-sdk-alpha/node_modules/react-native(/|$)'), + new RegExp('packages/mobile-sdk-alpha/node_modules/scheduler(/|$)'), + new RegExp('packages/mobile-sdk-demo/node_modules/react(/|$)'), + new RegExp('packages/mobile-sdk-demo/node_modules/react-dom(/|$)'), + new RegExp('packages/mobile-sdk-demo/node_modules/react-native(/|$)'), + new RegExp('packages/mobile-sdk-demo/node_modules/scheduler(/|$)'), ], + // Enable automatic workspace package resolution + enableGlobalPackages: true, + + // Handle subpath exports (@selfxyz/common/constants) + unstable_enablePackageExports: true, + + // Enable native symlink support (optional, for compatibility) + unstable_enableSymlinks: true, + + // Define search order for node modules - prioritize app's modules for React consistency + nodeModulesPaths: [ + path.resolve(projectRoot, 'node_modules'), // App's own node_modules FIRST + path.resolve(workspaceRoot, 'node_modules'), // Workspace root node_modules SECOND + ], + + // Essential polyfills for React Native + extraNodeModules: { + stream: require.resolve('stream-browserify'), + buffer: require.resolve('buffer'), + util: require.resolve('util'), + assert: require.resolve('assert'), + events: require.resolve('events'), + // App-specific alias + '@': path.join(__dirname, 'src'), + }, + + // Support package exports with conditions + unstable_conditionNames: ['react-native', 'import', 'require'], + + // SVG support assetExts: assetExts.filter(ext => ext !== 'svg'), sourceExts: [...sourceExts, 'svg'], - // Custom resolver to handle Node.js modules elegantly + // Custom resolver to handle both .js imports in TypeScript and Node.js modules resolveRequest: (context, moduleName, platform) => { - // Handle problematic Node.js modules that don't work in React Native + // Handle React Native gesture handler that needs app-level resolution + const appLevelModules = { + 'react-native-gesture-handler': + 'react-native-gesture-handler/lib/commonjs/index.js', + }; + + if (appLevelModules[moduleName]) { + try { + return { + type: 'sourceFile', + filePath: require.resolve(appLevelModules[moduleName], { + paths: [projectRoot], + }), + }; + } catch (error) { + console.warn(`Failed to resolve ${moduleName}:`, error); + // Fall back to default resolution + return context.resolveRequest(context, moduleName, platform); + } + } + + // React modules now resolve naturally through nodeModulesPaths (app's node_modules first) + + // Force SDK to use built ESM to avoid duplicate React and source transpilation issues + if (moduleName === '@selfxyz/mobile-sdk-alpha') { + return { + type: 'sourceFile', + filePath: path.resolve( + workspaceRoot, + 'packages/mobile-sdk-alpha/dist/esm/index.js', + ), + }; + } + // For relative imports in common source files that end with .js + if ( + context.originModulePath?.includes('/common/src/') && + moduleName.endsWith('.js') + ) { + const tsModuleName = moduleName.replace(/\.js$/, '.ts'); + return context.resolveRequest(context, tsModuleName, platform); + } + + // Handle problematic package exports and Node.js modules + + // Fix @tamagui/config v2-native export resolution + if (moduleName === '@tamagui/config/v2-native') { + try { + return { + type: 'sourceFile', + filePath: require.resolve('@tamagui/config/dist/esm/v2-native.js'), + }; + } catch { + // Fallback to main export if specific file doesn't exist + return { + type: 'sourceFile', + filePath: require.resolve('@tamagui/config'), + }; + } + } + + // Fix @noble/hashes subpath export resolution + if (moduleName.startsWith('@noble/hashes/')) { + try { + // Extract the subpath (e.g., 'crypto.js', 'sha256', 'hmac') + const subpath = moduleName.replace('@noble/hashes/', ''); + const basePath = require.resolve('@noble/hashes'); + + // For .js files, look in the package directory + if (subpath.endsWith('.js')) { + const subpathFile = path.join(path.dirname(basePath), subpath); + return { + type: 'sourceFile', + filePath: subpathFile, + }; + } else { + // For other imports like 'sha256', 'hmac', etc., try the main directory + const subpathFile = path.join( + path.dirname(basePath), + `${subpath}.js`, + ); + return { + type: 'sourceFile', + filePath: subpathFile, + }; + } + } catch { + // Fallback to main package if subpath doesn't exist + return { + type: 'sourceFile', + filePath: require.resolve('@noble/hashes'), + }; + } + } + + // Fix snarkjs and ffjavascript platform exports for Android + if (platform === 'android') { + // Handle snarkjs and its nested dependencies that have platform export issues + if ( + moduleName.includes('/snarkjs') && + (moduleName.endsWith('/snarkjs') || + moduleName.includes('/snarkjs/node_modules')) + ) { + try { + // Try to resolve the main package file + const packagePath = moduleName.split('/node_modules/').pop(); + const resolved = require.resolve(packagePath || 'snarkjs'); + return { + type: 'sourceFile', + filePath: resolved, + }; + } catch { + // Fallback to basic snarkjs resolution + try { + return { + type: 'sourceFile', + filePath: require.resolve('snarkjs'), + }; + } catch { + // Continue to next check + } + } + } + + // Handle ffjavascript from any nested location + if ( + moduleName.includes('/ffjavascript') && + moduleName.endsWith('/ffjavascript') + ) { + try { + // Try to resolve ffjavascript from the specific nested location first + const resolved = require.resolve(moduleName); + return { + type: 'sourceFile', + filePath: resolved, + }; + } catch { + // Fallback to resolving ffjavascript from the closest available location + try { + const resolved = require.resolve('ffjavascript'); + return { + type: 'sourceFile', + filePath: resolved, + }; + } catch { + // Continue to next check + } + } + } + + // Handle direct package imports for known problematic packages + const platformProblematicPackages = ['snarkjs', 'ffjavascript']; + for (const pkg of platformProblematicPackages) { + if (moduleName === pkg || moduleName.startsWith(`${pkg}/`)) { + try { + return { + type: 'sourceFile', + filePath: require.resolve(pkg), + }; + } catch { + // Continue to next check + continue; + } + } + } + } + const nodeModuleRedirects = { - crypto: require.resolve('crypto-browserify'), + crypto: path.resolve(__dirname, '../common/src/polyfills/crypto.ts'), fs: false, // Disable filesystem access os: false, // Disable OS-specific modules readline: false, // Disable readline module constants: require.resolve('constants-browserify'), path: require.resolve('path-browserify'), + 'web-worker': false, // Disable web workers (not available in React Native) }; if ( @@ -247,11 +296,36 @@ const config = { }; } + // Handle optional peer dependencies by returning empty modules + const optionalPeerDependencies = [ + 'react-native-reanimated', + '@react-native-masked-view/masked-view', + '@react-native-firebase/analytics', + ]; + + if (optionalPeerDependencies.includes(moduleName)) { + // Return empty module for optional peer dependencies + return { type: 'empty' }; + } + // Fall back to default Metro resolver for all other modules - return context.resolveRequest(context, moduleName, platform); + try { + return context.resolveRequest(context, moduleName, platform); + } catch (error) { + // Check if this is one of our expected optional dependencies + if (optionalPeerDependencies.some(dep => moduleName.includes(dep))) { + return { type: 'empty' }; + } + + // If default resolution fails, log and re-throw + console.warn( + `Metro resolver failed for module "${moduleName}":`, + error.message, + ); + throw error; + } }, }, - watchFolders, }; module.exports = mergeConfig(defaultConfig, config); diff --git a/app/package.json b/app/package.json index 492c2d68a..c3d55bdb3 100644 --- a/app/package.json +++ b/app/package.json @@ -69,15 +69,20 @@ "web:build": "yarn build:deps && vite build", "web:preview": "vite preview" }, + "resolutions": { + "punycode": "npm:punycode.js@2.3.1" + }, "overrides": { - "punycode": "npm:punycode.js@^2.3.1" + "punycode": "npm:punycode.js@2.3.1" }, "dependencies": { "@babel/runtime": "^7.28.3", "@ethersproject/shims": "^5.7.0", "@noble/hashes": "^1.5.0", + "@openpassport/zk-kit-imt": "^0.0.5", "@openpassport/zk-kit-lean-imt": "^0.0.6", "@openpassport/zk-kit-smt": "^0.0.1", + "@peculiar/x509": "^1.13.0", "@react-native-async-storage/async-storage": "^2.2.0", "@react-native-clipboard/clipboard": "1.16.3", "@react-native-community/blur": "^4.4.1", @@ -94,11 +99,12 @@ "@selfxyz/mobile-sdk-alpha": "workspace:^", "@sentry/react": "^9.32.0", "@sentry/react-native": "7.0.1", - "@tamagui/animations-css": "^1.129.3", - "@tamagui/animations-react-native": "^1.129.3", + "@stablelib/cbor": "^2.0.1", + "@tamagui/animations-css": "1.126.14", + "@tamagui/animations-react-native": "1.126.14", "@tamagui/config": "1.126.14", "@tamagui/lucide-icons": "1.126.14", - "@tamagui/toast": "^1.127.2", + "@tamagui/toast": "1.126.14", "@xstate/react": "^5.0.3", "add": "^2.0.6", "asn1js": "^3.0.6", @@ -107,12 +113,17 @@ "elliptic": "^6.6.1", "ethers": "^6.11.0", "expo-modules-core": "^2.2.1", + "hash.js": "^1.1.7", + "i18n-iso-countries": "^7.14.0", + "js-sha1": "^0.7.0", + "js-sha256": "^0.11.1", "js-sha512": "^0.9.0", "lottie-react": "^2.4.1", "lottie-react-native": "7.2.2", "node-forge": "^1.3.1", "pkijs": "^3.2.5", "poseidon-lite": "^0.2.0", + "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-native": "0.76.9", @@ -148,6 +159,7 @@ "devDependencies": { "@babel/core": "^7.28.3", "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/preset-env": "^7.28.3", "@react-native-community/cli": "^16.0.3", "@react-native/babel-preset": "0.76.9", "@react-native/eslint-config": "0.76.9", @@ -175,8 +187,8 @@ "@typescript-eslint/parser": "^8.39.0", "@vitejs/plugin-react-swc": "^3.10.2", "babel-plugin-module-resolver": "^5.0.2", + "buffer": "^6.0.3", "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.12.1", "dompurify": "^3.2.6", "eslint": "^8.57.0", "eslint-config-prettier": "10.1.8", diff --git a/app/scripts/tests/alias-imports.test.cjs b/app/scripts/tests/aliasImports.test.cjs similarity index 100% rename from app/scripts/tests/alias-imports.test.cjs rename to app/scripts/tests/aliasImports.test.cjs diff --git a/app/scripts/tests/bundle-analyze-ci.test.cjs b/app/scripts/tests/bundleAnalyzeCi.test.cjs similarity index 100% rename from app/scripts/tests/bundle-analyze-ci.test.cjs rename to app/scripts/tests/bundleAnalyzeCi.test.cjs diff --git a/app/scripts/tests/cleanup-ios-build.test.cjs b/app/scripts/tests/cleanupIosBuild.test.cjs similarity index 100% rename from app/scripts/tests/cleanup-ios-build.test.cjs rename to app/scripts/tests/cleanupIosBuild.test.cjs diff --git a/app/scripts/tests/mobile-deploy-confirm.test.cjs b/app/scripts/tests/mobileDeployConfirm.test.cjs similarity index 100% rename from app/scripts/tests/mobile-deploy-confirm.test.cjs rename to app/scripts/tests/mobileDeployConfirm.test.cjs diff --git a/app/scripts/tests/mobile-deploy-confirm-module.test.cjs b/app/scripts/tests/mobileDeployConfirmModule.test.cjs similarity index 100% rename from app/scripts/tests/mobile-deploy-confirm-module.test.cjs rename to app/scripts/tests/mobileDeployConfirmModule.test.cjs diff --git a/app/scripts/tests/tree-shaking.test.cjs b/app/scripts/tests/treeShaking.test.cjs similarity index 100% rename from app/scripts/tests/tree-shaking.test.cjs rename to app/scripts/tests/treeShaking.test.cjs diff --git a/app/src/components/homeScreen/idCard.tsx b/app/src/components/homeScreen/idCard.tsx index a997124e1..cea58cb35 100644 --- a/app/src/components/homeScreen/idCard.tsx +++ b/app/src/components/homeScreen/idCard.tsx @@ -6,16 +6,13 @@ import type { FC } from 'react'; import { Dimensions } from 'react-native'; import { Separator, Text, XStack, YStack } from 'tamagui'; -import { - AadhaarData, - isAadhaarDocument, - isMRZDocument, - PassportData, -} from '@selfxyz/common'; +import type { AadhaarData } from '@selfxyz/common'; import { attributeToPosition, attributeToPosition_ID, } from '@selfxyz/common/constants'; +import type { PassportData } from '@selfxyz/common/types/passport'; +import { isAadhaarDocument, isMRZDocument } from '@selfxyz/common/utils/types'; import { SvgXml } from '@/components/homeScreen/SvgXmlWrapper'; import AadhaarIcon from '@/images/icons/aadhaar.svg'; diff --git a/app/src/hooks/useMockDataForm.ts b/app/src/hooks/useMockDataForm.ts index 12cd9c5f1..62c52788f 100644 --- a/app/src/hooks/useMockDataForm.ts +++ b/app/src/hooks/useMockDataForm.ts @@ -14,12 +14,12 @@ export const useMockDataForm = () => { const [selectedDocumentType, setSelectedDocumentType] = useState< 'mock_passport' | 'mock_id_card' | 'mock_aadhaar' >('mock_passport'); - const [isInOfacList, setIsInOfacList] = useState(true); + const [isInOfacList, setIsInOfacList] = useState(false); const resetFormValues = () => { setAge(21); setExpiryYears(5); - setIsInOfacList(true); + setIsInOfacList(false); setSelectedDocumentType('mock_passport'); setSelectedAlgorithm('sha256 rsa 65537 2048'); setSelectedCountry('USA'); diff --git a/app/src/mocks/nfcScanner.ts b/app/src/mocks/nfcScanner.ts new file mode 100644 index 000000000..03ba23b01 --- /dev/null +++ b/app/src/mocks/nfcScanner.ts @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +// Web mock for NFC scanner functionality +// NFC passport scanning is not supported on web browsers + +export const parseScanResponse = (_response: unknown) => { + throw new Error('NFC passport scanning is not supported on web browsers'); +}; + +export const scan = async (_inputs: unknown) => { + throw new Error('NFC passport scanning is not supported on web browsers'); +}; diff --git a/app/src/providers/passportDataProvider.tsx b/app/src/providers/passportDataProvider.tsx index a6c14ed9c..c6ad74890 100644 --- a/app/src/providers/passportDataProvider.tsx +++ b/app/src/providers/passportDataProvider.tsx @@ -44,17 +44,16 @@ import type { PropsWithChildren } from 'react'; import React, { createContext, useCallback, useContext, useMemo } from 'react'; import Keychain from 'react-native-keychain'; -import { isMRZDocument } from '@selfxyz/common'; import type { PublicKeyDetailsECDSA, PublicKeyDetailsRSA, -} from '@selfxyz/common/utils'; +} from '@selfxyz/common/types/certificates'; import { brutforceSignatureAlgorithmDsc, calculateContentHash, inferDocumentCategory, - parseCertificateSimple, } from '@selfxyz/common/utils'; +import { parseCertificateSimple } from '@selfxyz/common/utils/certificate_parsing/parseCertificateSimple'; import type { AadhaarData, DocumentCatalog, @@ -62,6 +61,7 @@ import type { IDDocument, PassportData, } from '@selfxyz/common/utils/types'; +import { isMRZDocument } from '@selfxyz/common/utils/types'; import type { DocumentsAdapter, SelfClient } from '@selfxyz/mobile-sdk-alpha'; import { getAllDocuments, useSelfClient } from '@selfxyz/mobile-sdk-alpha'; diff --git a/app/src/screens/document/DocumentNFCScanScreen.tsx b/app/src/screens/document/DocumentNFCScanScreen.tsx index c1bf062a8..316667499 100644 --- a/app/src/screens/document/DocumentNFCScanScreen.tsx +++ b/app/src/screens/document/DocumentNFCScanScreen.tsx @@ -628,9 +628,7 @@ const DocumentNFCScanScreen: React.FC = () => { width="$8" alignSelf="center" borderRadius={1000} - source={{ - uri: NFC_IMAGE, - }} + source={NFC_IMAGE} margin={20} /> diff --git a/app/src/screens/document/DocumentNFCScanScreen.web.tsx b/app/src/screens/document/DocumentNFCScanScreen.web.tsx index da73b9165..1a6afeadb 100644 --- a/app/src/screens/document/DocumentNFCScanScreen.web.tsx +++ b/app/src/screens/document/DocumentNFCScanScreen.web.tsx @@ -55,9 +55,7 @@ const DocumentNFCScanScreen: React.FC = () => { width="$8" alignSelf="center" borderRadius={1000} - source={{ - uri: NFC_IMAGE, - }} + source={NFC_IMAGE} margin={20} /> diff --git a/app/src/screens/document/aadhaar/AadhaarUploadScreen.tsx b/app/src/screens/document/aadhaar/AadhaarUploadScreen.tsx index 58acf4eef..70f1d51f5 100644 --- a/app/src/screens/document/aadhaar/AadhaarUploadScreen.tsx +++ b/app/src/screens/document/aadhaar/AadhaarUploadScreen.tsx @@ -259,7 +259,7 @@ const AadhaarUploadScreen: React.FC = () => { paddingVertical={20} > { const isFocused = useIsFocused(); const [doneScanningQR, setDoneScanningQR] = useState(false); const navigateToProve = useHapticNavigation('Prove'); - const navigateToHome = useHapticNavigation('Home'); - const onCancelPress = useCallback(() => { - navigateToHome(); - }, [navigateToHome]); // This resets to the default state when we navigate back to this screen useFocusEffect( diff --git a/app/src/screens/recovery/AccountRecoveryChoiceScreen.tsx b/app/src/screens/recovery/AccountRecoveryChoiceScreen.tsx index da3822ae5..1831bbb9e 100644 --- a/app/src/screens/recovery/AccountRecoveryChoiceScreen.tsx +++ b/app/src/screens/recovery/AccountRecoveryChoiceScreen.tsx @@ -66,8 +66,13 @@ const AccountRecoveryChoiceScreen: React.FC = () => { return useProtocolStore.getState()[docCategory].commitment_tree; }, getAltCSCA(docCategory) { - if (passportData.documentCategory === 'aadhaar') { - return useProtocolStore.getState().aadhaar.public_keys; + if (docCategory === 'aadhaar') { + const publicKeys = + useProtocolStore.getState().aadhaar.public_keys; + // Convert string[] to Record format expected by AlternativeCSCA + return publicKeys + ? Object.fromEntries(publicKeys.map(key => [key, key])) + : {}; } return useProtocolStore.getState()[docCategory].alternative_csca; }, diff --git a/app/src/screens/recovery/RecoverWithPhraseScreen.tsx b/app/src/screens/recovery/RecoverWithPhraseScreen.tsx index 0c94719ab..1e651b4e7 100644 --- a/app/src/screens/recovery/RecoverWithPhraseScreen.tsx +++ b/app/src/screens/recovery/RecoverWithPhraseScreen.tsx @@ -72,7 +72,11 @@ const RecoverWithPhraseScreen: React.FC = () => { }, getAltCSCA(docCategory) { if (docCategory === 'aadhaar') { - return useProtocolStore.getState()[docCategory].public_keys; + const publicKeys = useProtocolStore.getState().aadhaar.public_keys; + // Convert string[] to Record format expected by AlternativeCSCA + return publicKeys + ? Object.fromEntries(publicKeys.map(key => [key, key])) + : {}; } return useProtocolStore.getState()[docCategory].alternative_csca; }, diff --git a/app/src/screens/system/Loading.tsx b/app/src/screens/system/Loading.tsx index e5ceac9c6..397ae3d17 100644 --- a/app/src/screens/system/Loading.tsx +++ b/app/src/screens/system/Loading.tsx @@ -10,12 +10,10 @@ import { Text, YStack } from 'tamagui'; import type { StaticScreenProps } from '@react-navigation/native'; import { useIsFocused } from '@react-navigation/native'; -import { IDDocument } from '@selfxyz/common/dist/esm/src/utils/types'; -import type { PassportData } from '@selfxyz/common/types'; +import { IDDocument } from '@selfxyz/common/utils/types'; import failAnimation from '@/assets/animations/loading/fail.json'; import proveLoadingAnimation from '@/assets/animations/loading/prove.json'; -import successAnimation from '@/assets/animations/loading/success.json'; import CloseWarningIcon from '@/images/icons/close-warning.svg'; import { loadPassportDataAndSecret } from '@/providers/passportDataProvider'; import { black, slate400, white, zinc500, zinc900 } from '@/utils/colors'; diff --git a/app/src/stores/database.ts b/app/src/stores/database.ts index e782fc7d1..55d94008c 100644 --- a/app/src/stores/database.ts +++ b/app/src/stores/database.ts @@ -4,12 +4,8 @@ import SQLite from 'react-native-sqlite-storage'; -import type { - ProofDB, - ProofDBResult, - ProofHistory, -} from '@/stores/proof-types'; -import { ProofStatus } from '@/stores/proof-types'; +import type { ProofDB, ProofDBResult, ProofHistory } from '@/stores/proofTypes'; +import { ProofStatus } from '@/stores/proofTypes'; const PAGE_SIZE = 20; const DB_NAME = 'proof_history.db'; diff --git a/app/src/stores/database.web.ts b/app/src/stores/database.web.ts index 99c8a672c..21b7717a4 100644 --- a/app/src/stores/database.web.ts +++ b/app/src/stores/database.web.ts @@ -2,12 +2,8 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import type { - ProofDB, - ProofDBResult, - ProofHistory, -} from '@/stores/proof-types'; -import { ProofStatus } from '@/stores/proof-types'; +import type { ProofDB, ProofDBResult, ProofHistory } from '@/stores/proofTypes'; +import { ProofStatus } from '@/stores/proofTypes'; export const DB_NAME = 'proof_history_db'; const STORE_NAME = 'proof_history'; diff --git a/app/src/stores/proofHistoryStore.ts b/app/src/stores/proofHistoryStore.ts index 89ea31d11..48d25116d 100644 --- a/app/src/stores/proofHistoryStore.ts +++ b/app/src/stores/proofHistoryStore.ts @@ -8,8 +8,8 @@ import { create } from 'zustand'; import { WS_DB_RELAYER } from '@selfxyz/common/constants'; import { database } from '@/stores/database'; -import type { ProofHistory } from '@/stores/proof-types'; -import { ProofStatus } from '@/stores/proof-types'; +import type { ProofHistory } from '@/stores/proofTypes'; +import { ProofStatus } from '@/stores/proofTypes'; interface ProofHistoryState { proofHistory: ProofHistory[]; diff --git a/app/src/stores/proof-types.ts b/app/src/stores/proofTypes.ts similarity index 100% rename from app/src/stores/proof-types.ts rename to app/src/stores/proofTypes.ts diff --git a/app/src/types/country-iso-3-to-2.d.ts b/app/src/types/countryIso3To2.d.ts similarity index 100% rename from app/src/types/country-iso-3-to-2.d.ts rename to app/src/types/countryIso3To2.d.ts diff --git a/packages/mobile-sdk-alpha/demo-app/src/ProveQRCode.tsx b/app/src/types/elliptic.d.ts similarity index 63% rename from packages/mobile-sdk-alpha/demo-app/src/ProveQRCode.tsx rename to app/src/types/elliptic.d.ts index 25baf9b3a..b746be8d6 100644 --- a/packages/mobile-sdk-alpha/demo-app/src/ProveQRCode.tsx +++ b/app/src/types/elliptic.d.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -export default function ProveQRCode() { - // TODO: implement QR code proof - return null; +declare module 'elliptic' { + const elliptic: any; + export = elliptic; } diff --git a/app/src/types/png.d.ts b/app/src/types/png.d.ts index 031ff0ecb..85881324c 100644 --- a/app/src/types/png.d.ts +++ b/app/src/types/png.d.ts @@ -3,6 +3,8 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. declare module '*.png' { - const content: any; - export default content; + import type { ImageSourcePropType } from 'react-native'; + + const pngContent: ImageSourcePropType; + export default pngContent; } diff --git a/app/src/types/react-native-passport-reader.d.ts b/app/src/types/reactNativePassportReader.d.ts similarity index 100% rename from app/src/types/react-native-passport-reader.d.ts rename to app/src/types/reactNativePassportReader.d.ts diff --git a/app/src/types/svg.d.ts b/app/src/types/svg.d.ts index 765c05aaa..2cf977a93 100644 --- a/app/src/types/svg.d.ts +++ b/app/src/types/svg.d.ts @@ -6,6 +6,6 @@ declare module '*.svg' { import type React from 'react'; import type { SvgProps } from 'react-native-svg'; - const content: React.FC; - export default content; + const svgContent: React.FC; + export default svgContent; } diff --git a/app/src/utils/proving/provingMachine.ts b/app/src/utils/proving/provingMachine.ts index 674f3f765..3eccbe366 100644 --- a/app/src/utils/proving/provingMachine.ts +++ b/app/src/utils/proving/provingMachine.ts @@ -136,6 +136,11 @@ const _generateCircuitInputs = async ( circuitTypeWithDocumentExtension = `${circuitType}${document === 'passport' ? '' : '_id'}`; break; case 'dsc': + if (document === 'aadhaar') { + throw new Error( + 'DSC circuit type is not supported for Aadhaar documents', + ); + } ({ inputs, circuitName, endpointType, endpoint } = generateTEEInputsDSC( passportData as PassportData, protocolStore[document].csca_tree as string[][], @@ -1014,7 +1019,7 @@ export const useProvingStore = create((set, get) => { step: 'protocol_store_fetch', document, }); - await useProtocolStore.getState()[document].fetch_all(env!); + await useProtocolStore.getState().aadhaar.fetch_all(env!); break; } logProofEvent('info', 'Data fetch succeeded', context, { @@ -1114,10 +1119,17 @@ export const useProvingStore = create((set, get) => { secret as string, { getCommitmentTree, - getAltCSCA: (docType: DocumentCategory) => - docType === 'aadhaar' - ? useProtocolStore.getState().aadhaar.public_keys - : useProtocolStore.getState()[docType].alternative_csca, + getAltCSCA: (docType: DocumentCategory) => { + if (docType === 'aadhaar') { + const publicKeys = + useProtocolStore.getState().aadhaar.public_keys; + // Convert string[] to Record format expected by AlternativeCSCA + return publicKeys + ? Object.fromEntries(publicKeys.map(key => [key, key])) + : {}; + } + return useProtocolStore.getState()[docType].alternative_csca; + }, }, ); logProofEvent( diff --git a/app/tests/src/stores/database.test.ts b/app/tests/src/stores/database.test.ts index 268478de1..680c23b8c 100644 --- a/app/tests/src/stores/database.test.ts +++ b/app/tests/src/stores/database.test.ts @@ -5,7 +5,7 @@ import SQLite from 'react-native-sqlite-storage'; import { database } from '@/stores/database'; -import { ProofStatus } from '@/stores/proof-types'; +import { ProofStatus } from '@/stores/proofTypes'; // Mock react-native-sqlite-storage jest.mock('react-native-sqlite-storage', () => ({ diff --git a/app/tests/src/stores/proofHistoryStore.test.ts b/app/tests/src/stores/proofHistoryStore.test.ts index c4793204d..10d2c46d0 100644 --- a/app/tests/src/stores/proofHistoryStore.test.ts +++ b/app/tests/src/stores/proofHistoryStore.test.ts @@ -6,8 +6,8 @@ import { io } from 'socket.io-client'; import { act } from '@testing-library/react-native'; import { database } from '@/stores/database'; -import { ProofStatus } from '@/stores/proof-types'; import { useProofHistoryStore } from '@/stores/proofHistoryStore'; +import { ProofStatus } from '@/stores/proofTypes'; // Mock socket.io-client jest.mock('socket.io-client', () => ({ diff --git a/app/tests/utils/proving/provingMachine.startFetchingData.test.ts b/app/tests/utils/proving/provingMachine.startFetchingData.test.ts index 6dd4c4dac..8a230046c 100644 --- a/app/tests/utils/proving/provingMachine.startFetchingData.test.ts +++ b/app/tests/utils/proving/provingMachine.startFetchingData.test.ts @@ -39,18 +39,46 @@ jest.mock('@/providers/authProvider', () => ({ // app/tests/utils/proving/provingMachine.startFetchingData.test.ts -jest.mock('@selfxyz/mobile-sdk-alpha', () => { - const actual = jest.requireActual('@selfxyz/mobile-sdk-alpha'); +jest.mock('@selfxyz/mobile-sdk-alpha', () => ({ + __esModule: true, + // Mock only the specific functions needed by this test + loadSelectedDocument: jest.fn().mockResolvedValue({ + data: { + documentCategory: 'passport', + mock: false, + dsc_parsed: { authorityKeyIdentifier: 'key' }, + }, + }), +})); + +// Mock the stores import separately with state management +jest.mock('@selfxyz/mobile-sdk-alpha/stores', () => { + const mockProtocolStoreState = {}; + const mockSelfAppStoreState = { selfApp: {}, handleProofResult: jest.fn() }; + return { - __esModule: true, - ...actual, - loadSelectedDocument: jest.fn().mockResolvedValue({ - data: { - documentCategory: 'passport', - mock: false, - dsc_parsed: { authorityKeyIdentifier: 'key' }, + useProtocolStore: Object.assign( + jest.fn(() => mockProtocolStoreState), + { + // Add Zustand store methods with state management + setState: jest.fn(updates => + Object.assign(mockProtocolStoreState, updates), + ), + getState: jest.fn(() => mockProtocolStoreState), + subscribe: jest.fn(() => jest.fn()), }, - }), + ), + useSelfAppStore: Object.assign( + jest.fn(() => mockSelfAppStoreState), + { + // Add Zustand store methods with state management + setState: jest.fn(updates => + Object.assign(mockSelfAppStoreState, updates), + ), + getState: jest.fn(() => mockSelfAppStoreState), + subscribe: jest.fn(() => jest.fn()), + }, + ), }; }); diff --git a/app/tests/utils/proving/provingMachine.test.ts b/app/tests/utils/proving/provingMachine.test.ts index 76afb7023..54cf078f3 100644 --- a/app/tests/utils/proving/provingMachine.test.ts +++ b/app/tests/utils/proving/provingMachine.test.ts @@ -16,15 +16,21 @@ jest.mock('@/navigation', () => ({ }, })); -jest.mock('@selfxyz/mobile-sdk-alpha', () => { - const actual = jest.requireActual('@selfxyz/mobile-sdk-alpha'); - - return { - ...actual, - loadSelectedDocument: jest.fn().mockResolvedValue(null), - hasAnyValidRegisteredDocument: jest.fn().mockResolvedValue(true), - }; -}); +jest.mock('@selfxyz/mobile-sdk-alpha', () => ({ + // Mock only the exports needed by this test + SdkEvents: { + DOCUMENT_SELECTED: 'DOCUMENT_SELECTED', + DOCUMENT_LOADED: 'DOCUMENT_LOADED', + REGISTRATION_COMPLETED: 'REGISTRATION_COMPLETED', + PROVING_PASSPORT_DATA_NOT_FOUND: 'PROVING_PASSPORT_DATA_NOT_FOUND', + PROVING_PASSPORT_NOT_SUPPORTED: 'PROVING_PASSPORT_NOT_SUPPORTED', + PROVING_ACCOUNT_RECOVERY_REQUIRED: 'PROVING_ACCOUNT_RECOVERY_REQUIRED', + PROVING_ACCOUNT_VERIFIED_SUCCESS: 'PROVING_ACCOUNT_VERIFIED_SUCCESS', + PROVING_REGISTER_ERROR_OR_FAILURE: 'PROVING_REGISTER_ERROR_OR_FAILURE', + }, + loadSelectedDocument: jest.fn().mockResolvedValue(null), + hasAnyValidRegisteredDocument: jest.fn().mockResolvedValue(true), +})); describe('provingMachine registration completion', () => { beforeEach(() => { diff --git a/app/tsconfig.json b/app/tsconfig.json index b4651bb3a..38ac62482 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -3,23 +3,17 @@ "compilerOptions": { "lib": ["dom", "esnext"], "module": "esnext", + "baseUrl": ".", "resolveJsonModule": true, "esModuleInterop": true, + "skipLibCheck": true, + "skipDefaultLibCheck": false, "paths": { "@env": ["./env.ts"], - "@selfxyz/common": ["../common"], - "@selfxyz/common/*": ["../common/*"], - "@selfxyz/common/utils/passports/*": ["../common/utils/passports/*"], "@selfxyz/mobile-sdk-alpha": [ "../packages/mobile-sdk-alpha/src", "../packages/mobile-sdk-alpha/dist" ], - "@selfxyz/mobile-sdk-alpha/constants/analytics": [ - "../packages/mobile-sdk-alpha/dist/esm/constants/analytics.js" - ], - "@selfxyz/mobile-sdk-alpha/stores": [ - "../packages/mobile-sdk-alpha/dist/esm/stores.js" - ], "@/*": ["./src/*"] } }, diff --git a/app/vite.config.ts b/app/vite.config.ts index 243279557..1494b0833 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -50,10 +50,30 @@ export default defineConfig({ __dirname, 'src/mocks/react-native-passport-reader.ts', ), - crypto: 'crypto-browserify', + '@/utils/nfcScanner': resolve(__dirname, 'src/mocks/nfcScanner.ts'), + crypto: resolve(__dirname, '../common/src/polyfills/crypto.ts'), + buffer: 'buffer', + + // Fix @noble/hashes subpath exports for web builds + '@noble/hashes/crypto.js': '@noble/hashes/crypto', }, }, plugins: [ + { + name: 'fix-buffer-externalization', + transform(code, id) { + // Fix the mobile-sdk-alpha chunk that references Buffer + if (id.includes('mobile-sdk-alpha') && code.includes('from "buffer"')) { + // Keep the import so the polyfill is bundled, and set global assignment + const fixedCode = code.replace( + /import\s+\{\s*Buffer\s*\}\s+from\s+['"]buffer['"]/g, + "import { Buffer } from 'buffer';\nif (typeof globalThis.Buffer === 'undefined') { globalThis.Buffer = Buffer; }", + ); + return { code: fixedCode, map: null }; + } + return null; + }, + }, react(), svgr({ include: '**/*.svg', @@ -85,6 +105,7 @@ export default defineConfig({ global: 'globalThis', }, optimizeDeps: { + include: ['buffer'], exclude: ['fs', 'path', 'child_process', '@zk-email/helpers'], esbuildOptions: { // Optimize minification @@ -93,7 +114,6 @@ export default defineConfig({ minifyWhitespace: true, }, }, - build: { emptyOutDir: true, outDir: resolve(__dirname, 'web/dist'), @@ -103,7 +123,7 @@ export default defineConfig({ cssMinify: true, cssCodeSplit: true, rollupOptions: { - external: ['fs', 'child_process'], + external: ['fs', 'child_process', '@zk-email/helpers'], output: { // Optimize chunk size and minification compact: true, diff --git a/app/web/index.html b/app/web/index.html index 82d06b41d..5b3bddcc5 100644 --- a/app/web/index.html +++ b/app/web/index.html @@ -5,6 +5,22 @@ Self App + +
diff --git a/packages/mobile-sdk-alpha/demo-app/babel.config.js b/babel.config.js similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/babel.config.js rename to babel.config.js diff --git a/common/index.ts b/common/index.ts index a7174d797..0546c71d9 100644 --- a/common/index.ts +++ b/common/index.ts @@ -2,8 +2,13 @@ export type { AadhaarData, CertificateData, + DeployedCircuits, + DocumentCatalog, DocumentCategory, + DocumentMetadata, + IDDocument, IdDocInput, + OfacTree, PassportData, PassportMetadata, PublicKeyDetailsECDSA, @@ -57,6 +62,7 @@ export { bigIntToString, brutforceSignatureAlgorithmDsc, buildSMT, + calculateContentHash, calculateUserIdentifierHash, findStartPubKeyIndex, formatEndpoint, @@ -74,10 +80,13 @@ export { getCircuitNameFromPassportData, getLeafCscaTree, getLeafDscTree, + fetchOfacTrees, getSKIPEM, + generateTEEInputsDiscloseStateless, getSolidityPackedUserContextData, getUniversalLink, hashEndpointWithScope, + inferDocumentCategory, initElliptic, initPassportDataParsing, parseCertificateSimple, @@ -85,6 +94,15 @@ export { stringToBigInt, } from './src/utils/index.js'; +// Crypto polyfill for cross-platform compatibility +export { + createHash, + createHmac, + default as cryptoPolyfill, + pbkdf2Sync, + randomBytes, +} from './src/polyfills/crypto.js'; + export { createSelector } from './src/utils/aadhaar/constants.js'; // Hash utilities diff --git a/common/package.json b/common/package.json index d67130bb7..17a884401 100644 --- a/common/package.json +++ b/common/package.json @@ -661,6 +661,7 @@ }, "dependencies": { "@anon-aadhaar/core": "npm:@selfxyz/anon-aadhaar-core@^0.0.1", + "@noble/hashes": "^1.5.0", "@openpassport/zk-kit-imt": "^0.0.5", "@openpassport/zk-kit-lean-imt": "^0.0.6", "@openpassport/zk-kit-smt": "^0.0.1", diff --git a/common/src/polyfills/crypto.ts b/common/src/polyfills/crypto.ts new file mode 100644 index 000000000..ceea9fc57 --- /dev/null +++ b/common/src/polyfills/crypto.ts @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +/** + * Crypto polyfill using @noble/hashes for web builds + * This replaces crypto-browserify with a more modern and secure implementation + */ + +import { hmac } from '@noble/hashes/hmac'; +import { md5 as nobleMd5 } from '@noble/hashes/legacy'; +import { pbkdf2 as noblePbkdf2 } from '@noble/hashes/pbkdf2'; +import { sha1 as nobleSha1 } from '@noble/hashes/sha1'; +import { sha256 as nobleSha256 } from '@noble/hashes/sha256'; +import { sha512 as nobleSha512 } from '@noble/hashes/sha512'; + +// Create hash instances that mimic Node.js crypto API +function createHash(algorithm: string) { + const alg = algorithm.toLowerCase(); + + let hasher: any; + + switch (alg) { + case 'sha1': + hasher = nobleSha1.create(); + break; + case 'sha256': + hasher = nobleSha256.create(); + break; + case 'sha512': + hasher = nobleSha512.create(); + break; + case 'md5': + hasher = nobleMd5.create(); + break; + default: + throw new Error(`Unsupported hash algorithm: ${algorithm}`); + } + + let finalized = false; + + return { + update(data: string | Uint8Array) { + if (finalized) { + throw new Error('Cannot update after calling digest(). Hash instance has been finalized.'); + } + if (typeof data === 'string') { + hasher.update(new TextEncoder().encode(data)); + } else { + hasher.update(data); + } + return this; + }, + digest(encoding?: BufferEncoding) { + if (finalized) { + throw new Error('Digest already called. Hash instance has been finalized.'); + } + finalized = true; + const result = hasher.digest(); + + if (encoding === 'hex') { + return Array.from(result) + .map((b: number) => b.toString(16).padStart(2, '0')) + .join(''); + } + return typeof Buffer !== 'undefined' ? Buffer.from(result) : result; + }, + }; +} + +function createHmac(algorithm: string, key: string | Uint8Array) { + const alg = algorithm.toLowerCase(); + + let hashFn: any; + + switch (alg) { + case 'sha1': + hashFn = nobleSha1; + break; + case 'sha256': + hashFn = nobleSha256; + break; + case 'sha512': + hashFn = nobleSha512; + break; + default: + throw new Error(`Unsupported HMAC algorithm: ${algorithm}`); + } + + const keyBytes = typeof key === 'string' ? new TextEncoder().encode(key) : key; + const hmacState = hmac.create(hashFn, keyBytes); + let finalized = false; + + return { + update(data: string | Uint8Array) { + if (finalized) { + throw new Error('Cannot update after calling digest(). Hash instance has been finalized.'); + } + const dataBytes = typeof data === 'string' ? new TextEncoder().encode(data) : data; + hmacState.update(dataBytes); + return this; + }, + digest(encoding?: BufferEncoding) { + if (finalized) { + throw new Error('Digest already called. Hash instance has been finalized.'); + } + finalized = true; + const result = hmacState.digest(); + + if (encoding === 'hex') { + return Array.from(result) + .map((b: number) => b.toString(16).padStart(2, '0')) + .join(''); + } + return typeof Buffer !== 'undefined' ? Buffer.from(result) : result; + }, + }; +} + +function randomBytes(size: number): Uint8Array | Buffer { + if (!Number.isInteger(size) || size <= 0) { + throw new Error('randomBytes size must be a positive integer'); + } + const cryptoObj = globalThis.crypto; + if (typeof cryptoObj?.getRandomValues !== 'function') { + throw new Error('globalThis.crypto.getRandomValues is not available'); + } + const out = new Uint8Array(size); + const MAX = 65536; // WebCrypto limit per call + for (let offset = 0; offset < size; offset += MAX) { + cryptoObj.getRandomValues(out.subarray(offset, Math.min(offset + MAX, size))); + } + return typeof Buffer !== 'undefined' ? Buffer.from(out) : out; +} + +function pbkdf2Sync( + password: string | Uint8Array, + salt: string | Uint8Array, + iterations: number, + keylen: number, + digest: string +): Uint8Array | Buffer { + const passwordBytes = + typeof password === 'string' ? new TextEncoder().encode(password) : password; + const saltBytes = typeof salt === 'string' ? new TextEncoder().encode(salt) : salt; + + let hashFn: any; + switch (digest.toLowerCase()) { + case 'sha1': + hashFn = nobleSha1; + break; + case 'sha256': + hashFn = nobleSha256; + break; + case 'sha512': + hashFn = nobleSha512; + break; + default: + throw new Error(`Unsupported PBKDF2 digest: ${digest}`); + } + + const derivedKey = noblePbkdf2(hashFn, passwordBytes, saltBytes, { + c: iterations, + dkLen: keylen, + }); + return typeof Buffer !== 'undefined' ? Buffer.from(derivedKey) : derivedKey; +} + +// Export crypto-like interface +export default { + createHash, + createHmac, + randomBytes, + pbkdf2Sync, +}; + +export { createHash, createHmac, pbkdf2Sync, randomBytes }; diff --git a/common/src/types/globals.d.ts b/common/src/types/globals.d.ts new file mode 100644 index 000000000..bf68989ed --- /dev/null +++ b/common/src/types/globals.d.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +declare global { + /** + * Development mode flag + * Set to true when running in development mode, false in production + */ + const __DEV__: boolean; +} + +export {}; diff --git a/common/src/types/index.ts b/common/src/types/index.ts index 84f11c052..3de702c3c 100644 --- a/common/src/types/index.ts +++ b/common/src/types/index.ts @@ -1,3 +1,9 @@ -export type { DocumentCategory, DocumentType, PassportData } from '../utils/types.js'; +export type { + DocumentCatalog, + DocumentCategory, + DocumentMetadata, + DocumentType, + PassportData, +} from '../utils/types.js'; export type { PassportMetadata } from '../utils/passports/passport_parsing/parsePassportData.js'; export type { UserIdType } from '../utils/circuits/uuid.js'; diff --git a/common/src/utils/bytes.ts b/common/src/utils/bytes.ts index ffd9deeca..850ca3e95 100644 --- a/common/src/utils/bytes.ts +++ b/common/src/utils/bytes.ts @@ -91,7 +91,7 @@ export function num2Bits(n: number, inValue: bigint): bigint[] { return out; } -export function packBytes(unpacked) { +export function packBytes(unpacked: number[]) { const bytesCount = [31, 31, 31]; const packed = [0n, 0n, 0n]; diff --git a/common/src/utils/certificate_parsing/curves.ts b/common/src/utils/certificate_parsing/curves.ts index 283ae9fb0..b572be4da 100644 --- a/common/src/utils/certificate_parsing/curves.ts +++ b/common/src/utils/certificate_parsing/curves.ts @@ -9,7 +9,7 @@ export interface StandardCurve { } export function getCurveForElliptic(curveName: string): string { - const curves = { + const curves: Record = { secp224r1: 'p224', secp256r1: 'p256', secp384r1: 'p384', diff --git a/common/src/utils/index.ts b/common/src/utils/index.ts index f152e13c7..c7fb6220e 100644 --- a/common/src/utils/index.ts +++ b/common/src/utils/index.ts @@ -1,21 +1,24 @@ -export type { AadhaarData, DocumentCategory, PassportData } from './types.js'; +export type { + AadhaarData, + DeployedCircuits, + DocumentCatalog, + DocumentCategory, + DocumentMetadata, + IDDocument, + OfacTree, + PassportData, +} from './types.js'; export type { CertificateData, PublicKeyDetailsECDSA, PublicKeyDetailsRSA, } from './certificate_parsing/dataStructure.js'; +export type { EndpointType, Mode, SelfApp, SelfAppDisclosureConfig } from './appType.js'; export type { IdDocInput } from './passports/genMockIdDoc.js'; export type { PassportMetadata } from './passports/passport_parsing/parsePassportData.js'; export type { TEEPayload, TEEPayloadBase, TEEPayloadDisclose } from './proving.js'; export type { UserIdType } from './circuits/uuid.js'; -export { - EndpointType, - Mode, - SelfApp, - SelfAppBuilder, - SelfAppDisclosureConfig, - getUniversalLink, -} from './appType.js'; +export { SelfAppBuilder, getUniversalLink } from './appType.js'; export { bigIntToString, formatEndpoint, hashEndpointWithScope, stringToBigInt } from './scope.js'; export { brutforceSignatureAlgorithmDsc } from './passports/passport_parsing/brutForceDscSignature.js'; export { buildSMT, getLeafCscaTree, getLeafDscTree } from './trees.js'; @@ -27,7 +30,6 @@ export { inferDocumentCategory, initPassportDataParsing, } from './passports/passport.js'; -export { isAadhaarDocument, isMRZDocument } from './types.js'; export { calculateUserIdentifierHash, customHasher, @@ -47,6 +49,7 @@ export { } from './proving.js'; export { extractQRDataFields, getAadharRegistrationWindow } from './aadhaar/utils.js'; export { formatMrz } from './passports/format.js'; +export { fetchOfacTrees } from './ofac.js'; export { genAndInitMockPassportData } from './passports/genMockPassportData.js'; export { genMockIdDoc, @@ -62,9 +65,11 @@ export { export { generateTEEInputsAadhaarDisclose, generateTEEInputsAadhaarRegister, + generateTEEInputsDiscloseStateless, } from './circuits/registerInputs.js'; export { getCircuitNameFromPassportData } from './circuits/circuitsName.js'; export { getSKIPEM } from './csca.js'; export { initElliptic } from './certificate_parsing/elliptic.js'; +export { isAadhaarDocument, isMRZDocument } from './types.js'; export { parseCertificateSimple } from './certificate_parsing/parseCertificateSimple.js'; export { parseDscCertificateData } from './passports/passport_parsing/parseDscCertificateData.js'; diff --git a/common/tests/cryptoHash.test.ts b/common/tests/cryptoHash.test.ts new file mode 100644 index 000000000..996b4d1c9 --- /dev/null +++ b/common/tests/cryptoHash.test.ts @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import { createHash } from '../src/polyfills/crypto'; +import { sha256 } from '@noble/hashes/sha256'; + +describe('Hash Finalization', () => { + const message = 'Hello World!'; + + it('should throw on subsequent digest calls (Node.js parity)', () => { + const hashInstance = createHash('sha256'); + hashInstance.update(message); + + const digest1 = hashInstance.digest('hex'); + expect(typeof digest1).toBe('string'); + expect(digest1.length).toBe(64); // SHA256 hex is 64 chars + + // Subsequent digest calls should throw + expect(() => hashInstance.digest('hex')).toThrow(/Digest already called/); + expect(() => hashInstance.digest()).toThrow(/Digest already called/); + + // Updates should also throw after finalization + expect(() => hashInstance.update('more data')).toThrow(/Cannot update after calling digest/); + }); + + it('should produce correct hash before finalization', () => { + const hashInstance = createHash('sha256'); + hashInstance.update(message); + + // Expected result from @noble/hashes + const expected = sha256(new TextEncoder().encode(message)); + const expectedHex = Array.from(expected) + .map((b: number) => b.toString(16).padStart(2, '0')) + .join(''); + + // Test hex digest + const hexResult = hashInstance.digest('hex'); + expect(hexResult).toBe(expectedHex); + }); + + it('should produce correct binary digest before finalization', () => { + const hashInstance = createHash('sha256'); + hashInstance.update(message); + + // Expected result from @noble/hashes + const expected = sha256(new TextEncoder().encode(message)); + + // Test binary digest + const binaryResult = hashInstance.digest(); + // Convert to Uint8Array for comparison (handles both Buffer and Uint8Array cases) + const resultArray = new Uint8Array(binaryResult); + + expect(resultArray).toEqual(expected); + }); +}); diff --git a/common/tests/cryptoHmac.test.ts b/common/tests/cryptoHmac.test.ts new file mode 100644 index 000000000..200764927 --- /dev/null +++ b/common/tests/cryptoHmac.test.ts @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import { createHmac } from '../src/polyfills/crypto'; +import { hmac } from '@noble/hashes/hmac'; +import { sha256 } from '@noble/hashes/sha256'; + +describe('HMAC Streaming', () => { + const key = 'test-key'; + const message1 = 'Hello '; + const message2 = 'World!'; + const fullMessage = message1 + message2; + + it('should produce the same result for single vs multiple update calls', () => { + // Expected result: compute HMAC of full message in one go + const expected = hmac( + sha256, + new TextEncoder().encode(key), + new TextEncoder().encode(fullMessage) + ); + const expectedHex = Array.from(expected) + .map((b: number) => b.toString(16).padStart(2, '0')) + .join(''); + + // Single update call + const hmacSingle = createHmac('sha256', key); + hmacSingle.update(fullMessage); + const singleResult = hmacSingle.digest('hex'); + + // Multiple update calls (this should be equivalent but currently fails) + const hmacMultiple = createHmac('sha256', key); + hmacMultiple.update(message1); + hmacMultiple.update(message2); + const multipleResult = hmacMultiple.digest('hex'); + + // These should all be equal + expect(singleResult).toBe(expectedHex); + expect(multipleResult).toBe(expectedHex); + expect(singleResult).toBe(multipleResult); + }); + + it('should handle binary data correctly with streaming', () => { + const chunk1 = new Uint8Array([1, 2, 3]); + const chunk2 = new Uint8Array([4, 5, 6]); + const fullData = new Uint8Array([1, 2, 3, 4, 5, 6]); + + // Expected result + const expected = hmac(sha256, new TextEncoder().encode(key), fullData); + + // Single update + const hmacSingle = createHmac('sha256', key); + hmacSingle.update(fullData); + const singleResult = hmacSingle.digest(); + + // Multiple updates + const hmacMultiple = createHmac('sha256', key); + hmacMultiple.update(chunk1); + hmacMultiple.update(chunk2); + const multipleResult = hmacMultiple.digest(); + + // Convert to Uint8Array for comparison (handles both Buffer and Uint8Array cases) + const singleArray = new Uint8Array(singleResult); + const multipleArray = new Uint8Array(multipleResult); + + expect(singleArray).toEqual(expected); + expect(multipleArray).toEqual(expected); + expect(singleArray).toEqual(multipleArray); + }); + + it('should throw on subsequent digest calls (Node.js parity)', () => { + const hmacInstance = createHmac('sha256', key); + hmacInstance.update(fullMessage); + + const digest1 = hmacInstance.digest('hex'); + expect(typeof digest1).toBe('string'); + expect(digest1.length).toBe(64); // SHA256 hex is 64 chars + + // Subsequent digest calls should throw + expect(() => hmacInstance.digest('hex')).toThrow(); + expect(() => hmacInstance.digest()).toThrow(); + + // Updates should also throw after finalization + expect(() => hmacInstance.update('more data')).toThrow(); + }); +}); diff --git a/package.json b/package.json index 5ea4bfeaf..5d4f3e921 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,23 @@ { "name": "self-workspace-root", - "workspaces": [ - "app", - "circuits", - "common", - "contracts", - "packages/*", - "prover/tests", - "sdk/*", - "scripts/tests", - "packages/mobile-sdk-alpha/demo-app" - ], + "workspaces": { + "packages": [ + "app", + "circuits", + "common", + "contracts", + "packages/*", + "prover/tests", + "scripts/tests", + "sdk/*" + ] + }, "scripts": { - "build": "yarn workspaces foreach --topological-dev --parallel --exclude @selfxyz/contracts -i --all run build", + "build": "yarn workspaces foreach --topological-dev --parallel --exclude @selfxyz/contracts --exclude @selfxyz/circuits --exclude mobile-sdk-demo -i --all run build", + "build:demo": "yarn workspace mobile-sdk-demo build", + "build:mobile-sdk": "yarn workspace @selfxyz/mobile-sdk-alpha build", "check:versions": "node scripts/check-package-versions.mjs", + "demo:mobile": "yarn build:mobile-sdk && yarn build:demo && yarn workspace mobile-sdk-demo start", "format": "SKIP_BUILD_DEPS=1 yarn format:root && yarn format:github && SKIP_BUILD_DEPS=1 yarn workspaces foreach --parallel -i --all --exclude self-workspace-root run format", "format:github": "yarn prettier --parser yaml --write .github/**/*.yml --single-quote false", "format:root": "echo 'format markdown' && yarn prettier --parser markdown --write *.md && echo 'format yaml' && yarn prettier --parser yaml --write .*.{yml,yaml} --single-quote false && yarn prettier --write scripts/**/*.{js,mjs,ts} && yarn prettier --parser json --write scripts/**/*.json", @@ -23,11 +27,11 @@ "lint:headers": "node scripts/check-duplicate-headers.cjs . && node scripts/check-license-headers.mjs . --check", "lint:headers:fix": "node scripts/check-duplicate-headers.cjs . && node scripts/check-license-headers.mjs . --fix", "prepare": "husky", - "reinstall-app": "rm -rf node_modules app/node_modules && yarn install && yarn workspace @selfxyz/mobile-app run install-app", + "reinstall-app": "yarn install && (cd app && yarn clean:watchman && yarn clean:build && yarn clean:ios && yarn clean:xcode && yarn clean:pod-cache && yarn clean:android-deps && rm -rf app/node_modules) && yarn install && yarn workspace @selfxyz/mobile-app run install-app", "sort-package-jsons": "find . -name 'package.json' -not -path './node_modules/*' -not -path './*/node_modules/*' | xargs npx sort-package-json", "test": "yarn workspaces foreach --parallel -i --all run test", "test:license-headers": "cd scripts/tests && node check-license-headers.test.mjs", - "types": "yarn workspaces foreach --topological-dev --parallel --exclude @selfxyz/contracts -i --all run types " + "types": "yarn workspaces foreach --topological-dev --parallel --exclude @selfxyz/contracts --exclude @selfxyz/common --exclude @selfxyz/mobile-app -i --all run types" }, "resolutions": { "@babel/core": "^7.28.4", @@ -38,9 +42,17 @@ "@types/react": "^18.3.4", "punycode": "npm:punycode.js@^2.3.1", "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-native": "0.76.9" + }, + "dependencies": { + "@babel/runtime": "^7.28.3", + "js-sha1": "^0.7.0", + "react": "^18.3.1", "react-native": "0.76.9" }, "devDependencies": { + "@react-native-community/cli-server-api": "^16.0.3", "@types/node": "^22.18.3", "gitleaks": "1.0.0", "husky": "9.1.7", diff --git a/packages/mobile-sdk-alpha/.eslintignore b/packages/mobile-sdk-alpha/.eslintignore index 73bac7a1a..e69de29bb 100644 --- a/packages/mobile-sdk-alpha/.eslintignore +++ b/packages/mobile-sdk-alpha/.eslintignore @@ -1 +0,0 @@ -demo-app/** diff --git a/packages/mobile-sdk-alpha/.eslintrc.cjs b/packages/mobile-sdk-alpha/.eslintrc.cjs index abd9cf6a3..33612a6f2 100644 --- a/packages/mobile-sdk-alpha/.eslintrc.cjs +++ b/packages/mobile-sdk-alpha/.eslintrc.cjs @@ -8,11 +8,13 @@ module.exports = { parserOptions: { ecmaVersion: 2021, sourceType: 'module', - ecmaFeatures: { jsx: false }, + ecmaFeatures: { jsx: true }, }, extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', 'plugin:import/recommended', 'plugin:import/typescript', 'plugin:prettier/recommended', @@ -20,6 +22,9 @@ module.exports = { plugins: ['simple-import-sort', 'import', 'sort-exports'], ignorePatterns: ['dist/', 'node_modules/'], settings: { + react: { + version: 'detect', + }, 'import/resolver': { typescript: { alwaysTryTypes: true, @@ -67,6 +72,8 @@ module.exports = { '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], // Add prettier rule to show prettier errors as ESLint errors 'prettier/prettier': ['warn', {}, { usePrettierrc: true }], + // Disable prop-types for TypeScript files since we use TypeScript types + 'react/prop-types': 'off', }, overrides: [ { diff --git a/packages/mobile-sdk-alpha/.prettierignore b/packages/mobile-sdk-alpha/.prettierignore index 2516c633d..de4d1f007 100644 --- a/packages/mobile-sdk-alpha/.prettierignore +++ b/packages/mobile-sdk-alpha/.prettierignore @@ -1,4 +1,2 @@ dist node_modules -demo-app/android -demo-app/ios diff --git a/packages/mobile-sdk-alpha/README.md b/packages/mobile-sdk-alpha/README.md index 4f71acac7..d393e2a17 100644 --- a/packages/mobile-sdk-alpha/README.md +++ b/packages/mobile-sdk-alpha/README.md @@ -109,22 +109,3 @@ describe('Real mobile-sdk-alpha Integration', () => { - `npm run validate:exports` — ensure named exports only. - `npm run validate:pkg` — check packaging and export conditions. - `npm run report:exports` — output current public symbols. - -## Self Demo App - -The Self Demo App is a lightweight React Native playground in [`demo-app`](./demo-app). - -```bash -# start Metro bundler -yarn workspace demo-app start - -# type-check -yarn workspace demo-app build - -# run tests -yarn workspace demo-app test - -# or via package scripts -cd packages/mobile-sdk-alpha -yarn run:demo -``` diff --git a/packages/mobile-sdk-alpha/demo-app/App.tsx b/packages/mobile-sdk-alpha/demo-app/App.tsx deleted file mode 100644 index 64548fc90..000000000 --- a/packages/mobile-sdk-alpha/demo-app/App.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; - -function App() { - return ( - - Self Demo App - Register Document - Generate Mock - Prove QR Code - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#fff', - }, - title: { - fontSize: 20, - marginBottom: 12, - fontWeight: 'bold', - }, - item: { - fontSize: 16, - marginVertical: 4, - }, -}); - -export default App; diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/build.gradle b/packages/mobile-sdk-alpha/demo-app/android/app/build.gradle deleted file mode 100644 index 219d424fd..000000000 --- a/packages/mobile-sdk-alpha/demo-app/android/app/build.gradle +++ /dev/null @@ -1,119 +0,0 @@ -apply plugin: "com.android.application" -apply plugin: "org.jetbrains.kotlin.android" -apply plugin: "com.facebook.react" - -/** - * This is the configuration block to customize your React Native Android app. - * By default you don't need to apply any configuration, just uncomment the lines you need. - */ -react { - /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '../..' - root = file("../../../") - // The folder where the react-native NPM package is. Default is ../../node_modules/react-native - reactNativeDir = file("../../../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen - codegenDir = file("../../../node_modules/@react-native/codegen") - // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js - cliFile = file("../../../node_modules/react-native/cli.js") - - /* Variants */ - // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. By default is just 'debug'. - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] - - /* Bundling */ - // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] - // - // The command to run when bundling. By default is 'bundle' - // bundleCommand = "ram-bundle" - // - // The path to the CLI configuration file. Default is empty. - // bundleConfig = file(../rn-cli.config.js) - // - // The name of the generated asset file containing your JS bundle - // bundleAssetName = "MyApplication.android.bundle" - // - // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' - // entryFile = file("../js/MyApplication.android.js") - // - // A list of extra flags to pass to the 'bundle' commands. - // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle - // extraPackagerArgs = [] - - /* Hermes Commands */ - // The hermes compiler command to run. By default it is 'hermesc' - // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" - // - // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" - // hermesFlags = ["-O", "-output-source-map"] - - /* Autolinking */ - autolinkLibrariesWithApp() -} - -/** - * Set this to true to Run Proguard on Release builds to minify the Java bytecode. - */ -def enableProguardInReleaseBuilds = false - -/** - * The preferred build flavor of JavaScriptCore (JSC) - * - * For example, to use the international variant, you can use: - * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - */ -def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' - -android { - ndkVersion rootProject.ext.ndkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - compileSdk rootProject.ext.compileSdkVersion - - namespace "com.selfdemoapp" - defaultConfig { - applicationId "com.selfdemoapp" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" - } - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - buildTypes { - debug { - signingConfig signingConfigs.debug - } - release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig signingConfigs.debug - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - } - } -} - -dependencies { - // The version of react-native is set by the React Native Gradle Plugin - implementation("com.facebook.react:react-android") - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } -} diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainActivity.kt b/packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainActivity.kt deleted file mode 100644 index dafa2976f..000000000 --- a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainActivity.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.selfdemoapp - -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.ReactApplication -import com.facebook.react.ReactNativeHost -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate - -class MainActivity : ReactActivity() { - - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - override fun getMainComponentName(): String = "SelfDemoApp" - - /** - * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] - * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] - */ - override fun createReactActivityDelegate(): ReactActivityDelegate = - object : DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) { - override fun getReactNativeHost(): ReactNativeHost { - return (application as ReactApplication).reactNativeHost - } - } -} diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainApplication.kt b/packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainApplication.kt deleted file mode 100644 index 053ce3903..000000000 --- a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/java/com/selfdemoapp/MainApplication.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.selfdemoapp - -import android.app.Application -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint -import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.soloader.SoLoader -import com.facebook.react.soloader.OpenSourceMergedSoMapping - -class MainApplication : Application(), ReactApplication { - - override val reactNativeHost: ReactNativeHost = - object : DefaultReactNativeHost(this) { - override fun getPackages(): MutableList = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - } - - override fun getJSMainModuleName(): String = "index" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } - - override fun onCreate() { - super.onCreate() - // Initialize SoLoader with the merged mapping to support Hermes on RN 0.76+ - SoLoader.init(this, OpenSourceMergedSoMapping) - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - DefaultNewArchitectureEntryPoint.load() - } - } -} diff --git a/packages/mobile-sdk-alpha/demo-app/android/build.gradle b/packages/mobile-sdk-alpha/demo-app/android/build.gradle deleted file mode 100644 index 7092a7902..000000000 --- a/packages/mobile-sdk-alpha/demo-app/android/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -buildscript { - ext { - buildToolsVersion = "36.0.0" - minSdkVersion = 24 - compileSdkVersion = 36 - targetSdkVersion = 36 - ndkVersion = "27.0.12077973" - kotlinVersion = "2.1.20" - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.android.tools.build:gradle") - classpath("com.facebook.react:react-native-gradle-plugin") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") - } -} - -apply plugin: "com.facebook.react.rootproject" diff --git a/packages/mobile-sdk-alpha/demo-app/android/settings.gradle b/packages/mobile-sdk-alpha/demo-app/android/settings.gradle deleted file mode 100644 index fa197ee4d..000000000 --- a/packages/mobile-sdk-alpha/demo-app/android/settings.gradle +++ /dev/null @@ -1,5 +0,0 @@ -pluginManagement { includeBuild("/Volumes/files/Projects/selfxyz/selfapp/node_modules/@react-native/gradle-plugin") } -plugins { id("com.facebook.react.settings") } -extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } -rootProject.name = 'SelfDemoApp' -include ':app' diff --git a/packages/mobile-sdk-alpha/demo-app/metro.config.cjs b/packages/mobile-sdk-alpha/demo-app/metro.config.cjs deleted file mode 100644 index 60b0a1955..000000000 --- a/packages/mobile-sdk-alpha/demo-app/metro.config.cjs +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -const path = require('path'); -const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); - -const defaultConfig = getDefaultConfig(__dirname); - -const config = { - watchFolders: [ - path.resolve(__dirname, '../../..'), // monorepo root - ], - resolver: { - extraNodeModules: { - '@babel/runtime': path.resolve(__dirname, '../../../node_modules/@babel/runtime'), - // Pin React and React Native to monorepo root - react: path.resolve(__dirname, '../../../node_modules/react'), - 'react-native': path.resolve(__dirname, '../../../node_modules/react-native'), - // Crypto polyfills - stream: require.resolve('stream-browserify'), - buffer: require.resolve('buffer'), - util: require.resolve('util'), - assert: require.resolve('assert'), - }, - nodeModulesPaths: [path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, '../../../node_modules')], - }, -}; - -module.exports = mergeConfig(defaultConfig, config); diff --git a/packages/mobile-sdk-alpha/demo-app/package.json b/packages/mobile-sdk-alpha/demo-app/package.json deleted file mode 100644 index 181566bb9..000000000 --- a/packages/mobile-sdk-alpha/demo-app/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "demo-app", - "version": "0.0.1", - "private": true, - "main": "index.js", - "scripts": { - "android": "react-native run-android", - "build": "tsc -p tsconfig.json --noEmit --pretty false", - "ios": "react-native run-ios", - "start": "react-native start", - "test": "jest" - }, - "dependencies": { - "@babel/runtime": "^7.28.3", - "@noble/hashes": "^1.5.0", - "@react-native/gradle-plugin": "0.76.9", - "assert": "^2.1.0", - "buffer": "^6.0.3", - "ethers": "^6.11.0", - "react": "^18.3.1", - "react-native": "0.76.9", - "react-native-get-random-values": "^1.11.0", - "stream-browserify": "^3.0.0", - "util": "^0.12.5" - }, - "devDependencies": { - "@babel/core": "^7.28.3", - "@tsconfig/react-native": "^3.0.6", - "@types/jest": "^29.5.14", - "@types/react": "^18.3.4", - "babel-jest": "^29.6.3", - "jest": "^29.6.3", - "metro-react-native-babel-preset": "0.76.9", - "react-test-renderer": "^18.3.1", - "typescript": "^5.9.2" - } -} diff --git a/packages/mobile-sdk-alpha/package.json b/packages/mobile-sdk-alpha/package.json index 02a0dba92..33dba6a4e 100644 --- a/packages/mobile-sdk-alpha/package.json +++ b/packages/mobile-sdk-alpha/package.json @@ -14,16 +14,22 @@ "exports": { ".": { "types": "./dist/esm/index.d.ts", + "react-native": "./dist/esm/index.js", "browser": "./dist/esm/browser.js", "import": "./dist/esm/index.js", - "require": "./dist/cjs/index.cjs", - "default": "./dist/esm/index.js" + "require": "./dist/cjs/index.cjs" }, "./browser": { "types": "./dist/esm/browser.d.ts", "import": "./dist/esm/browser.js", "require": "./dist/cjs/browser.cjs" }, + "./constants": { + "types": "./dist/esm/constants/index.d.ts", + "react-native": "./dist/esm/constants/index.js", + "import": "./dist/esm/constants/index.js", + "require": "./dist/cjs/constants/index.cjs" + }, "./stores": { "types": "./dist/esm/stores.d.ts", "import": "./dist/esm/stores.js", @@ -31,12 +37,26 @@ }, "./constants/analytics": { "types": "./dist/esm/constants/analytics.d.ts", + "react-native": "./dist/esm/constants/analytics.js", "import": "./dist/esm/constants/analytics.js", "require": "./dist/cjs/constants/analytics.cjs" + }, + "./components": { + "types": "./dist/esm/components/index.d.ts", + "react-native": "./dist/esm/components/index.js", + "import": "./dist/esm/components/index.js", + "require": "./dist/cjs/components/index.cjs" + }, + "./hooks": { + "types": "./dist/esm/hooks/index.d.ts", + "react-native": "./dist/esm/hooks/index.js", + "import": "./dist/esm/hooks/index.js", + "require": "./dist/cjs/hooks/index.cjs" } }, "main": "./dist/cjs/index.cjs", "module": "./dist/esm/index.js", + "react-native": "./dist/esm/index.js", "types": "./dist/esm/index.d.ts", "files": [ "dist", @@ -48,12 +68,8 @@ "scripts": { "build": "rm -rf dist && tsup && yarn postbuild", "postbuild": "node ./scripts/postBuild.mjs", - "demo:android": "yarn workspace demo-app android", - "demo:ios": "yarn workspace demo-app ios", - "demo:start": "yarn workspace demo-app start", - "demo:test": "yarn workspace demo-app test", - "fmt": "yarn prettier --check .", - "fmt:fix": "yarn prettier --write .", + "fmt": "prettier --check .", + "fmt:fix": "prettier --write .", "format": "sh -c 'if [ -z \"$SKIP_BUILD_DEPS\" ]; then yarn nice; else yarn fmt:fix; fi'", "lint": "eslint .", "lint:fix": "eslint --fix .", @@ -62,20 +78,22 @@ "report:exports": "node ./scripts/report-exports.mjs", "test": "vitest run", "test:build": "yarn build && yarn test && yarn types && yarn lint", - "test:demo": "yarn workspace demo-app test", "typecheck": "tsc -p tsconfig.json --noEmit", "types": "tsc -p tsconfig.json --noEmit", "validate:exports": "node ./scripts/validate-exports.mjs", "validate:pkg": "node ./scripts/verify-conditions.mjs" }, "dependencies": { + "@babel/runtime": "^7.28.3", "@selfxyz/common": "workspace:^", "socket.io-client": "^4.8.1", "zustand": "^4.5.2" }, "devDependencies": { + "@tamagui/types": "1.126.14", "@testing-library/react": "^14.1.2", "@types/react": "^18.3.4", + "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", "eslint": "^8.57.0", @@ -83,12 +101,16 @@ "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.31.0", "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-react": "^7.37.5", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-sort-exports": "^0.9.1", "jsdom": "^24.0.0", "prettier": "^3.5.3", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-native": "0.76.9", + "react-native-web": "^0.21.1", + "tamagui": "1.126.14", "tsup": "^8.0.1", "typescript": "^5.9.2", "vitest": "^1.6.0" @@ -96,7 +118,12 @@ "peerDependencies": { "react": "^18.3.1", "react-native": "0.76.9", - "tamagui": "^1.126.0" + "tamagui": "1.126.x" + }, + "peerDependenciesMeta": { + "tamagui": { + "optional": false + } }, "packageManager": "yarn@4.6.0", "publishConfig": { diff --git a/packages/mobile-sdk-alpha/src/browser.ts b/packages/mobile-sdk-alpha/src/browser.ts index 4c2991070..1dbeffc1e 100644 --- a/packages/mobile-sdk-alpha/src/browser.ts +++ b/packages/mobile-sdk-alpha/src/browser.ts @@ -57,7 +57,7 @@ export { extractMRZInfo, formatDateToYYMMDD, scanMRZ } from './mrz'; export { generateMockDocument, signatureAlgorithmToStrictSignatureAlgorithm } from './mock/generator'; -export { generateTEEInputsDisclose } from './processing/generate-disclosure-inputs'; +export { generateTEEInputsDisclose } from './processing/generateDisclosureInputs'; // Core functions export { isPassportDataValid } from './validation/document'; diff --git a/packages/mobile-sdk-alpha/src/client.ts b/packages/mobile-sdk-alpha/src/client.ts index 51f4d3d22..a1d5b6ef0 100644 --- a/packages/mobile-sdk-alpha/src/client.ts +++ b/packages/mobile-sdk-alpha/src/client.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import type { DocumentCatalog, PassportData } from '@selfxyz/common/utils/types'; +import type { DocumentCatalog, PassportData } from '@selfxyz/common'; import { defaultConfig } from './config/defaults'; import { mergeConfig } from './config/merge'; diff --git a/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx index eb5a0b81d..befd88093 100644 --- a/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx +++ b/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx @@ -3,10 +3,10 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import { useCallback } from 'react'; -import { Button, Text, YStack } from 'tamagui'; +import { StyleSheet, Text, TouchableOpacity } from 'react-native'; +import { View } from 'tamagui'; -import { getSKIPEM } from '@selfxyz/common/utils/csca'; -import { initPassportDataParsing } from '@selfxyz/common/utils/passports'; +import { getSKIPEM, initPassportDataParsing } from '@selfxyz/common'; import { useSelfClient } from '../../context'; import { MRZInfo, ScanResultNFC } from '../../types/public'; @@ -40,11 +40,38 @@ export const NFCScannerScreen = ({ onSuccess, onFailure, mrzData }: ScreenProps ); return ( - - - NFC Scanner - - - + + NFC Scanner + onNFCScan({})}> + Simulate NFC Scan + + ); }; + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16, + justifyContent: 'center', + alignItems: 'center', + gap: 16, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + textAlign: 'center', + marginBottom: 16, + }, + button: { + backgroundColor: '#007AFF', + paddingHorizontal: 24, + paddingVertical: 12, + borderRadius: 8, + }, + buttonText: { + color: 'white', + fontSize: 16, + fontWeight: '600', + }, +}); diff --git a/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx index 6c5f4d5a7..5e78c36b6 100644 --- a/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx +++ b/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx @@ -2,20 +2,21 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { Button, Text, YStack } from 'tamagui'; +import { StyleSheet, Text, TouchableOpacity } from 'react-native'; +import { View } from 'tamagui'; import type { PassportCameraProps } from '../../types/ui'; import { MRZScannerView } from '../MRZScannerView'; // Simple placeholder component - this would be replaced with actual camera UI export const PassportCameraScreen = ({ onMRZDetected }: PassportCameraProps) => ( - - - Passport Camera - + + Passport Camera - - + Simulate MRZ Detection + + ); + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16, + justifyContent: 'center', + alignItems: 'center', + gap: 16, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + textAlign: 'center', + marginBottom: 16, + }, + button: { + backgroundColor: '#007AFF', + paddingHorizontal: 24, + paddingVertical: 12, + borderRadius: 8, + }, + buttonText: { + color: 'white', + fontSize: 16, + fontWeight: '600', + }, +}); diff --git a/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx index c0e7b2514..1320a0ab9 100644 --- a/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx +++ b/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx @@ -2,18 +2,59 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { Button, Text, YStack } from 'tamagui'; +import { StyleSheet, Text, TouchableOpacity } from 'react-native'; +import { View } from 'tamagui'; import type { ScreenProps } from '../../types/ui'; export const QRCodeScreen = ({ onSuccess, onFailure }: ScreenProps) => ( - - - QR Code Scanner - - - - + + QR Code Scanner + + Simulate Success + + onFailure(new Error('QR scan failed'))}> + Simulate Failure + + ); + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16, + justifyContent: 'center', + alignItems: 'center', + gap: 16, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + textAlign: 'center', + marginBottom: 16, + }, + button: { + backgroundColor: '#007AFF', + paddingHorizontal: 24, + paddingVertical: 12, + borderRadius: 8, + }, + buttonText: { + color: 'white', + fontSize: 16, + fontWeight: '600', + }, + outlinedButton: { + backgroundColor: 'transparent', + borderWidth: 1, + borderColor: '#007AFF', + paddingHorizontal: 24, + paddingVertical: 12, + borderRadius: 8, + }, + outlinedButtonText: { + color: '#007AFF', + fontSize: 16, + fontWeight: '600', + }, +}); diff --git a/packages/mobile-sdk-alpha/src/documents/utils.ts b/packages/mobile-sdk-alpha/src/documents/utils.ts index 64228717d..6c7e56c36 100644 --- a/packages/mobile-sdk-alpha/src/documents/utils.ts +++ b/packages/mobile-sdk-alpha/src/documents/utils.ts @@ -2,16 +2,20 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { +import type { AadhaarData, - brutforceSignatureAlgorithmDsc, - isMRZDocument, - parseCertificateSimple, + DocumentMetadata, + IDDocument, PublicKeyDetailsECDSA, PublicKeyDetailsRSA, } from '@selfxyz/common'; -import { calculateContentHash, inferDocumentCategory } from '@selfxyz/common/utils'; -import { DocumentMetadata, IDDocument } from '@selfxyz/common/utils/types'; +import { + brutforceSignatureAlgorithmDsc, + calculateContentHash, + inferDocumentCategory, + isMRZDocument, + parseCertificateSimple, +} from '@selfxyz/common'; import { SelfClient } from '../types/public'; diff --git a/packages/mobile-sdk-alpha/src/index.ts b/packages/mobile-sdk-alpha/src/index.ts index 0af419d6a..1843fea7a 100644 --- a/packages/mobile-sdk-alpha/src/index.ts +++ b/packages/mobile-sdk-alpha/src/index.ts @@ -56,11 +56,9 @@ export { sdkError, } from './errors'; +// Screen Components (React Native-based) export { NFCScannerScreen } from './components/screens/NFCScannerScreen'; - -// Screen Components export { PassportCameraScreen } from './components/screens/PassportCameraScreen'; - export { QRCodeScreen } from './components/screens/QRCodeScreen'; export { SdkEvents } from './types/events'; @@ -92,10 +90,9 @@ export { formatDateToYYMMDD, scanMRZ } from './mrz'; export { generateMockDocument, signatureAlgorithmToStrictSignatureAlgorithm } from './mock/generator'; -export { generateTEEInputsDisclose } from './processing/generate-disclosure-inputs'; +export { generateTEEInputsDisclose } from './processing/generateDisclosureInputs'; // Documents utils - // Core functions export { isPassportDataValid } from './validation/document'; diff --git a/packages/mobile-sdk-alpha/src/mock/generator.ts b/packages/mobile-sdk-alpha/src/mock/generator.ts index d9ba4c71f..b6aedc44d 100644 --- a/packages/mobile-sdk-alpha/src/mock/generator.ts +++ b/packages/mobile-sdk-alpha/src/mock/generator.ts @@ -2,9 +2,8 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import type { IdDocInput, PassportData } from '@selfxyz/common/utils'; -import { getSKIPEM } from '@selfxyz/common/utils/csca'; -import { generateMockDSC, genMockIdDoc, initPassportDataParsing } from '@selfxyz/common/utils/passports'; +import type { IdDocInput, PassportData } from '@selfxyz/common'; +import { generateMockDSC, genMockIdDoc, getSKIPEM, initPassportDataParsing } from '@selfxyz/common'; export interface GenerateMockDocumentOptions { age: number; diff --git a/packages/mobile-sdk-alpha/src/processing/generate-disclosure-inputs.ts b/packages/mobile-sdk-alpha/src/processing/generateDisclosureInputs.ts similarity index 86% rename from packages/mobile-sdk-alpha/src/processing/generate-disclosure-inputs.ts rename to packages/mobile-sdk-alpha/src/processing/generateDisclosureInputs.ts index 24c421a9a..a653e75e6 100644 --- a/packages/mobile-sdk-alpha/src/processing/generate-disclosure-inputs.ts +++ b/packages/mobile-sdk-alpha/src/processing/generateDisclosureInputs.ts @@ -2,9 +2,8 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import type { DocumentCategory, PassportData } from '@selfxyz/common/types'; -import type { SelfApp } from '@selfxyz/common/utils'; -import { generateTEEInputsDiscloseStateless } from '@selfxyz/common/utils/circuits/registerInputs'; +import type { DocumentCategory, PassportData, SelfApp } from '@selfxyz/common'; +import { generateTEEInputsDiscloseStateless } from '@selfxyz/common'; import { useProtocolStore } from '../stores/protocolStore'; diff --git a/packages/mobile-sdk-alpha/src/stores/protocolStore.ts b/packages/mobile-sdk-alpha/src/stores/protocolStore.ts index 580655193..a82b754ad 100644 --- a/packages/mobile-sdk-alpha/src/stores/protocolStore.ts +++ b/packages/mobile-sdk-alpha/src/stores/protocolStore.ts @@ -4,6 +4,7 @@ import { create } from 'zustand'; +import type { DeployedCircuits, OfacTree } from '@selfxyz/common'; import { API_URL, API_URL_STAGING, @@ -15,15 +16,14 @@ import { DSC_TREE_URL_ID_CARD, DSC_TREE_URL_STAGING, DSC_TREE_URL_STAGING_ID_CARD, + fetchOfacTrees, IDENTITY_TREE_URL, IDENTITY_TREE_URL_ID_CARD, IDENTITY_TREE_URL_STAGING, IDENTITY_TREE_URL_STAGING_ID_CARD, TREE_URL, TREE_URL_STAGING, -} from '@selfxyz/common/constants'; -import { fetchOfacTrees } from '@selfxyz/common/utils/ofac'; -import type { DeployedCircuits, OfacTree } from '@selfxyz/common/utils/types'; +} from '@selfxyz/common'; interface ProtocolState { passport: { diff --git a/packages/mobile-sdk-alpha/src/stores/selfAppStore.tsx b/packages/mobile-sdk-alpha/src/stores/selfAppStore.tsx index 379ee87ad..3aa31674e 100644 --- a/packages/mobile-sdk-alpha/src/stores/selfAppStore.tsx +++ b/packages/mobile-sdk-alpha/src/stores/selfAppStore.tsx @@ -6,8 +6,8 @@ import type { Socket } from 'socket.io-client'; import socketIo from 'socket.io-client'; import { create } from 'zustand'; -import { WS_DB_RELAYER } from '@selfxyz/common/constants'; -import type { SelfApp } from '@selfxyz/common/utils/appType'; +import type { SelfApp } from '@selfxyz/common'; +import { WS_DB_RELAYER } from '@selfxyz/common'; interface SelfAppState { selfApp: SelfApp | null; diff --git a/packages/mobile-sdk-alpha/src/types/events.ts b/packages/mobile-sdk-alpha/src/types/events.ts index 8c6d2921d..9cdb33a46 100644 --- a/packages/mobile-sdk-alpha/src/types/events.ts +++ b/packages/mobile-sdk-alpha/src/types/events.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { DocumentCategory } from '@selfxyz/common/types'; +import { DocumentCategory } from '@selfxyz/common'; import type { Progress } from './public'; diff --git a/packages/mobile-sdk-alpha/src/types/public.ts b/packages/mobile-sdk-alpha/src/types/public.ts index 0fc120c4b..6dfc53f6a 100644 --- a/packages/mobile-sdk-alpha/src/types/public.ts +++ b/packages/mobile-sdk-alpha/src/types/public.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import type { DocumentCatalog, IDDocument, PassportData } from '@selfxyz/common/utils/types'; +import type { DocumentCatalog, IDDocument, PassportData } from '@selfxyz/common'; import { SDKEvent, SDKEventMap } from './events'; diff --git a/packages/mobile-sdk-alpha/src/validation/document.ts b/packages/mobile-sdk-alpha/src/validation/document.ts index 04015536c..2f6041da5 100644 --- a/packages/mobile-sdk-alpha/src/validation/document.ts +++ b/packages/mobile-sdk-alpha/src/validation/document.ts @@ -2,9 +2,8 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { hash } from '@selfxyz/common/utils/hash/sha'; -import { formatMrz } from '@selfxyz/common/utils/passportFormat'; -import type { PassportData } from '@selfxyz/common/utils/types'; +import type { PassportData } from '@selfxyz/common'; +import { formatMrz, hash } from '@selfxyz/common'; /** * Checks if two numeric arrays contain the same values in the same order. diff --git a/packages/mobile-sdk-alpha/tests/client-mrz.test.ts b/packages/mobile-sdk-alpha/tests/clientMrz.test.ts similarity index 100% rename from packages/mobile-sdk-alpha/tests/client-mrz.test.ts rename to packages/mobile-sdk-alpha/tests/clientMrz.test.ts diff --git a/packages/mobile-sdk-alpha/tests/documents/utils.test.ts b/packages/mobile-sdk-alpha/tests/documents/utils.test.ts index 9c4d6eb97..a6da43a89 100644 --- a/packages/mobile-sdk-alpha/tests/documents/utils.test.ts +++ b/packages/mobile-sdk-alpha/tests/documents/utils.test.ts @@ -4,7 +4,8 @@ import { describe, expect, it } from 'vitest'; -import { DocumentCatalog, PassportData } from '@selfxyz/common/utils/types'; +import type { DocumentCatalog } from '@selfxyz/common/types'; +import type { PassportData } from '@selfxyz/common/types/passport'; import { createSelfClient, defaultConfig, DocumentsAdapter, loadSelectedDocument, SelfClient } from '../../src'; diff --git a/packages/mobile-sdk-alpha/tests/entry.test.tsx b/packages/mobile-sdk-alpha/tests/entry.test.tsx index 939127cd5..83c4ab747 100644 --- a/packages/mobile-sdk-alpha/tests/entry.test.tsx +++ b/packages/mobile-sdk-alpha/tests/entry.test.tsx @@ -3,7 +3,6 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. /* @vitest-environment jsdom */ -import React from 'react'; import { describe, expect, it } from 'vitest'; import { SelfMobileSdk, useSelfClient } from '../src/index'; diff --git a/packages/mobile-sdk-alpha/tests/mock/generator.test.ts b/packages/mobile-sdk-alpha/tests/mock/generator.test.ts index 6f88a8223..71d690826 100644 --- a/packages/mobile-sdk-alpha/tests/mock/generator.test.ts +++ b/packages/mobile-sdk-alpha/tests/mock/generator.test.ts @@ -6,14 +6,11 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { generateMockDocument, signatureAlgorithmToStrictSignatureAlgorithm } from '../../src/mock/generator'; -// Mock the external dependencies -vi.mock('@selfxyz/common/utils/csca', () => ({ - getSKIPEM: vi.fn(), -})); - -vi.mock('@selfxyz/common/utils/passports', () => ({ +// Mock the @selfxyz/common module to match the actual import path used in generator.ts +vi.mock('@selfxyz/common', () => ({ generateMockDSC: vi.fn(), genMockIdDoc: vi.fn(), + getSKIPEM: vi.fn(), initPassportDataParsing: vi.fn(), })); @@ -80,13 +77,8 @@ describe('generateMockDocument', () => { beforeEach(async () => { vi.clearAllMocks(); - // Import the mocked functions - const csca = await import('@selfxyz/common/utils/csca'); - const passports = await import('@selfxyz/common/utils/passports'); - getSKIPEM = csca.getSKIPEM; - generateMockDSC = passports.generateMockDSC; - genMockIdDoc = passports.genMockIdDoc; - initPassportDataParsing = passports.initPassportDataParsing; + // Import the mocked functions from the same path used by the implementation + ({ getSKIPEM, generateMockDSC, genMockIdDoc, initPassportDataParsing } = await import('@selfxyz/common')); // Setup default mocks with proper types vi.mocked(getSKIPEM).mockResolvedValue({ 'mock-key': 'mock-ski-pem' }); @@ -402,13 +394,8 @@ describe('generateMockDocument integration', () => { beforeEach(async () => { vi.clearAllMocks(); - // Import the mocked functions - const csca = await import('@selfxyz/common/utils/csca'); - const passports = await import('@selfxyz/common/utils/passports'); - getSKIPEM = csca.getSKIPEM; - generateMockDSC = passports.generateMockDSC; - genMockIdDoc = passports.genMockIdDoc; - initPassportDataParsing = passports.initPassportDataParsing; + // Import the mocked functions from the same path used by the implementation + ({ getSKIPEM, generateMockDSC, genMockIdDoc, initPassportDataParsing } = await import('@selfxyz/common')); // Setup default mocks with proper types vi.mocked(getSKIPEM).mockResolvedValue({ 'mock-key': 'mock-ski-pem' }); diff --git a/packages/mobile-sdk-alpha/tests/processing/generate-disclosure-inputs.test.ts b/packages/mobile-sdk-alpha/tests/processing/generateDisclosureInputs.test.ts similarity index 99% rename from packages/mobile-sdk-alpha/tests/processing/generate-disclosure-inputs.test.ts rename to packages/mobile-sdk-alpha/tests/processing/generateDisclosureInputs.test.ts index 754702efb..d33fca2fc 100644 --- a/packages/mobile-sdk-alpha/tests/processing/generate-disclosure-inputs.test.ts +++ b/packages/mobile-sdk-alpha/tests/processing/generateDisclosureInputs.test.ts @@ -10,7 +10,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { PassportData, SelfApp } from '@selfxyz/common'; -import { generateTEEInputsDisclose } from '../../src/processing/generate-disclosure-inputs'; +import { generateTEEInputsDisclose } from '../../src/processing/generateDisclosureInputs'; import { useProtocolStore } from '../../src/stores/protocolStore'; // Mocks for dependencies const mockSecret = '0x' + '00'.repeat(30) + 'a4ec'; // 32-byte hex string diff --git a/packages/mobile-sdk-alpha/tests/validation/document.test.ts b/packages/mobile-sdk-alpha/tests/validation/document.test.ts index cec4c0ad0..d1d7ba5df 100644 --- a/packages/mobile-sdk-alpha/tests/validation/document.test.ts +++ b/packages/mobile-sdk-alpha/tests/validation/document.test.ts @@ -4,9 +4,7 @@ import { describe, expect, it, vi } from 'vitest'; -import { hash } from '@selfxyz/common/utils/hash/sha'; -import { formatMrz } from '@selfxyz/common/utils/passportFormat'; -import { genAndInitMockPassportData } from '@selfxyz/common/utils/passports/genMockPassportData'; +import { formatMrz, genAndInitMockPassportData, hash } from '@selfxyz/common'; import { isPassportDataValid } from '../../src/validation/document'; diff --git a/packages/mobile-sdk-alpha/tsconfig.json b/packages/mobile-sdk-alpha/tsconfig.json index d76b5fac3..7e7ac63a4 100644 --- a/packages/mobile-sdk-alpha/tsconfig.json +++ b/packages/mobile-sdk-alpha/tsconfig.json @@ -16,7 +16,18 @@ "noEmit": true, "resolveJsonModule": true, "verbatimModuleSyntax": false, - "types": ["vitest/globals", "react"] + "types": ["vitest/globals", "react"], + "baseUrl": ".", + "paths": { + "@selfxyz/common": ["../../common/dist/esm/index"], + "@selfxyz/common/constants": ["../../common/dist/esm/src/constants/index"], + "@selfxyz/common/types": ["../../common/dist/esm/src/types/index"], + "@selfxyz/common/utils": ["../../common/dist/esm/src/utils/index"], + "@selfxyz/common/utils/hash/sha": ["../../common/dist/esm/src/utils/hash/sha"], + "@selfxyz/common/utils/passportFormat": ["../../common/dist/esm/src/utils/passports/format"], + "@selfxyz/common/utils/csca": ["../../common/dist/esm/src/utils/csca"], + "@selfxyz/common/utils/passports": ["../../common/dist/esm/src/utils/passports/index"] + } }, "include": ["src", "tests"] } diff --git a/packages/mobile-sdk-alpha/tsup.config.ts b/packages/mobile-sdk-alpha/tsup.config.ts index d2faf5bb8..da0723eb3 100644 --- a/packages/mobile-sdk-alpha/tsup.config.ts +++ b/packages/mobile-sdk-alpha/tsup.config.ts @@ -19,12 +19,27 @@ export default defineConfig([ format: ['esm'], dts: true, sourcemap: true, - splitting: false, + splitting: true, clean: true, outDir: 'dist/esm', tsconfig: './tsconfig.json', target: 'es2020', - external: ['react', 'react-native', '@selfxyz/common'], + external: [ + 'react', + 'react-native', + '@selfxyz/common', + // Common crypto dependencies (already in main app) + 'elliptic', + 'js-sha256', + 'js-sha1', + 'js-sha512', + 'node-forge', + 'ethers', + // React Native dependencies + '@react-native-async-storage/async-storage', + 'react-native-keychain', + 'react-native-sqlite-storage', + ], esbuildOptions(options) { options.supported = { ...options.supported, @@ -48,12 +63,27 @@ export default defineConfig([ format: ['cjs'], dts: false, sourcemap: true, - splitting: false, + splitting: true, clean: false, outDir: 'dist/cjs', tsconfig: './tsconfig.cjs.json', target: 'es2020', - external: ['react', 'react-native', '@selfxyz/common'], + external: [ + 'react', + 'react-native', + '@selfxyz/common', + // Common crypto dependencies (already in main app) + 'elliptic', + 'js-sha256', + 'js-sha1', + 'js-sha512', + 'node-forge', + 'ethers', + // React Native dependencies + '@react-native-async-storage/async-storage', + 'react-native-keychain', + 'react-native-sqlite-storage', + ], outExtension: ({ format }) => ({ js: format === 'cjs' ? '.cjs' : '.js' }), esbuildOptions(options) { options.supported = { diff --git a/packages/mobile-sdk-alpha/vitest.config.ts b/packages/mobile-sdk-alpha/vitest.config.ts index 5f69475af..e00ed40e9 100644 --- a/packages/mobile-sdk-alpha/vitest.config.ts +++ b/packages/mobile-sdk-alpha/vitest.config.ts @@ -9,12 +9,7 @@ export default defineConfig({ globals: true, environment: 'jsdom', setupFiles: ['./tests/setup.ts'], - exclude: ['demo-app/**'], - }, - resolve: { - alias: { - // Mock React Native modules for testing - 'react-native': 'react-native-web', - }, + include: ['tests/**/*.test.{ts,tsx}', 'src/**/*.test.{ts,tsx}'], + exclude: ['node_modules/**'], }, }); diff --git a/packages/mobile-sdk-demo/.eslintrc.cjs b/packages/mobile-sdk-demo/.eslintrc.cjs new file mode 100644 index 000000000..88988d0da --- /dev/null +++ b/packages/mobile-sdk-demo/.eslintrc.cjs @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2021, + sourceType: 'module', + ecmaFeatures: { jsx: true }, + }, + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + plugins: ['prettier', 'simple-import-sort'], + ignorePatterns: ['dist/', 'node_modules/', 'android/', 'ios/'], + env: { + node: true, + es6: true, + jest: true, + }, + globals: { + globalThis: 'readonly', + }, + settings: { + 'import/resolver': { + typescript: { + alwaysTryTypes: true, + project: ['packages/*/tsconfig.json', 'tsconfig.json'], + }, + }, + }, + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + '@typescript-eslint/no-require-imports': 'off', + 'prettier/prettier': ['warn', {}, { usePrettierrc: true }], + }, +}; diff --git a/packages/mobile-sdk-alpha/demo-app/.gitignore b/packages/mobile-sdk-demo/.gitignore similarity index 91% rename from packages/mobile-sdk-alpha/demo-app/.gitignore rename to packages/mobile-sdk-demo/.gitignore index 63b28ce59..9e64fd4fc 100644 --- a/packages/mobile-sdk-alpha/demo-app/.gitignore +++ b/packages/mobile-sdk-demo/.gitignore @@ -19,9 +19,6 @@ android/app/build/ android/app/.cxx/ android/build/ android/.gradle/ -android/gradle/ -android/gradlew -android/gradlew.bat android/local.properties *.apk *.aab diff --git a/packages/mobile-sdk-demo/.prettierrc b/packages/mobile-sdk-demo/.prettierrc new file mode 100644 index 000000000..7f393b90e --- /dev/null +++ b/packages/mobile-sdk-demo/.prettierrc @@ -0,0 +1,20 @@ +{ + "arrowParens": "avoid", + "bracketSameLine": false, + "bracketSpacing": true, + "singleQuote": true, + "trailingComma": "all", + "tabWidth": 2, + "useTabs": false, + "semi": true, + "endOfLine": "auto", + "printWidth": 120, + "overrides": [ + { + "files": ["*.yml", "*.yaml"], + "options": { + "singleQuote": false + } + } + ] +} diff --git a/packages/mobile-sdk-demo/App.tsx b/packages/mobile-sdk-demo/App.tsx new file mode 100644 index 000000000..b55490bda --- /dev/null +++ b/packages/mobile-sdk-demo/App.tsx @@ -0,0 +1,203 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useState } from 'react'; +import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; + +import type { IDDocument } from '@selfxyz/common'; + +type Screen = 'home' | 'register' | 'generate' | 'prove' | 'camera' | 'nfc' | 'onboarding' | 'qr'; +type GenerateMockCmp = typeof import('./src/GenerateMock').default; +type RegisterDocumentCmp = typeof import('./src/RegisterDocument').default; +type ProveQRCodeCmp = typeof import('./src/ProveQRCode').default; + +function App() { + const [screen, setScreen] = useState('home'); + const [mockDocument, setMockDocument] = useState(null); + + const navigate = (next: Screen) => setScreen(next); + + if (screen === 'generate') { + const GenerateMock = require('./src/GenerateMock').default as GenerateMockCmp; + return navigate('home')} />; + } + + if (screen === 'register') { + const RegisterDocument = require('./src/RegisterDocument').default as RegisterDocumentCmp; + return navigate('home')} />; + } + + if (screen === 'prove') { + const ProveQRCode = require('./src/ProveQRCode').default as ProveQRCodeCmp; + return navigate('home')} />; + } + + if (screen === 'camera') { + const DocumentCamera = require('./src/DocumentCamera').default; + return navigate('home')} />; + } + + if (screen === 'nfc') { + const DocumentNFCScan = require('./src/DocumentNFCScan').default; + return navigate('home')} />; + } + + if (screen === 'onboarding') { + const DocumentOnboarding = require('./src/DocumentOnboarding').default; + return navigate('home')} />; + } + + if (screen === 'qr') { + const QRCodeViewFinder = require('./src/QRCodeViewFinder').default; + return navigate('home')} />; + } + + const MenuButton = ({ + title, + onPress, + isWorking = false, + }: { + title: string; + onPress: () => void; + isWorking?: boolean; + }) => ( + + + {title} + + + ); + + return ( + + + Self Demo App + Mobile SDK Alpha - Available Screens + + + + 🎯 Core Features + navigate('generate')} isWorking={true} /> + navigate('register')} + isWorking={Boolean(mockDocument)} + /> + navigate('prove')} isWorking={Boolean(mockDocument)} /> + + + + 📷 Document Scanning + navigate('camera')} /> + navigate('nfc')} /> + navigate('onboarding')} /> + + + + 📱 QR Code Features + navigate('qr')} /> + + + + ✅ Working | ⏳ Placeholder (Not Implemented) + Tap any screen to explore the demo interface + + + ); +} + +const styles = StyleSheet.create({ + container: { + flexGrow: 1, + backgroundColor: '#f8f9fa', + padding: 20, + }, + header: { + alignItems: 'center', + marginBottom: 32, + paddingTop: 20, + }, + title: { + fontSize: 28, + fontWeight: 'bold', + textAlign: 'center', + color: '#1a1a1a', + marginBottom: 8, + }, + subtitle: { + fontSize: 16, + color: '#666', + textAlign: 'center', + }, + section: { + marginBottom: 32, + }, + sectionTitle: { + fontSize: 20, + fontWeight: 'bold', + marginBottom: 16, + color: '#333', + textAlign: 'center', + }, + menuButton: { + width: '100%', + paddingVertical: 16, + paddingHorizontal: 20, + borderRadius: 12, + marginBottom: 12, + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.1, + shadowRadius: 3.84, + elevation: 5, + }, + workingButton: { + backgroundColor: '#007AFF', + }, + placeholderButton: { + backgroundColor: '#fff', + borderWidth: 1, + borderColor: '#e1e5e9', + }, + menuButtonText: { + fontSize: 16, + fontWeight: '600', + textAlign: 'center', + }, + workingButtonText: { + color: '#fff', + }, + placeholderButtonText: { + color: '#666', + }, + footer: { + marginTop: 20, + padding: 20, + backgroundColor: '#fff', + borderRadius: 12, + borderWidth: 1, + borderColor: '#e1e5e9', + }, + footerText: { + textAlign: 'center', + color: '#666', + fontSize: 14, + fontWeight: '500', + marginBottom: 8, + }, + footerSubtext: { + textAlign: 'center', + color: '#999', + fontSize: 12, + }, +}); + +export default App; diff --git a/packages/mobile-sdk-demo/README.md b/packages/mobile-sdk-demo/README.md new file mode 100644 index 000000000..a5f226065 --- /dev/null +++ b/packages/mobile-sdk-demo/README.md @@ -0,0 +1,53 @@ +# Self Demo App + +This is a demo application for testing the Self mobile SDK. + +## Configuration + +### New Architecture + +The new architecture (Fabric + TurboModules) can be toggled on/off: + +**Android**: Set `newArchEnabled` in `android/gradle.properties` + +- `newArchEnabled=true` - Enable new architecture +- `newArchEnabled=false` - Disable new architecture (default) + +**iOS**: Set `:fabric_enabled` in `ios/Podfile` + +- `:fabric_enabled => true` - Enable new architecture +- `:fabric_enabled => false` - Disable new architecture (default) + +### Hermes Engine + +Hermes JavaScript engine can be toggled on/off: + +**Android**: Set `hermesEnabled` in `android/gradle.properties` + +- `hermesEnabled=true` - Enable Hermes (default) +- `hermesEnabled=false` - Use JSC instead + +**iOS**: Set `:hermes_enabled` in `ios/Podfile` + +- `:hermes_enabled => true` - Enable Hermes (default) +- `:hermes_enabled => false` - Use JSC instead + +## Current Settings + +- **New Architecture**: Disabled +- **Hermes**: Enabled + +## Build Commands + +After changing configuration: + +```bash +# Clean and rebuild +yarn clean + +# Run on Android +yarn android + +# Run on iOS +yarn ios +``` diff --git a/packages/mobile-sdk-alpha/demo-app/__tests__/App.test.tsx b/packages/mobile-sdk-demo/__tests__/App.test.tsx similarity index 50% rename from packages/mobile-sdk-alpha/demo-app/__tests__/App.test.tsx rename to packages/mobile-sdk-demo/__tests__/App.test.tsx index 60b80c6b6..2f65bbb4f 100644 --- a/packages/mobile-sdk-alpha/demo-app/__tests__/App.test.tsx +++ b/packages/mobile-sdk-demo/__tests__/App.test.tsx @@ -3,15 +3,20 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import React from 'react'; -import renderer from 'react-test-renderer'; import { Text } from 'react-native'; +import renderer from 'react-test-renderer'; + import App from '../App'; -test('renders menu items', () => { +test('renders menu buttons', () => { const rendered = renderer.create(); - const texts = rendered.root.findAllByType(Text).map(node => React.Children.toArray(node.props.children).join('')); - const expected = ['Self Demo App', 'Register Document', 'Generate Mock', 'Prove QR Code']; - expect(texts).toEqual(expect.arrayContaining(expected)); - expect(texts).toHaveLength(expected.length); + const textNodes = rendered.root.findAllByType(Text); + + expect(textNodes.some(node => node.props.children === 'Self Demo App')).toBe(true); + + ['✅ Generate Mock Data', '⏳ Register Document', '⏳ Prove QR Code'].forEach(label => { + expect(textNodes.some(node => node.props.children === label)).toBe(true); + }); + rendered.unmount(); }); diff --git a/packages/mobile-sdk-alpha/demo-app/__tests__/cryptoPolyfills.test.ts b/packages/mobile-sdk-demo/__tests__/cryptoPolyfills.test.ts similarity index 99% rename from packages/mobile-sdk-alpha/demo-app/__tests__/cryptoPolyfills.test.ts rename to packages/mobile-sdk-demo/__tests__/cryptoPolyfills.test.ts index f516fda91..2318401ef 100644 --- a/packages/mobile-sdk-alpha/demo-app/__tests__/cryptoPolyfills.test.ts +++ b/packages/mobile-sdk-demo/__tests__/cryptoPolyfills.test.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { randomBytes, sha256, sha512, computeHmac, pbkdf2 } from '../src/utils/ethers'; +import { computeHmac, pbkdf2, randomBytes, sha256, sha512 } from '../src/utils/ethers'; describe('Crypto Polyfills', () => { describe('randomBytes', () => { diff --git a/packages/mobile-sdk-demo/android/app/build.gradle b/packages/mobile-sdk-demo/android/app/build.gradle new file mode 100644 index 000000000..2ad7aae31 --- /dev/null +++ b/packages/mobile-sdk-demo/android/app/build.gradle @@ -0,0 +1,53 @@ +apply plugin: "com.android.application" +apply plugin: "com.facebook.react" +apply plugin: "org.jetbrains.kotlin.android" + +react { + root = file("../../") + reactNativeDir = file("../../../../node_modules/react-native") + codegenDir = file("../../../../node_modules/@react-native/codegen") + cliFile = file("../../../../node_modules/react-native/cli.js") + autolinkLibrariesWithApp() +} + +android { + namespace "com.selfxyz.demoapp" + ndkVersion rootProject.ext.ndkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + compileSdkVersion rootProject.ext.compileSdkVersion + + defaultConfig { + applicationId "com.selfxyz.demoapp" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + } + + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } + } + + buildTypes { + debug { + signingConfig signingConfigs.debug + } + } + + buildFeatures { + buildConfig true + } +} + +dependencies { + implementation("com.facebook.react:react-android:0.76.9") + implementation("com.facebook.react:hermes-android:0.76.9") + if (project.hasProperty('newArchEnabled') ? newArchEnabled.toBoolean() : false) { + implementation("com.facebook.react:react-android-codegen:0.76.9") + } +} diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/debug.keystore b/packages/mobile-sdk-demo/android/app/debug.keystore similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/debug.keystore rename to packages/mobile-sdk-demo/android/app/debug.keystore diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/proguard-rules.pro b/packages/mobile-sdk-demo/android/app/proguard-rules.pro similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/proguard-rules.pro rename to packages/mobile-sdk-demo/android/app/proguard-rules.pro diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/debug/AndroidManifest.xml b/packages/mobile-sdk-demo/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/debug/AndroidManifest.xml rename to packages/mobile-sdk-demo/android/app/src/debug/AndroidManifest.xml diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/AndroidManifest.xml b/packages/mobile-sdk-demo/android/app/src/main/AndroidManifest.xml similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/AndroidManifest.xml rename to packages/mobile-sdk-demo/android/app/src/main/AndroidManifest.xml diff --git a/packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainActivity.kt b/packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainActivity.kt new file mode 100644 index 000000000..77e827622 --- /dev/null +++ b/packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainActivity.kt @@ -0,0 +1,24 @@ +package com.selfxyz.demoapp + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate + +class MainActivity : ReactActivity() { + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "SelfDemoApp" + + /** + * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + */ + override fun createReactActivityDelegate(): ReactActivityDelegate { + return DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + } +} diff --git a/packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainApplication.kt b/packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainApplication.kt new file mode 100644 index 000000000..a3bbfd440 --- /dev/null +++ b/packages/mobile-sdk-demo/android/app/src/main/java/com/selfxyz/demoapp/MainApplication.kt @@ -0,0 +1,46 @@ +package com.selfxyz.demoapp + +import android.app.Application +import com.facebook.react.PackageList +import com.facebook.react.ReactApplication +import com.facebook.react.ReactNativeHost +import com.facebook.react.ReactPackage +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load +import com.facebook.react.defaults.DefaultReactNativeHost +import com.facebook.soloader.SoLoader +import com.facebook.react.soloader.OpenSourceMergedSoMapping + +class MainApplication : Application(), ReactApplication { + + private val mReactNativeHost: ReactNativeHost = object : DefaultReactNativeHost(this) { + override fun getPackages(): List { + // Packages that cannot be autolinked yet can be added manually here, for example: + // packages.add(new MyReactNativePackage()); + return PackageList(this).packages + } + + override fun getJSMainModuleName(): String { + return "index" + } + + override fun getUseDeveloperSupport(): Boolean { + return BuildConfig.DEBUG + } + + override val isNewArchEnabled: Boolean + get() = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean + get() = BuildConfig.IS_HERMES_ENABLED + } + + override val reactNativeHost: ReactNativeHost + get() = mReactNativeHost + + override fun onCreate() { + super.onCreate() + SoLoader.init(this, OpenSourceMergedSoMapping) + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + load() + } + } +} diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/drawable/rn_edit_text_material.xml b/packages/mobile-sdk-demo/android/app/src/main/res/drawable/rn_edit_text_material.xml similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/drawable/rn_edit_text_material.xml rename to packages/mobile-sdk-demo/android/app/src/main/res/drawable/rn_edit_text_material.xml diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to packages/mobile-sdk-demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/values/strings.xml b/packages/mobile-sdk-demo/android/app/src/main/res/values/strings.xml similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/values/strings.xml rename to packages/mobile-sdk-demo/android/app/src/main/res/values/strings.xml diff --git a/packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/values/styles.xml b/packages/mobile-sdk-demo/android/app/src/main/res/values/styles.xml similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/android/app/src/main/res/values/styles.xml rename to packages/mobile-sdk-demo/android/app/src/main/res/values/styles.xml diff --git a/packages/mobile-sdk-demo/android/build.gradle b/packages/mobile-sdk-demo/android/build.gradle new file mode 100644 index 000000000..df682b4c5 --- /dev/null +++ b/packages/mobile-sdk-demo/android/build.gradle @@ -0,0 +1,40 @@ +buildscript { + ext { + buildToolsVersion = "35.0.0" + minSdkVersion = 24 + compileSdkVersion = 35 + targetSdkVersion = 35 + ndkVersion = "27.0.11718014" + kotlinVersion = "1.9.24" + } + repositories { + google() + mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath("com.android.tools.build:gradle:8.6.0") + classpath("com.facebook.react:react-native-gradle-plugin") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + } +} + +allprojects { + repositories { + google() + mavenCentral() + maven { + // All of React Native (JS, Android binaries) is installed from npm + url("$rootDir/../../../node_modules/react-native/android") + } + maven { + // Android JSC is installed from npm + url("$rootDir/../../../node_modules/jsc-android/dist") + } + maven { url 'https://jitpack.io' } + } +} + +apply plugin: "com.facebook.react.rootproject" diff --git a/packages/mobile-sdk-alpha/demo-app/android/gradle.properties b/packages/mobile-sdk-demo/android/gradle.properties similarity index 95% rename from packages/mobile-sdk-alpha/demo-app/android/gradle.properties rename to packages/mobile-sdk-demo/android/gradle.properties index 183b46a8d..e646a71a8 100644 --- a/packages/mobile-sdk-alpha/demo-app/android/gradle.properties +++ b/packages/mobile-sdk-demo/android/gradle.properties @@ -42,3 +42,6 @@ hermesEnabled=true # This allows your app to draw behind system bars for an immersive UI. # Note: Only works with ReactActivity and should not be used with custom Activity. edgeToEdgeEnabled=false + +# Designate the project root for the React Native Gradle plugin +react.projectRoot=../../../../ diff --git a/packages/mobile-sdk-demo/android/gradle/libs.versions.toml b/packages/mobile-sdk-demo/android/gradle/libs.versions.toml new file mode 100644 index 000000000..3b161c3fe --- /dev/null +++ b/packages/mobile-sdk-demo/android/gradle/libs.versions.toml @@ -0,0 +1,20 @@ +[versions] +agp = "8.6.0" +gson = "2.8.9" +guava = "31.0.1-jre" +javapoet = "1.13.0" +junit = "4.13.2" +kotlin = "1.9.25" +assertj = "3.25.1" + +[libraries] +kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +javapoet = { module = "com.squareup:javapoet", version.ref = "javapoet" } +junit = {module = "junit:junit", version.ref = "junit" } +assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } + +[plugins] +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/packages/mobile-sdk-demo/android/gradle/wrapper/gradle-wrapper.jar b/packages/mobile-sdk-demo/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..a4b76b9530d66f5e68d973ea569d8e19de379189 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 0 HcmV?d00001 diff --git a/packages/mobile-sdk-demo/android/gradle/wrapper/gradle-wrapper.properties b/packages/mobile-sdk-demo/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..c1d5e0185 --- /dev/null +++ b/packages/mobile-sdk-demo/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/mobile-sdk-demo/android/gradlew b/packages/mobile-sdk-demo/android/gradlew new file mode 100755 index 000000000..f5feea6d6 --- /dev/null +++ b/packages/mobile-sdk-demo/android/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/packages/mobile-sdk-demo/android/gradlew.bat b/packages/mobile-sdk-demo/android/gradlew.bat new file mode 100644 index 000000000..16302dede --- /dev/null +++ b/packages/mobile-sdk-demo/android/gradlew.bat @@ -0,0 +1,99 @@ +@REM Copyright (c) Meta Platforms, Inc. and affiliates. +@REM +@REM This source code is licensed under the MIT license found in the +@REM LICENSE file in the root directory of this source tree. + +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/packages/mobile-sdk-demo/android/settings.gradle b/packages/mobile-sdk-demo/android/settings.gradle new file mode 100644 index 000000000..dc05e371f --- /dev/null +++ b/packages/mobile-sdk-demo/android/settings.gradle @@ -0,0 +1,8 @@ +pluginManagement { includeBuild("../../../node_modules/@react-native/gradle-plugin") } +plugins { id("com.facebook.react.settings") } +extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> + ex.autolinkLibrariesFromCommand(['yarn','exec','react-native','config']) +} +rootProject.name = 'SelfDemoApp' +include ':app' +includeBuild('../../../node_modules/@react-native/gradle-plugin') diff --git a/packages/mobile-sdk-alpha/demo-app/app.json b/packages/mobile-sdk-demo/app.json similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/app.json rename to packages/mobile-sdk-demo/app.json diff --git a/packages/mobile-sdk-alpha/demo-app/src/GenerateMock.tsx b/packages/mobile-sdk-demo/babel.config.js similarity index 62% rename from packages/mobile-sdk-alpha/demo-app/src/GenerateMock.tsx rename to packages/mobile-sdk-demo/babel.config.js index 939f30b21..23cd46c8b 100644 --- a/packages/mobile-sdk-alpha/demo-app/src/GenerateMock.tsx +++ b/packages/mobile-sdk-demo/babel.config.js @@ -2,7 +2,6 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -export default function GenerateMock() { - // TODO: implement mock generation - return null; -} +module.exports = { + presets: ['module:@react-native/babel-preset'], +}; diff --git a/packages/mobile-sdk-demo/crypto-polyfill.js b/packages/mobile-sdk-demo/crypto-polyfill.js new file mode 100644 index 000000000..1c69bd729 --- /dev/null +++ b/packages/mobile-sdk-demo/crypto-polyfill.js @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +// Crypto polyfill using @noble/hashes for React Native compatibility +const { sha256 } = require('@noble/hashes/sha256'); +const { sha1 } = require('@noble/hashes/sha1'); +const { sha512 } = require('@noble/hashes/sha512'); +const { Buffer } = require('buffer'); +require('react-native-get-random-values'); // installs globalThis.crypto.getRandomValues + +// Create a crypto polyfill that provides the Node.js crypto API +const crypto = { + createHash: algorithm => { + const algorithms = { + sha256: sha256, + sha1: sha1, + sha512: sha512, + }; + + const hashFunction = algorithms[algorithm.toLowerCase()]; + if (!hashFunction) { + throw new Error(`Unsupported hash algorithm: ${algorithm}`); + } + + let data = Buffer.alloc(0); + + const api = { + update: inputData => { + // Accumulate data + data = Buffer.concat([data, Buffer.from(inputData)]); + return api; + }, + digest: encoding => { + const hash = hashFunction(data); + if (encoding === 'hex') { + return Buffer.from(hash).toString('hex'); + } + return Buffer.from(hash); + }, + }; + return api; + }, + + // Add other commonly used crypto methods as needed + randomBytes: size => { + const array = new Uint8Array(size); + if (typeof globalThis.crypto?.getRandomValues !== 'function') { + throw new Error('crypto.getRandomValues not available; ensure polyfill is loaded'); + } + globalThis.crypto.getRandomValues(array); + return Buffer.from(array); + }, +}; + +module.exports = crypto; diff --git a/packages/mobile-sdk-alpha/demo-app/index.js b/packages/mobile-sdk-demo/index.js similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/index.js rename to packages/mobile-sdk-demo/index.js diff --git a/packages/mobile-sdk-alpha/demo-app/ios/.xcode.env b/packages/mobile-sdk-demo/ios/.xcode.env similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/.xcode.env rename to packages/mobile-sdk-demo/ios/.xcode.env diff --git a/packages/mobile-sdk-alpha/demo-app/ios/Podfile b/packages/mobile-sdk-demo/ios/Podfile similarity index 80% rename from packages/mobile-sdk-alpha/demo-app/ios/Podfile rename to packages/mobile-sdk-demo/ios/Podfile index b1e79c1d7..20d5c2340 100644 --- a/packages/mobile-sdk-alpha/demo-app/ios/Podfile +++ b/packages/mobile-sdk-demo/ios/Podfile @@ -1,6 +1,6 @@ # Resolve react_native_pods.rb with node to allow for hoisting require Pod::Executable.execute_command("node", ["-p", - 'require.resolve( + 'require.resolve( "react-native/scripts/react_native_pods.rb", {paths: [process.argv[1]]}, )', __dir__]).strip @@ -21,9 +21,13 @@ target "SelfDemoApp" do :path => config[:reactNativePath], # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/..", - :fabric_enabled => false + :fabric_enabled => false, + :hermes_enabled => true, ) + # Use the custom NFCPassportReader fork + pod "NFCPassportReader", :git => "git@github.com:selfxyz/NFCPassportReader.git" + post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( diff --git a/packages/mobile-sdk-alpha/demo-app/ios/Podfile.lock b/packages/mobile-sdk-demo/ios/Podfile.lock similarity index 95% rename from packages/mobile-sdk-alpha/demo-app/ios/Podfile.lock rename to packages/mobile-sdk-demo/ios/Podfile.lock index 0f64e921f..55d6ef350 100644 --- a/packages/mobile-sdk-alpha/demo-app/ios/Podfile.lock +++ b/packages/mobile-sdk-demo/ios/Podfile.lock @@ -8,6 +8,18 @@ PODS: - hermes-engine (0.76.9): - hermes-engine/Pre-built (= 0.76.9) - hermes-engine/Pre-built (0.76.9) + - Mixpanel-swift (5.0.0): + - Mixpanel-swift/Complete (= 5.0.0) + - Mixpanel-swift/Complete (5.0.0) + - mobile-sdk-alpha (0.1.0): + - NFCPassportReader + - QKMRZParser + - React-Core + - NFCPassportReader (2.1.1): + - Mixpanel-swift (~> 5.0.0) + - OpenSSL-Universal (= 1.1.1900) + - OpenSSL-Universal (1.1.1900) + - QKMRZParser (2.0.0) - RCT-Folly (2024.10.14.00): - boost - DoubleConversion @@ -1286,6 +1298,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-get-random-values (1.11.0): + - React-Core - React-nativeconfig (0.76.9) - React-NativeModulesApple (0.76.9): - glog @@ -1558,6 +1572,27 @@ PODS: - React-logger - React-perflogger - React-utils (= 0.76.9) + - RNCPicker (2.11.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - SocketRocket (0.7.1) - Yoga (0.0.0) @@ -1569,6 +1604,8 @@ DEPENDENCIES: - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - "mobile-sdk-alpha (from `../node_modules/@selfxyz/mobile-sdk-alpha`)" + - "NFCPassportReader (from `git@github.com:selfxyz/NFCPassportReader.git`)" - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) @@ -1600,6 +1637,7 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) + - react-native-get-random-values (from `../node_modules/react-native-get-random-values`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -1627,10 +1665,14 @@ DEPENDENCIES: - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCodegen (from `build/generated/ios`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCPicker (from `../node_modules/@react-native-picker/picker`)" - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: trunk: + - Mixpanel-swift + - OpenSSL-Universal + - QKMRZParser - SocketRocket EXTERNAL SOURCES: @@ -1649,6 +1691,10 @@ EXTERNAL SOURCES: hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" :tag: hermes-2024-11-12-RNv0.76.2-5b4aa20c719830dcf5684832b89a6edb95ac3d64 + mobile-sdk-alpha: + :path: "../node_modules/@selfxyz/mobile-sdk-alpha" + NFCPassportReader: + :git: "git@github.com:selfxyz/NFCPassportReader.git" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -1707,6 +1753,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" React-microtasksnativemodule: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" + react-native-get-random-values: + :path: "../node_modules/react-native-get-random-values" React-nativeconfig: :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: @@ -1761,9 +1809,16 @@ EXTERNAL SOURCES: :path: build/generated/ios ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNCPicker: + :path: "../node_modules/@react-native-picker/picker" Yoga: :path: "../node_modules/react-native/ReactCommon/yoga" +CHECKOUT OPTIONS: + NFCPassportReader: + :commit: 04ede227cbfd377e2b4bc9b38f9a89eebdcab52f + :git: "git@github.com:selfxyz/NFCPassportReader.git" + SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 @@ -1772,6 +1827,11 @@ SPEC CHECKSUMS: fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6 glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11 + Mixpanel-swift: e9bef28a9648faff384d5ba6f48ecc2787eb24c0 + mobile-sdk-alpha: 96949ad8c8b61a9fa6b918a4202f9cebb9c678cc + NFCPassportReader: 48873f856f91215dbfa1eaaec20eae639672862e + OpenSSL-Universal: 84efb8a29841f2764ac5403e0c4119a28b713346 + QKMRZParser: 6b419b6f07d6bff6b50429b97de10846dc902c29 RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17 RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83 RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716 @@ -1801,6 +1861,7 @@ SPEC CHECKSUMS: React-logger: c4052eb941cca9a097ef01b59543a656dc088559 React-Mapbuffer: 33546a3ebefbccb8770c33a1f8a5554fa96a54de React-microtasksnativemodule: d80ff86c8902872d397d9622f1a97aadcc12cead + react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678 React-NativeModulesApple: cebca2e5320a3d66e123cade23bd90a167ffce5e React-perflogger: 72e653eb3aba9122f9e57cf012d22d2486f33358 @@ -1828,9 +1889,10 @@ SPEC CHECKSUMS: React-utils: ed818f19ab445000d6b5c4efa9d462449326cc9f ReactCodegen: f853a20cc9125c5521c8766b4b49375fec20648b ReactCommon: 300d8d9c5cb1a6cd79a67cf5d8f91e4d477195f9 + RNCPicker: 3549e7ab9a00047753e9fa852a1858a154cc4275 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a -PODFILE CHECKSUM: 224b208fda585f58e738758a69abedc60fa27789 +PODFILE CHECKSUM: 7db6890d140dc2f697c16380d1412b8861ebcff7 COCOAPODS: 1.16.2 diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcodeproj/project.pbxproj b/packages/mobile-sdk-demo/ios/SelfDemoApp.xcodeproj/project.pbxproj similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcodeproj/project.pbxproj rename to packages/mobile-sdk-demo/ios/SelfDemoApp.xcodeproj/project.pbxproj diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/SelfDemoApp.xcscheme b/packages/mobile-sdk-demo/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/SelfDemoApp.xcscheme similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/SelfDemoApp.xcscheme rename to packages/mobile-sdk-demo/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/SelfDemoApp.xcscheme diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/TempProject.xcscheme b/packages/mobile-sdk-demo/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/TempProject.xcscheme similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/TempProject.xcscheme rename to packages/mobile-sdk-demo/ios/SelfDemoApp.xcodeproj/xcshareddata/xcschemes/TempProject.xcscheme diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcworkspace/contents.xcworkspacedata b/packages/mobile-sdk-demo/ios/SelfDemoApp.xcworkspace/contents.xcworkspacedata similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp.xcworkspace/contents.xcworkspacedata rename to packages/mobile-sdk-demo/ios/SelfDemoApp.xcworkspace/contents.xcworkspacedata diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/AppDelegate.swift b/packages/mobile-sdk-demo/ios/SelfDemoApp/AppDelegate.swift similarity index 88% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/AppDelegate.swift rename to packages/mobile-sdk-demo/ios/SelfDemoApp/AppDelegate.swift index 0d5a70d24..4bb67808c 100644 --- a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/AppDelegate.swift +++ b/packages/mobile-sdk-demo/ios/SelfDemoApp/AppDelegate.swift @@ -12,10 +12,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window = UIWindow(frame: UIScreen.main.bounds) - let bridge = RCTBridge( + guard let bridge = RCTBridge( delegate: self, launchOptions: launchOptions - ) + ) else { + assertionFailure("Failed to initialize RCTBridge") + return false + } let rootView = RCTRootView( bridge: bridge, diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Images.xcassets/AppIcon.appiconset/Contents.json b/packages/mobile-sdk-demo/ios/SelfDemoApp/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Images.xcassets/AppIcon.appiconset/Contents.json rename to packages/mobile-sdk-demo/ios/SelfDemoApp/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Images.xcassets/Contents.json b/packages/mobile-sdk-demo/ios/SelfDemoApp/Images.xcassets/Contents.json similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Images.xcassets/Contents.json rename to packages/mobile-sdk-demo/ios/SelfDemoApp/Images.xcassets/Contents.json diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Info-Debug.plist b/packages/mobile-sdk-demo/ios/SelfDemoApp/Info-Debug.plist similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Info-Debug.plist rename to packages/mobile-sdk-demo/ios/SelfDemoApp/Info-Debug.plist diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Info.plist b/packages/mobile-sdk-demo/ios/SelfDemoApp/Info.plist similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/Info.plist rename to packages/mobile-sdk-demo/ios/SelfDemoApp/Info.plist diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/LaunchScreen.storyboard b/packages/mobile-sdk-demo/ios/SelfDemoApp/LaunchScreen.storyboard similarity index 100% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/LaunchScreen.storyboard rename to packages/mobile-sdk-demo/ios/SelfDemoApp/LaunchScreen.storyboard diff --git a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/PrivacyInfo.xcprivacy b/packages/mobile-sdk-demo/ios/SelfDemoApp/PrivacyInfo.xcprivacy similarity index 97% rename from packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/PrivacyInfo.xcprivacy rename to packages/mobile-sdk-demo/ios/SelfDemoApp/PrivacyInfo.xcprivacy index 41b8317f0..5cae44312 100644 --- a/packages/mobile-sdk-alpha/demo-app/ios/SelfDemoApp/PrivacyInfo.xcprivacy +++ b/packages/mobile-sdk-demo/ios/SelfDemoApp/PrivacyInfo.xcprivacy @@ -6,18 +6,19 @@ NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons - C617.1 + 1C8F.1 + CA92.1 NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons - CA92.1 + C617.1 diff --git a/packages/mobile-sdk-alpha/demo-app/jest.config.cjs b/packages/mobile-sdk-demo/jest.config.cjs similarity index 53% rename from packages/mobile-sdk-alpha/demo-app/jest.config.cjs rename to packages/mobile-sdk-demo/jest.config.cjs index 25a9b5619..8554cd893 100644 --- a/packages/mobile-sdk-alpha/demo-app/jest.config.cjs +++ b/packages/mobile-sdk-demo/jest.config.cjs @@ -4,6 +4,12 @@ module.exports = { preset: 'react-native', + setupFilesAfterEnv: ['/jest.setup.js'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], transformIgnorePatterns: ['node_modules/(?!(react-native|@react-native|@selfxyz)/)'], + moduleDirectories: ['node_modules', '/../../../node_modules'], + moduleNameMapper: { + '^@selfxyz/common$': '/../../common/dist/cjs/index.cjs', + '^@selfxyz/mobile-sdk-alpha$': '/../mobile-sdk-alpha/dist/cjs/index.cjs', + }, }; diff --git a/packages/mobile-sdk-demo/jest.setup.js b/packages/mobile-sdk-demo/jest.setup.js new file mode 100644 index 000000000..9e50c5605 --- /dev/null +++ b/packages/mobile-sdk-demo/jest.setup.js @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +/** @jest-environment jsdom */ + +// Mock the native bridge configuration FIRST +global.__fbBatchedBridgeConfig = { + remoteModuleConfig: [], + localModulesConfig: {}, +}; + +// Mock React Native's native modules +const { NativeModules } = require('react-native'); + +// Mock NativeModules +NativeModules.PlatformConstants = { + getConstants: () => ({ + isTesting: true, + reactNativeVersion: { + major: 0, + minor: 76, + patch: 9, + }, + }), +}; + +// Mock DeviceInfo native module +NativeModules.DeviceInfo = { + getConstants: () => ({ + Dimensions: { + window: { width: 375, height: 812 }, + screen: { width: 375, height: 812 }, + }, + PixelRatio: 2, + }), +}; + +// Mock other common native modules +NativeModules.StatusBarManager = { + getConstants: () => ({}), +}; + +NativeModules.Appearance = { + getConstants: () => ({}), +}; + +NativeModules.SourceCode = { + getConstants: () => ({ + scriptURL: 'http://localhost:8081/index.bundle?platform=ios&dev=true', + }), +}; + +NativeModules.UIManager = { + getConstants: () => ({}), + measure: jest.fn(), + measureInWindow: jest.fn(), + measureLayout: jest.fn(), + findSubviewIn: jest.fn(), + dispatchViewManagerCommand: jest.fn(), + setLayoutAnimationEnabledExperimental: jest.fn(), + configureNextLayoutAnimation: jest.fn(), + removeSubviewsFromContainerWithID: jest.fn(), + replaceExistingNonRootView: jest.fn(), + setChildren: jest.fn(), + manageChildren: jest.fn(), + setJSResponder: jest.fn(), + clearJSResponder: jest.fn(), + createView: jest.fn(), + updateView: jest.fn(), + removeRootView: jest.fn(), + addRootView: jest.fn(), + updateRootView: jest.fn(), +}; + +NativeModules.KeyboardObserver = { + addListener: jest.fn(), + removeListeners: jest.fn(), +}; + +// Mock react-native-get-random-values +jest.mock('react-native-get-random-values', () => ({ + polyfillGlobal: jest.fn(), +})); + +// Mock @react-native-picker/picker +jest.mock('@react-native-picker/picker', () => ({ + Picker: 'Picker', + PickerIOS: 'PickerIOS', +})); + +// Mock ethers +jest.mock('ethers', () => { + const mockRandomBytes = jest.fn().mockImplementation(length => new Uint8Array(length)); + mockRandomBytes.register = jest.fn(); + + const mockHashFunction = jest.fn().mockImplementation(() => '0x' + 'a'.repeat(64)); + mockHashFunction.register = jest.fn(); + + const mockSha512Function = jest.fn().mockImplementation(() => '0x' + 'a'.repeat(128)); + mockSha512Function.register = jest.fn(); + + return { + ethers: { + Wallet: jest.fn().mockImplementation(() => ({ + address: '0x1234567890123456789012345678901234567890', + signMessage: jest.fn().mockResolvedValue('0xsignature'), + })), + JsonRpcProvider: jest.fn().mockImplementation(() => ({ + getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), + })), + randomBytes: mockRandomBytes, + computeHmac: mockHashFunction, + pbkdf2: mockHashFunction, + sha256: mockHashFunction, + sha512: mockSha512Function, + ripemd160: mockHashFunction, + scrypt: mockHashFunction, + }, + }; +}); + +// Mock @selfxyz/common +jest.mock('@selfxyz/common', () => ({ + generateMockPassportData: jest.fn().mockReturnValue({ + documentNumber: '123456789', + dateOfBirth: '1990-01-01', + dateOfExpiry: '2030-01-01', + firstName: 'John', + lastName: 'Doe', + }), + cryptoPolyfill: { + createHash: jest.fn().mockReturnValue({ + update: jest.fn().mockReturnThis(), + digest: jest.fn().mockReturnValue('mocked-hash'), + }), + createHmac: jest.fn().mockReturnValue({ + update: jest.fn().mockReturnThis(), + digest: jest.fn().mockReturnValue('mocked-hmac'), + }), + randomBytes: jest.fn().mockImplementation(size => new Uint8Array(size)), + pbkdf2Sync: jest.fn().mockImplementation(() => new Uint8Array(32)), + }, +})); + +// Mock @selfxyz/mobile-sdk-alpha +jest.mock('@selfxyz/mobile-sdk-alpha', () => ({ + SelfSDK: { + initialize: jest.fn().mockResolvedValue(undefined), + generateProof: jest.fn().mockResolvedValue('mock-proof'), + registerDocument: jest.fn().mockResolvedValue('mock-registration'), + }, +})); + +// Mock console methods to avoid test output clutter +global.console = { + ...console, + log: jest.fn(), + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), +}; diff --git a/packages/mobile-sdk-demo/metro.config.cjs b/packages/mobile-sdk-demo/metro.config.cjs new file mode 100644 index 000000000..6ad3d83c5 --- /dev/null +++ b/packages/mobile-sdk-demo/metro.config.cjs @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); +const path = require('node:path'); +const findYarnWorkspaceRoot = require('find-yarn-workspace-root'); + +const defaultConfig = getDefaultConfig(__dirname); + +const projectRoot = __dirname; +const workspaceRoot = findYarnWorkspaceRoot(__dirname) || path.resolve(__dirname, '../..'); + +/** + * Modern Metro configuration for demo app using native workspace capabilities + * Based on the working main app configuration + */ +const config = { + projectRoot, + + watchFolders: [ + workspaceRoot, // Watch entire workspace root + path.resolve(workspaceRoot, 'common'), + path.resolve(workspaceRoot, 'packages/mobile-sdk-alpha'), + ], + + resolver: { + // Prevent Haste module naming collisions from duplicate package.json files + blockList: [ + // Ignore built package.json files to prevent Haste collisions + /.*\/dist\/package\.json$/, + /.*\/build\/package\.json$/, + ], + // Let workspace packages resolve naturally to their built exports (override where needed) + alias: { + '@selfxyz/mobile-sdk-alpha': path.resolve(workspaceRoot, 'packages/mobile-sdk-alpha/src'), + }, + // Enable workspace-aware resolution + enableGlobalPackages: true, + unstable_enablePackageExports: true, + // Prefer React Native-specific exports when available to avoid Node-only deps + unstable_conditionNames: ['require', 'react-native'], + unstable_enableSymlinks: true, + nodeModulesPaths: [path.resolve(projectRoot, 'node_modules'), path.resolve(workspaceRoot, 'node_modules')], + extraNodeModules: { + '@babel/runtime': path.resolve(__dirname, '../../node_modules/@babel/runtime'), + // Pin React and React Native to monorepo root + react: path.resolve(__dirname, '../../node_modules/react'), + 'react-native': path.resolve(__dirname, '../../node_modules/react-native'), + // Add workspace packages for proper resolution + '@selfxyz/common': path.resolve(workspaceRoot, 'common'), + // Fix snarkjs resolution for @anon-aadhaar/core + snarkjs: path.resolve(__dirname, '../../node_modules/snarkjs/build/main.cjs'), + // Fix ffjavascript resolution for snarkjs dependencies + ffjavascript: path.resolve(__dirname, '../../node_modules/ffjavascript/build/main.cjs'), + // Crypto polyfills - use custom polyfill with @noble/hashes + crypto: path.resolve(__dirname, 'src/polyfills/cryptoPolyfill.js'), + stream: require.resolve('stream-browserify'), + buffer: require.resolve('buffer'), + util: require.resolve('util'), + assert: require.resolve('assert'), + constants: require.resolve('constants-browserify'), + }, + // Prefer source files for @selfxyz/common so stack traces reference real filenames + resolveRequest: (context, moduleName, platform) => { + // Handle problematic Node.js modules that don't work in React Native + const nodeModuleRedirects = { + crypto: path.resolve(__dirname, 'src/polyfills/cryptoPolyfill.js'), + fs: false, // Disable filesystem access + os: false, // Disable OS-specific modules + readline: false, // Disable readline (pulls in events) + 'web-worker': false, // Disable web workers (not supported in React Native) + }; + + if (Object.prototype.hasOwnProperty.call(nodeModuleRedirects, moduleName)) { + if (nodeModuleRedirects[moduleName] === false) { + // Return empty module for disabled modules + return { type: 'empty' }; + } + // Redirect to polyfill + return { + type: 'sourceFile', + filePath: nodeModuleRedirects[moduleName], + }; + } + + // Let @selfxyz/common resolve through its package.json exports + // Remove custom resolution to let Metro handle it naturally + return context.resolveRequest(context, moduleName, platform); + }, + }, +}; + +module.exports = mergeConfig(defaultConfig, config); diff --git a/packages/mobile-sdk-demo/package.json b/packages/mobile-sdk-demo/package.json new file mode 100644 index 000000000..7069195b3 --- /dev/null +++ b/packages/mobile-sdk-demo/package.json @@ -0,0 +1,66 @@ +{ + "name": "mobile-sdk-demo", + "version": "0.0.1", + "private": true, + "main": "index.js", + "scripts": { + "analyze:bundle:android": "yarn prebuild && node scripts/bundle-analyze-ci.cjs android", + "analyze:bundle:ios": "yarn prebuild && node scripts/bundle-analyze-ci.cjs ios", + "preandroid": "yarn prebuild", + "android": "react-native run-android --verbose", + "prebuild": "yarn workspace @selfxyz/mobile-sdk-alpha build", + "build": "tsc -p tsconfig.json --noEmit --pretty false", + "clean": "rm -rf ios/build android/app/build android/build && cd android && ./gradlew clean && cd ..", + "fmt": "prettier --check .", + "fmt:fix": "prettier --write .", + "preios": "yarn prebuild", + "ios": "react-native run-ios", + "lint": "eslint .", + "lint:fix": "eslint --fix .", + "nice": "yarn lint:fix && yarn fmt:fix", + "start": "react-native start", + "test": "jest" + }, + "dependencies": { + "@babel/runtime": "^7.28.3", + "@noble/hashes": "^1.5.0", + "@react-native-picker/picker": "^2.11.1", + "@react-native/gradle-plugin": "0.76.9", + "@selfxyz/common": "workspace:*", + "@selfxyz/mobile-sdk-alpha": "workspace:*", + "assert": "^2.1.0", + "buffer": "^6.0.3", + "constants-browserify": "^1.0.0", + "ethers": "^6.11.0", + "find-yarn-workspace-root": "^2.0.0", + "react": "^18.3.1", + "react-native": "0.76.9", + "react-native-get-random-values": "^1.11.0", + "stream-browserify": "^3.0.0", + "tamagui": "1.126.14", + "util": "^0.12.5" + }, + "devDependencies": { + "@babel/core": "^7.28.3", + "@react-native-community/cli": "^16.0.3", + "@react-native/metro-config": "0.76.9", + "@tsconfig/react-native": "^3.0.6", + "@types/jest": "^29.5.14", + "@types/react": "^18.3.4", + "@typescript-eslint/eslint-plugin": "^8.44.0", + "@typescript-eslint/parser": "^8.44.0", + "babel-jest": "^29.6.3", + "eslint": "^8.57.0", + "eslint-config-prettier": "^10.1.8", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-sort-exports": "^0.9.1", + "jest": "^29.6.3", + "metro-react-native-babel-preset": "0.76.9", + "prettier": "^3.6.2", + "react-test-renderer": "^18.3.1", + "typescript": "^5.9.2" + } +} diff --git a/packages/mobile-sdk-demo/scripts/bundle-analyze-ci.cjs b/packages/mobile-sdk-demo/scripts/bundle-analyze-ci.cjs new file mode 100644 index 000000000..f99eef475 --- /dev/null +++ b/packages/mobile-sdk-demo/scripts/bundle-analyze-ci.cjs @@ -0,0 +1,98 @@ +#!/usr/bin/env node +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +const { execSync } = require('child_process'); +const { existsSync, statSync, unlinkSync } = require('fs'); +const os = require('os'); +const { join } = require('path'); + +const platform = process.argv[2]; +if (!platform || !['android', 'ios'].includes(platform)) { + console.error('Usage: bundle-analyze-ci.cjs '); + process.exit(1); +} + +// Bundle size thresholds in MB - adjusted for demo app +const BUNDLE_THRESHOLDS_MB = { + ios: 10, // Smaller threshold for demo app + android: 10, // Smaller threshold for demo app +}; + +function formatBytes(bytes) { + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + if (bytes === 0) return '0 Bytes'; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + ' ' + sizes[i]; +} + +function checkBundleSize(bundleSize, targetPlatform) { + const thresholdMB = BUNDLE_THRESHOLDS_MB[targetPlatform]; + const thresholdBytes = thresholdMB * 1024 * 1024; + + console.log(`\n📦 Demo App Bundle size: ${formatBytes(bundleSize)}`); + console.log(`🎯 Threshold: ${thresholdMB}MB (${formatBytes(thresholdBytes)})`); + + if (bundleSize > thresholdBytes) { + const overage = bundleSize - thresholdBytes; + console.error(`\n❌ Bundle size exceeds threshold by ${formatBytes(overage)}!`); + console.error(` Current: ${formatBytes(bundleSize)}`); + console.error(` Threshold: ${thresholdMB}MB`); + console.error(` Please reduce bundle size to continue.`); + console.error(`\n💡 To increase the threshold, edit BUNDLE_THRESHOLDS_MB in this script.`); + return false; + } else { + const remaining = thresholdBytes - bundleSize; + console.log(`✅ Demo app bundle size is within threshold (${formatBytes(remaining)} remaining)`); + return true; + } +} + +// Use Metro's built-in bundle command +const tmpDir = os.tmpdir(); +const bundleFile = join(tmpDir, `demo-app-${platform}.bundle`); +const sourcemapFile = join(tmpDir, `demo-app-${platform}.bundle.map`); + +console.log(`🔨 Generating demo app ${platform} bundle using Metro...`); + +try { + execSync( + `npx react-native bundle ` + + `--platform ${platform} ` + + `--dev false ` + + `--entry-file index.js ` + + `--bundle-output ${bundleFile} ` + + `--sourcemap-output ${sourcemapFile} ` + + `--minify false ` + + `--config metro.config.cjs ` + + `--reset-cache`, + { + stdio: 'inherit', + }, + ); +} catch (error) { + console.error(`❌ Failed to generate demo app bundle: ${error.message}`); + process.exit(1); +} + +// Check bundle size against threshold +if (existsSync(bundleFile)) { + const bundleSize = statSync(bundleFile).size; + console.log(`📁 Demo app bundle generated at: ${bundleFile}`); + if (!checkBundleSize(bundleSize, platform)) { + process.exit(1); + } + + // Clean up temporary files + try { + unlinkSync(bundleFile); + unlinkSync(sourcemapFile); + console.log('🧹 Cleaned up temporary bundle files'); + } catch (cleanupError) { + console.warn('⚠️ Could not clean up temporary files:', cleanupError.message); + } +} else { + console.error(`❌ Bundle file not found at ${bundleFile}`); + process.exit(1); +} diff --git a/packages/mobile-sdk-demo/src/DocumentCamera.tsx b/packages/mobile-sdk-demo/src/DocumentCamera.tsx new file mode 100644 index 000000000..055865fca --- /dev/null +++ b/packages/mobile-sdk-demo/src/DocumentCamera.tsx @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React from 'react'; +import { Button, ScrollView, StyleSheet, Text, View } from 'react-native'; + +type Props = { + onBack: () => void; +}; + +export default function DocumentCamera({ onBack }: Props) { + return ( + + Document Camera + Passport/ID Scanning + + + + This screen would handle camera-based document scanning for passports and ID cards. + + + + Features (Not Implemented): + • Camera integration for document scanning + • MRZ (Machine Readable Zone) detection + • Document validation and parsing + • Real-time feedback and guidance + + + +