Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 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
18 changes: 18 additions & 0 deletions app/src/components/homeScreen/SvgXmlWrapper.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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';
import type { StyleProp, ViewStyle } from 'react-native';
import { SvgXml as RNSvgXml } from 'react-native-svg';

Comment on lines +5 to +8
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

Fix ref forwarding and remove the any generic.

The wrapper declares forwardRef but drops the ref and uses any, which will break ref consumers and fail lint (“Unexpected any”). Forward the ref to RNSvgXml and type it.

Preferred patch:

-import React, { forwardRef } from 'react';
+import React, { forwardRef } from 'react';
+import type { ElementRef } from 'react';
@@
-export const SvgXml = forwardRef<any, Props>((p, _ref) => <RNSvgXml {...p} />);
+export const SvgXml = forwardRef<ElementRef<typeof RNSvgXml>, Props>(
+  (props, ref) => <RNSvgXml ref={ref} {...props} />
+);

If react-native-svg’s SvgXml doesn’t support refs in your version, drop forwardRef entirely to avoid confusion:

-import React, { forwardRef } from 'react';
+import React from 'react';
@@
-export const SvgXml = forwardRef<any, Props>((p, _ref) => <RNSvgXml {...p} />);
+export const SvgXml = (props: Props) => <RNSvgXml {...props} />;

To double-check impact, scan for ref usage on this component:

#!/bin/bash
# Find call sites that pass a ref to SvgXml wrapper
rg -nP --type=tsx -C2 '<SvgXml[^>]*\bref=' app

Also applies to: 16-18

🤖 Prompt for AI Agents
In app/src/components/homeScreen/SvgXmlWrapper.native.tsx lines 5-8 (also apply
same change at 16-18): the component declares forwardRef but drops the ref and
uses the `any` generic; update the wrapper to forward the ref through to
RNSvgXml and remove `any` by typing the ref properly (e.g. use React.Ref or
React.ComponentRef with typeof RNSvgXml and appropriate props types) so
consumers receive a correctly typed ref; if your version of react-native-svg
doesn’t expose refs for SvgXml, remove forwardRef entirely to avoid lying about
support.

type Props = {
xml: string;
width?: number;
height?: number;
style?: StyleProp<ViewStyle>;
};
Comment on lines +9 to +14
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

Allow string dimensions to prevent layout breakage.

react-native-svg and RN styles accept number | string (e.g., '100%'). Restricting to number risks runtime issues where percentages are expected.

 type Props = {
   xml: string;
-  width?: number;
-  height?: number;
+  width?: number | string;
+  height?: number | string;
   style?: StyleProp<ViewStyle>;
 };
📝 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
type Props = {
xml: string;
width?: number;
height?: number;
style?: StyleProp<ViewStyle>;
};
type Props = {
xml: string;
width?: number | string;
height?: number | string;
style?: StyleProp<ViewStyle>;
};
🤖 Prompt for AI Agents
In app/src/components/homeScreen/SvgXmlWrapper.native.tsx around lines 9 to 14,
the Props type currently restricts width and height to number which breaks cases
where percentages (e.g., '100%') are used; update the Props to allow number |
string for width and height, and ensure any places that pass these props to
react-native-svg or View style accept string values (cast or forward the values
unchanged) so percentage dimensions work without runtime errors.


export const SvgXml = forwardRef<any, Props>((p, _ref) => <RNSvgXml {...p} />);

Check warning on line 16 in app/src/components/homeScreen/SvgXmlWrapper.native.tsx

View workflow job for this annotation

GitHub Actions / build-deps

Unexpected any. Specify a different type
SvgXml.displayName = 'SvgXml';
export default SvgXml;
8 changes: 8 additions & 0 deletions app/src/components/homeScreen/SvgXmlWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// 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.

// Re-export from the native version - Metro resolver will automatically
// pick the appropriate platform-specific file (.web.tsx or .native.tsx)
export { SvgXml } from '@/components/homeScreen/SvgXmlWrapper.native';
export { default } from '@/components/homeScreen/SvgXmlWrapper.native';
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

'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