Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/actions/cache-built-deps/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: cache-built-deps
description: Cache built JS artifacts (common + mobile-sdk-alpha)
inputs:
cache-version:
description: Cache version string for cache key
required: true
outputs:
cache-hit:
description: Whether cache was hit during restore
value: ${{ steps.restore.outputs.cache-hit }}
runs:
using: composite
steps:
- id: restore
name: Restore Built Dependencies
uses: actions/cache/restore@v4
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ inputs.cache-version }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
fail-on-cache-miss: false
- name: Save Built Dependencies
if: steps.restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ inputs.cache-version }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
142 changes: 73 additions & 69 deletions .github/workflows/mobile-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,16 @@ jobs:
cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
- name: Install Dependencies
uses: ./.github/actions/yarn-install
- name: Restore Built Dependencies
id: built-deps-restore
uses: actions/cache/restore@v4
- name: Cache Built Dependencies
id: built-deps
uses: ./.github/actions/cache-built-deps
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
fail-on-cache-miss: false
cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
- name: Build dependencies (cache miss)
if: steps.built-deps-restore.outputs.cache-hit != 'true'
if: steps.built-deps.outputs.cache-hit != 'true'
run: |
echo "Cache miss for built dependencies. Building now..."
yarn workspace @selfxyz/mobile-app run build:deps
- name: Save Built Dependencies
if: steps.built-deps-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
- name: Run linter
run: yarn lint
working-directory: ./app
Expand Down Expand Up @@ -137,30 +125,70 @@ jobs:
cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
- name: Install Dependencies
uses: ./.github/actions/yarn-install
- name: Restore Built Dependencies
id: built-deps-restore
uses: actions/cache/restore@v4
- name: Cache Built Dependencies
id: built-deps
uses: ./.github/actions/cache-built-deps
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
fail-on-cache-miss: false
cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
- name: Debug Cache Restoration
run: |
echo "Cache hit: ${{ steps.built-deps.outputs.cache-hit }}"
echo "Cache key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}"
echo "Checking if cached files exist:"
ls -la packages/mobile-sdk-alpha/dist/ || echo "❌ mobile-sdk-alpha dist not found"
ls -la packages/mobile-sdk-alpha/dist/cjs/ || echo "❌ mobile-sdk-alpha dist/cjs not found"
ls -la packages/mobile-sdk-alpha/dist/cjs/index.cjs || echo "❌ mobile-sdk-alpha dist/cjs/index.cjs not found"
ls -la common/dist/ || echo "❌ common dist not found"
ls -la common/dist/cjs/ || echo "❌ common dist/cjs not found"
ls -la common/dist/cjs/index.cjs || echo "❌ common dist/cjs/index.cjs not found"
- name: Build dependencies (cache miss)
if: steps.built-deps-restore.outputs.cache-hit != 'true'
if: steps.built-deps.outputs.cache-hit != 'true'
run: |
echo "Cache miss for built dependencies. Building now..."
yarn workspace @selfxyz/mobile-app run build:deps
- name: Save Built Dependencies
if: steps.built-deps-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
# Verify build completed successfully
if [ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "common/dist/cjs/index.cjs" ]; then
echo "❌ Build failed - required files missing after build"
ls -la packages/mobile-sdk-alpha/dist/ || echo "mobile-sdk-alpha dist not found"
ls -la common/dist/ || echo "common dist not found"
exit 1
fi
echo "✅ Build completed successfully"
- name: Force Build Dependencies If Missing
run: |
# Force build if required files don't exist, regardless of cache status
if [ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "common/dist/cjs/index.cjs" ]; then
echo "❌ Required dependency files missing, forcing rebuild..."
echo "Missing files:"
[ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] && echo " - packages/mobile-sdk-alpha/dist/cjs/index.cjs"
[ ! -f "common/dist/cjs/index.cjs" ] && echo " - common/dist/cjs/index.cjs"
yarn workspace @selfxyz/mobile-app run build:deps
# Verify build completed successfully
if [ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "common/dist/cjs/index.cjs" ]; then
echo "❌ Forced build failed - required files still missing"
ls -la packages/mobile-sdk-alpha/ || echo "packages/mobile-sdk-alpha not found"
ls -la packages/mobile-sdk-alpha/dist/ || echo "mobile-sdk-alpha dist not found"
ls -la common/ || echo "common not found"
ls -la common/dist/ || echo "common dist not found"
exit 1
fi
echo "✅ Forced build completed successfully"
else
echo "✅ All required dependency files exist"
fi
- name: Test
run: yarn test
run: |
# Final verification from app directory perspective
echo "Final verification before running tests (from app directory)..."
if [ ! -f "../packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "../common/dist/cjs/index.cjs" ]; then
echo "❌ Dependencies still not found from app directory"
ls -la ../packages/mobile-sdk-alpha/dist/ || echo "mobile-sdk-alpha dist not found"
ls -la ../common/dist/ || echo "common dist not found"
exit 1
fi
echo "✅ All dependencies verified, running tests..."
# Run jest through yarn to avoid the build:deps step since CI already built dependencies
yarn jest --passWithNoTests && node --test scripts/tests/*.cjs
working-directory: ./app
build-ios:
runs-on: macos-latest-large
Expand Down Expand Up @@ -257,28 +285,16 @@ jobs:
${{ runner.os }}-xcode-index-${{ env.XCODE_VERSION }}-
- name: Install Mobile Dependencies
uses: ./.github/actions/yarn-install
- name: Restore Built Dependencies
id: built-deps-restore
uses: actions/cache/restore@v4
- name: Cache Built Dependencies
id: built-deps
uses: ./.github/actions/cache-built-deps
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
fail-on-cache-miss: false
cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
- name: Build dependencies (cache miss)
if: steps.built-deps-restore.outputs.cache-hit != 'true'
if: steps.built-deps.outputs.cache-hit != 'true'
run: |
echo "Cache miss for built dependencies. Building now..."
yarn workspace @selfxyz/mobile-app run build:deps
- name: Save Built Dependencies
if: steps.built-deps-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
- name: Install Ruby Dependencies
run: |
echo "Installing Ruby dependencies..."
Expand Down Expand Up @@ -416,28 +432,16 @@ jobs:
run: sdkmanager "ndk;${{ env.ANDROID_NDK_VERSION }}"
- name: Install Mobile Dependencies
uses: ./.github/actions/yarn-install
- name: Restore Built Dependencies
id: built-deps-restore
uses: actions/cache/restore@v4
- name: Cache Built Dependencies
id: built-deps
uses: ./.github/actions/cache-built-deps
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
fail-on-cache-miss: false
cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
- name: Build dependencies (cache miss)
if: steps.built-deps-restore.outputs.cache-hit != 'true'
if: steps.built-deps.outputs.cache-hit != 'true'
run: |
echo "Cache miss for built dependencies. Building now..."
yarn workspace @selfxyz/mobile-app run build:deps
- name: Save Built Dependencies
if: steps.built-deps-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
common/dist
packages/mobile-sdk-alpha/dist
key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}
- name: Build Android (with AAPT2 symlink fix)
Comment on lines +436 to 445
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

NDK cache should use a composite and gate installation on cache-hit.

Direct actions/cache calls violate policy and always running sdkmanager wastes minutes. Wrap NDK caching in a composite that exposes cache-hit, then install only on miss.

-      - name: Cache NDK
-        uses: actions/cache@v4
-        with:
-          path: ${{ env.ANDROID_HOME }}/ndk/${{ env.ANDROID_NDK_VERSION }}
-          key: ${{ runner.os }}-ndk-${{ env.ANDROID_NDK_VERSION }}
-      - name: Install NDK
-        run: sdkmanager "ndk;${{ env.ANDROID_NDK_VERSION }}"
+      - name: Cache NDK
+        id: ndk-cache
+        uses: ./.github/actions/cache-ndk
+        with:
+          cache-version: ${{ env.GH_CACHE_VERSION }}-ndk${{ env.ANDROID_NDK_VERSION }}
+          ndk-path: ${{ env.ANDROID_HOME }}/ndk/${{ env.ANDROID_NDK_VERSION }}
+      - name: Install NDK (cache miss)
+        if: steps.ndk-cache.outputs.cache-hit != 'true'
+        run: sdkmanager "ndk;${{ env.ANDROID_NDK_VERSION }}"

New composite (add file .github/actions/cache-ndk/action.yml):

name: cache-ndk
description: Cache Android NDK
inputs:
  cache-version:
    required: true
  ndk-path:
    required: true
outputs:
  cache-hit:
    value: ${{ steps.restore.outputs.cache-hit }}
runs:
  using: composite
  steps:
    - id: restore
      uses: actions/cache/restore@v4
      with:
        path: ${{ inputs.ndk-path }}
        key: ndk-${{ inputs.cache-version }}
        fail-on-cache-miss: false
    - if: steps.restore.outputs.cache-hit != 'true'
      uses: actions/cache/save@v4
      with:
        path: ${{ inputs.ndk-path }}
        key: ndk-${{ inputs.cache-version }}

run: yarn android:ci
working-directory: ./app
2 changes: 1 addition & 1 deletion .github/workflows/web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ jobs:
shell: bash
run: yarn workspace @selfxyz/common build
- name: Build web app
run: yarn web:build
run: yarn workspace @selfxyz/mobile-app web:build
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
22.12.0
12 changes: 12 additions & 0 deletions app/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ module.exports = {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
extensions: [
'.ts',
'.tsx',
'.native.ts',
'.native.tsx',
'.web.ts',
'.web.tsx',
'.ios.ts',
'.ios.tsx',
'.android.ts',
'.android.tsx',
],
},
},
'import/ignore': ['react-native'],
Expand Down
2 changes: 1 addition & 1 deletion app/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = {
preset: 'react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
transformIgnorePatterns: [
'node_modules/(?!(react-native|@react-native|@react-navigation|@react-native-community|@segment/analytics-react-native|@openpassport|react-native-keychain|react-native-check-version|react-native-nfc-manager|react-native-passport-reader|react-native-gesture-handler|uuid|@stablelib|@react-native-google-signin|react-native-cloud-storage|@react-native-clipboard|@react-native-firebase|@selfxyz)/)',
'node_modules/(?!(react-native|@react-native|@react-navigation|@react-native-community|@segment/analytics-react-native|@openpassport|react-native-keychain|react-native-check-version|react-native-nfc-manager|react-native-passport-reader|react-native-gesture-handler|uuid|@stablelib|@react-native-google-signin|react-native-cloud-storage|@react-native-clipboard|@react-native-firebase|@selfxyz|@sentry)/)',
],
setupFiles: ['<rootDir>/jest.setup.js'],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$',
Expand Down
2 changes: 2 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
"@testing-library/react-native": "^13.3.3",
"@tsconfig/react-native": "^3.0.6",
"@types/add": "^2",
"@types/dompurify": "^3.2.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Move dompurify to dependencies (runtime), keep @types in devDeps.

The web SvgXml wrapper imports dompurify at runtime. Having it in devDependencies risks missing module errors in production web builds and CI. @types/dompurify can stay in devDependencies.

Apply:

--- a/app/package.json
+++ b/app/package.json
@@
   "dependencies": {
@@
+    "dompurify": "^3.2.6",
@@
   },
   "devDependencies": {
@@
-    "dompurify": "^3.2.6",
@@
   }

Also applies to: 174-174

🤖 Prompt for AI Agents
In app/package.json around lines 160 and 174, dompurify is currently listed
under devDependencies while @types/dompurify should remain in devDependencies;
move the runtime dependency "dompurify" from devDependencies to dependencies
(keeping "@types/dompurify" in devDependencies) so the module is available in
production and CI builds, updating the package.json accordingly and ensuring
version string is preserved.

"@types/elliptic": "^6",
"@types/jest": "^29.5.14",
"@types/node-forge": "^1.3.14",
Expand All @@ -170,6 +171,7 @@
"@typescript-eslint/parser": "^8.39.0",
"@vitejs/plugin-react-swc": "^3.10.2",
"babel-plugin-module-resolver": "^5.0.2",
"dompurify": "^3.2.6",
"eslint": "^8.57.0",
"eslint-config-prettier": "10.1.8",
"eslint-import-resolver-typescript": "^3.7.0",
Expand Down
20 changes: 20 additions & 0 deletions app/src/components/homeScreen/SvgXmlWrapper.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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, { forwardRef } from 'react';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { SvgXml: RNSvgXml } = require('react-native-svg');

type Props = {
xml: string;
width?: number;
height?: number;
style?: unknown;
};

export const SvgXml = forwardRef<React.ComponentRef<typeof RNSvgXml>, Props>(
(p, ref) => <RNSvgXml ref={ref} {...p} />,
);
SvgXml.displayName = 'SvgXml';
export default SvgXml;
16 changes: 16 additions & 0 deletions app/src/components/homeScreen/SvgXmlWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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 { Platform } from 'react-native';

// Platform-specific dynamic import
if (Platform.OS === 'web') {
// Web platform - use DOMPurify sanitized version
const webModule = require('@/components/homeScreen/SvgXmlWrapper.web');

Check failure on line 10 in app/src/components/homeScreen/SvgXmlWrapper.ts

View workflow job for this annotation

GitHub Actions / build-deps

A `require()` style import is forbidden
module.exports = webModule;
} else {
// Native platforms - use react-native-svg directly
const nativeModule = require('@/components/homeScreen/SvgXmlWrapper.native');

Check failure on line 14 in app/src/components/homeScreen/SvgXmlWrapper.ts

View workflow job for this annotation

GitHub Actions / build-deps

A `require()` style import is forbidden
module.exports = nativeModule;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Dynamic require violates lint and is unnecessary; rely on platform file resolution.

Metro/Vite can pick SvgXmlWrapper.native.tsx / SvgXmlWrapper.web.tsx automatically. This file introduces forbidden require() and breaks CI.

Recommended:

  • Delete this file and import from '@/components/homeScreen/SvgXmlWrapper' everywhere; bundlers will resolve to the platform variant.
  • If you must keep it temporarily, disable the rule locally to unblock CI (not preferred):
@@
-// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
+// 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.
 
+/* eslint-disable @typescript-eslint/no-require-imports */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Platform-specific dynamic import
if (Platform.OS === 'web') {
// Web platform - use DOMPurify sanitized version
const webModule = require('@/components/homeScreen/SvgXmlWrapper.web');
module.exports = webModule;
} else {
// Native platforms - use react-native-svg directly
const nativeModule = require('@/components/homeScreen/SvgXmlWrapper.native');
module.exports = nativeModule;
}
// 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.
/* eslint-disable @typescript-eslint/no-require-imports */
// Platform-specific dynamic import
if (Platform.OS === 'web') {
// Web platform - use DOMPurify sanitized version
const webModule = require('@/components/homeScreen/SvgXmlWrapper.web');
module.exports = webModule;
} else {
// Native platforms - use react-native-svg directly
const nativeModule = require('@/components/homeScreen/SvgXmlWrapper.native');
module.exports = nativeModule;
}
🧰 Tools
🪛 GitHub Check: build-deps

[failure] 14-14:
A require() style import is forbidden


[failure] 10-10:
A require() style import is forbidden

🪛 GitHub Actions: Mobile CI

[error] 10-10: ESLint: '@typescript-eslint/no-require-imports' - A 'require()' style import is forbidden.

🤖 Prompt for AI Agents
In app/src/components/homeScreen/SvgXmlWrapper.ts around lines 7 to 16, this
platform-switching file uses dynamic require() which violates lint rules and is
unnecessary because the bundler resolves SvgXmlWrapper.web/native automatically;
remove this file and update all imports to import from
'@/components/homeScreen/SvgXmlWrapper' (no extension) so Metro/Vite will pick
the correct platform file, then run lint/CI to confirm fixes; if you cannot
remove it immediately, as a short-lived fallback add a top-of-file
eslint-disable for the specific rule and a TODO to delete it once all imports
are updated (preferred: delete).

37 changes: 37 additions & 0 deletions app/src/components/homeScreen/SvgXmlWrapper.web.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 DOMPurify from 'dompurify';

Check warning on line 5 in app/src/components/homeScreen/SvgXmlWrapper.web.tsx

View workflow job for this annotation

GitHub Actions / build-deps

Using exported name 'DOMPurify' as identifier for default import
import React, { createElement, forwardRef } from 'react';

type Props = {
xml: string;
width?: number;
height?: number;
style?: React.CSSProperties;
} & React.HTMLAttributes<HTMLDivElement>;

export const SvgXml = forwardRef<HTMLDivElement, Props>(
({ xml, width, height, style, ...props }, ref) => {
// Initialize DOMPurify for web browser environment
const purify = DOMPurify(window);
const safe = purify.sanitize(xml, {
USE_PROFILES: { svg: true, svgFilters: true },
});
Comment on lines +15 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard DOMPurify factory for SSR/test environments and avoid re-instantiation.

DOMPurify(window) will throw if window is undefined (SSR, some tests). Add a guard and memoize the instance per mount.

Apply:

-import DOMPurify from 'dompurify';
-import React, { createElement, forwardRef } from 'react';
+import createDOMPurify from 'dompurify';
+import React, { createElement, forwardRef, useMemo } from 'react';
@@
-export const SvgXml = forwardRef<HTMLDivElement, Props>(
-  ({ xml, width, height, style, ...props }, ref) => {
-    // Initialize DOMPurify for web browser environment
-    const purify = DOMPurify(window);
-    const safe = purify.sanitize(xml, {
+export const SvgXml = forwardRef<HTMLDivElement, Props>(
+  ({ xml, width, height, style, ...props }, ref) => {
+    const purify = useMemo(
+      () => (typeof window !== 'undefined' ? createDOMPurify(window) : null),
+      [],
+    );
+    const safe = purify?.sanitize(xml, {
       USE_PROFILES: { svg: true, svgFilters: true },
-    });
+    }) ?? '';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const SvgXml = forwardRef<HTMLDivElement, Props>(
({ xml, width, height, style, ...props }, ref) => {
// Initialize DOMPurify for web browser environment
const purify = DOMPurify(window);
const safe = purify.sanitize(xml, {
USE_PROFILES: { svg: true, svgFilters: true },
});
// Before:
-import DOMPurify from 'dompurify';
import createDOMPurify from 'dompurify';
import React, { createElement, forwardRef, useMemo } from 'react';
export const SvgXml = forwardRef<HTMLDivElement, Props>(
- ({ xml, width, height, style, ...props }, ref) => {
- // Initialize DOMPurify for web browser environment
- const purify = DOMPurify(window);
({ xml, width, height, style, ...props }, ref) => {
// Guard for SSR/test and memoize to avoid re-instantiation
const purify = useMemo(
() => (typeof window !== 'undefined' ? createDOMPurify(window) : null),
[],
);
const safe = purify?.sanitize(xml, {
USE_PROFILES: { svg: true, svgFilters: true },
}) ?? '';
// …rest of rendering logic…
}
);
🤖 Prompt for AI Agents
In app/src/components/homeScreen/SvgXmlWrapper.web.tsx around lines 15 to 21,
calling DOMPurify(window) unguarded will throw in SSR/tests where window is
undefined and currently re-instantiates on every render; fix by checking for
typeof window !== "undefined" before calling DOMPurify and creating a single
memoized instance (use useMemo or useRef to create it once per mount), and fall
back to a no-op sanitizer when DOMPurify or window is unavailable; then use that
memoized sanitizer to call sanitize(xml, { USE_PROFILES: { svg: true,
svgFilters: true } }).

return createElement('div', {
ref,
style: {
width: width || 'auto',
height: height || 'auto',
display: 'inline-block',
...style,
},
dangerouslySetInnerHTML: { __html: safe },
...props,
});
},
);

SvgXml.displayName = 'SvgXml';
export default SvgXml;
10 changes: 5 additions & 5 deletions app/src/components/homeScreen/idCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@
// SPDX-License-Identifier: BUSL-1.1
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.

import React, { useCallback, useState } from 'react';

Check warning on line 5 in app/src/components/homeScreen/idCard.tsx

View workflow job for this annotation

GitHub Actions / build-deps

'useState' is defined but never used

Check warning on line 5 in app/src/components/homeScreen/idCard.tsx

View workflow job for this annotation

GitHub Actions / build-deps

'useCallback' is defined but never used
import { Dimensions, Pressable } from 'react-native';

Check warning on line 6 in app/src/components/homeScreen/idCard.tsx

View workflow job for this annotation

GitHub Actions / build-deps

'Pressable' is defined but never used
import { SvgXml } from 'react-native-svg';
import { Button, Image, Separator, Text, XStack, YStack } from 'tamagui';

Check warning on line 7 in app/src/components/homeScreen/idCard.tsx

View workflow job for this annotation

GitHub Actions / build-deps

'Image' is defined but never used

Check warning on line 7 in app/src/components/homeScreen/idCard.tsx

View workflow job for this annotation

GitHub Actions / build-deps

'Button' is defined but never used
import { useFocusEffect } from '@react-navigation/native';

import {
attributeToPosition,
attributeToPosition_ID,
formatMrz,
PassportData,
} from '@selfxyz/common/dist/esm';
import { pad } from '@selfxyz/common/dist/esm/src/utils/passports/passport';
} from '@selfxyz/common/constants';
import { PassportData } from '@selfxyz/common/types';
import { formatMrz } from '@selfxyz/common/utils';
import { pad } from '@selfxyz/common/utils/passports/passport';

import { SvgXml } from '@/components/homeScreen/SvgXmlWrapper';
import EPassport from '@/images/icons/epassport.svg';
import LogoGray from '@/images/logo_gray.svg';
import LogoInversed from '@/images/logo_inversed.svg';
Expand Down
Loading
Loading