diff --git a/docs/gigamind/pr-analysis-workflow.md b/docs/gigamind/pr-analysis-workflow.md index d86bfebf8..82f259307 100644 --- a/docs/gigamind/pr-analysis-workflow.md +++ b/docs/gigamind/pr-analysis-workflow.md @@ -1,193 +1,151 @@ -# PR Analysis Workflow - GigaMind Export +# PR Analysis Workflow -## Overview -This document exports all the neurons related to the PR analysis workflow that fetches PR comments and creates actionable issue files. This workflow is used to analyze GitHub PRs for action items and create comprehensive PR-{{NUMBER}}-ACTION_ITEMS.md files. +## Objective +Ensure consistent identification of only **medium to critical unresolved non-nitpick** comments from CodeRabbit PR reviews. -## The Prompt to Enable This Workflow - -To enable this workflow, use this prompt: +## Critical Filtering Rules +### 1. Status Verification (MANDATORY) ``` -giga fetch PR {{NUMBER}} and create an issue file +✅ INCLUDE: Comments without "✅ Addressed" status +❌ EXCLUDE: Comments with "✅ Addressed in commits X to Y" ``` -Replace `{{NUMBER}}` with the actual PR number you want to analyze. - -## Core Workflow Neuron - -### PR Analysis Workflow - v6 #w1x2y3 -When analyzing GitHub PRs for action items OR when asked to 'giga fetch PR {{NUMBER}} and create an issue file: 1) Use giga_read_pr to fetch PR data and CodeRabbit comments, 2) Focus on unresolved comments that DON'T have '✅ Addressed' status, 3) Group related issues by root cause (not just severity) - e.g., multiple DOM element issues → single React Native compatibility problem, 4) Prioritize by 'blocking merge' vs 'architectural' vs 'polish', 5) ALWAYS create PR-{{NUMBER}}-ACTION_ITEMS.md in repo root, 6) Use template from docs/templates/pr-action-items-template.md, 7) Focus on specific file paths, line numbers, and clear fixes, 8) Include code examples for complex fixes, 9) Add testing requirements and breaking changes, 10) NEVER use mcp_giga_plan - create real markdown files. An item is 'actionable' if it has: specific file path and line number, clear problem description, specific fix or action required, and priority level (blocking vs non-blocking). Always group related comments by root cause rather than treating each comment separately. - -## Supporting Neurons - -### PR Testing Status Tracking - v1 #s1t2u3 -When creating PR action items, always include a comprehensive testing status section. Track which test suites are passing vs failing, and include specific error messages when available. Common patterns: "yarn workspace @selfxyz/package test" failures, build errors, lint issues. Include both the current status and required actions to fix failing tests. This helps prioritize which issues are blocking vs non-blocking. - -### iOS Build Workspace Path Fix - v1 #a1b2c3 -The iOS build was failing with "Found no destinations for the scheme OpenPassport" because the fastlane configuration was using the wrong workspace path. The issue was in app/fastlane/Fastfile line 204 where it was constructing the path as "../ios/#{PROJECT_NAME}.xcworkspace" (which became "../ios/Self.xcworkspace") but the actual workspace file is "OpenPassport.xcworkspace". The fix was to hardcode the workspace path to "../ios/OpenPassport.xcworkspace" since the project name (Self) and workspace name (OpenPassport) are different. This is a common issue when the Xcode project name differs from the workspace name. - -### Security Template Vulnerabilities - v1 #m4n5o6 -When reviewing PR templates or documentation templates, always check for missing security warnings about credential exposure. Common vulnerability: templates that include code example sections without warnings about not pasting secrets, private keys, API tokens, or mnemonics. Always add security notes before code blocks in templates to prevent accidental credential leaks during code reviews. - -### iOS Workspace Path Dynamic Resolution - v2 #d4e5f6 -The iOS build workspace path should use dynamic resolution based on the scheme name rather than hardcoding. The optimal solution is: workspace_path = File.expand_path("../ios/#{PROJECT_SCHEME}.xcworkspace", Dir.pwd). This approach: 1) Uses the existing IOS_PROJECT_SCHEME environment variable (OpenPassport), 2) Assumes workspace name matches scheme name (OpenPassport.xcworkspace), 3) Is more flexible and maintainable than hardcoding, 4) Follows the pattern where scheme name and workspace name are consistent. This is better than hardcoding or using PROJECT_NAME since the workspace and scheme names match in this project structure. - -### Dependency Hoisting Issues - v1 #p7q8r9 -When packages import dependencies directly but don't declare them in their own package.json, this can cause hoisting/Metro/ESM resolution issues. Common pattern: package A imports 'uuid' but only package B declares it as dependency. Solution: Add the dependency to the package.json of any workspace that imports it directly. This prevents build failures in certain environments and ensures proper dependency resolution. - -### iOS Dependencies Bundle Exec Fix - v1 #g7h8i9 -When iOS build fails with "bundler: failed to load command: pod" and missing gem errors, the issue is that the system is trying to use global Ruby gems instead of the project's bundled gems. The solution is to: 1) Run "bundle install" in the app directory to install project dependencies, 2) Use "bundle exec pod install" instead of just "pod install" to ensure the bundled gems are used, 3) Run the command from the ios directory where the Podfile is located. The project uses Bundler to manage Ruby dependencies and has a Gemfile with specific versions of CocoaPods, Fastlane, and other gems. Always use "bundle exec" prefix for Ruby commands in this project to ensure consistent dependency versions. - -### Template File Management - v1 #t1u2v3 -Template files use kebab-case naming: 'pr-action-items-template.md' (with hyphens), NOT 'pr_action_items_template.md' (with underscores). The template is located at docs/templates/pr-action-items-template.md and should be used to create PR-specific action items files in the repo root as PR-{{NUMBER}}-ACTION_ITEMS.md. This ensures visibility and easy access for all team members reviewing the PR. When searching for or referencing template files, always use the correct kebab-case format. - -### Fastlane Workspace Path Robust Resolution - v1 #j1k2l3 -The fastlane workspace path resolution should be robust to handle cases where workspace filename differs from scheme name. The implementation should: 1) First try scheme-named workspace (e.g., OpenPassport.xcworkspace for OpenPassport scheme), 2) Fall back to project-named workspace (e.g., Self.xcworkspace for Self project) if scheme-named doesn't exist, 3) Raise a clear error with checked paths if neither exists to fail CI fast. This prevents build failures when workspace and scheme names don't match, and provides clear debugging information when no workspace is found. The logic should be: scheme_workspace_path = File.expand_path("../ios/#{PROJECT_SCHEME}.xcworkspace", Dir.pwd); project_workspace_path = File.expand_path("../ios/#{PROJECT_NAME}.xcworkspace", Dir.pwd); then check File.exist? on each path in order of preference. - -### PR Action Items Template - v2 #zfda91 -The PR action items template should be value-first and focused on actionable content. Structure: 1) Critical Issues (Blocking Merge) - specific file:line with clear actions, 2) Required Actions - grouped by root cause with specific fixes, 3) Testing Checklist - specific tests to run, 4) Breaking Changes - specific changes and migration needs. Remove generic sections that don't apply to most PRs. Focus on problem → solution → validation pattern. Template location: docs/templates/pr-action-items-template.md, generated files: PR-{{NUMBER}}-ACTION_ITEMS.md in project root. Prioritize actionable items over pretty formatting. Always include specific file paths, line numbers, and code examples for complex fixes. - -### Neuron Naming Convention - v1 -When creating new neurons, ALWAYS add a unique short hash (6-8 characters) to the end of the title to make neurons easier to find and distinguish. This hash should be unique for EACH neuron and automatically updated when creating new neurons. Use format: "Title - v1 #abc123" or "Title - v2 #def456". This helps with neuron discovery, prevents naming conflicts, and makes it easier to track neuron versions and updates. +### 2. Severity Classification +``` +✅ CRITICAL: Security vulnerabilities, memory leaks, breaking platform compatibility +✅ HIGH: API inconsistencies, type safety issues, significant architectural problems +✅ MEDIUM: Test coverage gaps, minor architectural improvements, performance concerns +❌ LOW/NITPICK: Style, naming, documentation, minor suggestions +``` -### Prefer markdown file lists over plans -Never create plans using the plan tool. Instead, prefer creating markdown file lists for task tracking, documentation, or organized information when needed. +### 3. Comment Type Mapping +``` +CodeRabbit Labels → Severity Level: +⚠️ "Potential issue" → HIGH/CRITICAL (include) +🛠️ "Refactor suggestion" → MEDIUM/HIGH (evaluate content) +💡 "Verification agent" → MEDIUM (include if architectural) +_suggestion_ → LOW (exclude unless security/performance) +``` -## Template File +## Step-by-Step Process + +### Phase 1: Data Collection +1. Use `giga_read_pr` to fetch PR data and all CodeRabbit comments +2. Count total comments for verification +3. Extract comment metadata: ID, type, status, file paths, line numbers + +### Phase 2: Initial Filtering +For each comment: +1. **Check Status**: Skip if contains "✅ Addressed" +2. **Check Type**: Identify comment category (security, architecture, style, etc.) +3. **Check Severity**: Map to CRITICAL/HIGH/MEDIUM/LOW scale +4. **Apply Filters**: Only keep MEDIUM+ severity unresolved comments + +### Phase 3: Content Analysis +For remaining comments: +1. **Read Full Content**: Understand the actual issue +2. **Categorize Impact**: + - Blocks merge? → CRITICAL + - Affects architecture/API? → HIGH + - Improves quality? → MEDIUM + - Cosmetic only? → LOW (exclude) +3. **Group by Root Cause**: Related issues together + +### Phase 4: Verification (MANDATORY) +1. **Double-check Status**: Re-verify no "✅ Addressed" comments included +2. **Count Verification**: Ensure unresolved count matches filtered list +3. **Severity Audit**: Confirm each issue is truly MEDIUM+ impact +4. **Template Selection**: + - If 0 unresolved → Use "All issues resolved" template + - If >0 unresolved → Use standard template with issues + +### Phase 5: Documentation +1. **Create Action Items**: Use updated template with verification checklist +2. **Provide Evidence**: Include comment IDs, file paths, line numbers +3. **Add Code Examples**: Show current issue and proposed fix +4. **Track Metrics**: Total comments, unresolved count, excluded count + +## Quality Assurance + +### Red Flags (Double-check if you see these) +- High unresolved count (>5) - likely including nitpicks +- All comments marked unresolved - likely missing status checks +- Style/formatting issues in critical section - wrong severity classification +- Vague descriptions - insufficient content analysis + +### Green Flags (Good indicators) +- Low unresolved count (0-3) - proper filtering +- Clear severity justification - good analysis +- Specific file paths and line numbers - thorough review +- Code examples for fixes - actionable output + +## Common Mistakes to Avoid + +### ❌ Don't Include: +- Comments with "✅ Addressed" status +- Style/formatting suggestions +- Documentation improvements (unless critical) +- Naming conventions +- Minor refactoring suggestions +- Import organization +- Code comments/JSDoc additions + +### ✅ Do Include: +- Security vulnerabilities +- Memory leaks +- Platform compatibility issues (DOM in React Native) +- API design inconsistencies +- Type safety problems +- Performance bottlenecks +- Test coverage gaps for critical paths +- Breaking changes without migration + +## Template Usage + +### When All Issues Resolved (Most Common) +```markdown +## Analysis Summary +**After thorough review of all 15 CodeRabbit comments, ALL issues have been resolved in subsequent commits. The PR is ready for merge.** -The workflow uses this template file located at `docs/templates/pr-action-items-template.md`: +### Unresolved Comments 🔴 (0/15) +**None** - All comments have been addressed. +``` +### When Unresolved Issues Exist (Rare) ```markdown - - -# PR {{PR_NUMBER}} Action Items - -## Overview -**Title:** {{PR_TITLE}} -**Author:** {{AUTHOR}} -**Status:** {{STATUS}} -**Created:** {{DATE}} -**Branch:** {{BRANCH}} - -{{PR_SUMMARY}} - ## Critical Issues (Blocking Merge) -### 1. {{ISSUE_TITLE}} -**Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}` -**Problem:** {{PROBLEM_DESCRIPTION}} -**Fix:** {{SPECIFIC_FIX_OR_ACTION}} - -### 2. {{ISSUE_TITLE}} -**Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}` -**Problem:** {{PROBLEM_DESCRIPTION}} -**Fix:** {{SPECIFIC_FIX_OR_ACTION}} - -## Required Actions - -### Issue 1: {{GROUPED_ISSUE_TITLE}} -**Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}`, `{{FILE_PATH}}:{{LINE_NUMBER}}` -**Root Cause:** {{ROOT_CAUSE_DESCRIPTION}} - -**Actions:** -- [ ] {{SPECIFIC_ACTION_1}} -- [ ] {{SPECIFIC_ACTION_2}} -- [ ] {{SPECIFIC_ACTION_3}} - -**Code Example:** -```{{LANGUAGE}} -{{CODE_EXAMPLE}} +### 1. Security Vulnerability - PII in Logs +**Files:** `src/logger.ts:45` +**CodeRabbit Comment:** 2286479767 +**Problem:** Logging sensitive user data without redaction +**Status:** ⚠️ **CRITICAL** - Security risk, blocks merge ``` -### Issue 2: {{GROUPED_ISSUE_TITLE}} -**Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}` -**Root Cause:** {{ROOT_CAUSE_DESCRIPTION}} - -**Actions:** -- [ ] {{SPECIFIC_ACTION_1}} -- [ ] {{SPECIFIC_ACTION_2}} - -## Testing Checklist - -### Before Merge -- [ ] {{SPECIFIC_TEST_TO_RUN}} -- [ ] {{SPECIFIC_VALIDATION}} -- [ ] {{SPECIFIC_BUILD_TEST}} - -### Post-Merge -- [ ] {{INTEGRATION_TEST}} -- [ ] {{PERFORMANCE_TEST}} - -## Breaking Changes - -### For Consumers -- [ ] {{SPECIFIC_BREAKING_CHANGE}} -- [ ] {{MIGRATION_NEEDED}} - -### Migration Guide -- [ ] Update import statements -- [ ] Replace deprecated API calls -- [ ] Handle new dependencies - -## Implementation Priority - -### Phase 1: Critical Fixes (Blocking Merge) -1. {{CRITICAL_FIX_1}} -2. {{CRITICAL_FIX_2}} - -### Phase 2: Architecture Improvements -1. {{ARCHITECTURE_FIX_1}} -2. {{ARCHITECTURE_FIX_2}} - -### Phase 3: Polish & Documentation -1. {{POLISH_ITEM_1}} -2. {{POLISH_ITEM_2}} - -## Notes - -- {{SPECIFIC_NOTE_1}} -- {{SPECIFIC_NOTE_2}} -- {{SPECIFIC_NOTE_3}} - ---- - -**Last Updated:** {{DATE}} -``` +## Metrics Tracking -## Workflow Steps Summary +Track these metrics for each PR analysis: +- Total CodeRabbit comments analyzed +- Comments with "✅ Addressed" status (excluded) +- Low/nitpick severity comments (excluded) +- Medium+ severity unresolved comments (included) +- Final unresolved count in action items -1. **Fetch PR Data**: Use `giga_read_pr` to get PR details and CodeRabbit comments -2. **Filter Comments**: Focus on unresolved comments without '✅ Addressed' status -3. **Group Issues**: Group related issues by root cause, not just severity -4. **Prioritize**: Categorize as 'blocking merge' vs 'architectural' vs 'polish' -5. **Create File**: Generate PR-{{NUMBER}}-ACTION_ITEMS.md in repo root -6. **Use Template**: Apply the template from docs/templates/pr-action-items-template.md -7. **Include Details**: Add specific file paths, line numbers, and clear fixes -8. **Add Examples**: Include code examples for complex fixes -9. **Add Testing**: Include testing requirements and breaking changes -10. **Avoid Plans**: Never use mcp_giga_plan - create real markdown files +## Continuous Improvement -## Key Principles +### Weekly Review +- Audit recent PR action items for false positives/negatives +- Check if any "resolved" issues were actually unresolved +- Verify severity classifications were accurate +- Update filtering rules based on patterns -- **Actionable Items**: Must have specific file path, line number, clear problem description, specific fix, and priority level -- **Root Cause Grouping**: Group related comments by root cause rather than treating each separately -- **Value-First**: Focus on actionable content over pretty formatting -- **Specific Details**: Always include file paths, line numbers, and code examples for complex fixes -- **Testing Focus**: Include comprehensive testing status and requirements +### Template Updates +- Add new comment type mappings as CodeRabbit evolves +- Refine severity criteria based on project needs +- Update verification checklist based on common mistakes +- Enhance automation where possible --- -**Export Date:** {{CURRENT_DATE}} -**Neuron Count:** 12 neurons exported -**Template Version:** Current as of export +**Remember**: It's better to exclude a borderline issue than include a nitpick. The goal is actionable, high-impact feedback only. diff --git a/docs/templates/pr-action-items-template.md b/docs/templates/pr-action-items-template.md index 3d0577acf..0581cd0e5 100644 --- a/docs/templates/pr-action-items-template.md +++ b/docs/templates/pr-action-items-template.md @@ -1,13 +1,42 @@ # PR {{PR_NUMBER}} Action Items @@ -21,23 +50,52 @@ INSTRUCTIONS FOR AGENTS: {{PR_SUMMARY}} +## Analysis Summary + + +**After thorough review of all {{TOTAL_COMMENTS}} CodeRabbit comments, ALL issues have been resolved in subsequent commits. The PR is ready for merge.** + +### Resolved Comments ✅ ({{TOTAL_COMMENTS}}/{{TOTAL_COMMENTS}}) +All comments show ✅ "Addressed" status, indicating they were fixed in commits: +- {{RESOLVED_CATEGORY_1}} - Fixed in commits {{COMMIT_RANGE}} +- {{RESOLVED_CATEGORY_2}} - Fixed in commits {{COMMIT_RANGE}} + +### Unresolved Comments 🔴 (0/{{TOTAL_COMMENTS}}) +**None** - All comments have been addressed. + +## Conclusion +**This PR is ready for merge.** All CodeRabbit issues have been resolved. + + + ## Critical Issues (Blocking Merge) + + ### 1. {{ISSUE_TITLE}} **Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}` +**CodeRabbit Comment:** {{COMMENT_ID}} **Problem:** {{PROBLEM_DESCRIPTION}} **Fix:** {{SPECIFIC_FIX_OR_ACTION}} +**Code Example:** +```{{LANGUAGE}} +{{CODE_EXAMPLE}} +``` + ### 2. {{ISSUE_TITLE}} **Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}` +**CodeRabbit Comment:** {{COMMENT_ID}} **Problem:** {{PROBLEM_DESCRIPTION}} **Fix:** {{SPECIFIC_FIX_OR_ACTION}} ## Required Actions ### Issue 1: {{GROUPED_ISSUE_TITLE}} -**Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}`, `{{FILE_PATH}}:{{LINE_NUMBER}}` **Root Cause:** {{ROOT_CAUSE_DESCRIPTION}} +**Files Affected:** +- `{{FILE_PATH}}:{{LINE_NUMBER}}` - {{ISSUE_DESCRIPTION}} +- `{{FILE_PATH}}:{{LINE_NUMBER}}` - {{ISSUE_DESCRIPTION}} **Actions:** - [ ] {{SPECIFIC_ACTION_1}} @@ -50,13 +108,29 @@ INSTRUCTIONS FOR AGENTS: ``` ### Issue 2: {{GROUPED_ISSUE_TITLE}} -**Files:** `{{FILE_PATH}}:{{LINE_NUMBER}}` **Root Cause:** {{ROOT_CAUSE_DESCRIPTION}} +**Files Affected:** +- `{{FILE_PATH}}:{{LINE_NUMBER}}` - {{ISSUE_DESCRIPTION}} **Actions:** - [ ] {{SPECIFIC_ACTION_1}} - [ ] {{SPECIFIC_ACTION_2}} +## CodeRabbit Analysis Summary + +### Resolved Comments ✅ +- {{RESOLVED_COMMENT_1}} +- {{RESOLVED_COMMENT_2}} + +### Unresolved Comments 🔴 +- {{UNRESOLVED_COMMENT_1}} - {{STATUS}} +- {{UNRESOLVED_COMMENT_2}} - {{STATUS}} + +### Comment Categories +- **Critical:** {{COUNT}} comments (React Native compatibility, security, memory leaks) +- **Architecture:** {{COUNT}} comments (API design, type safety) +- **Code Quality:** {{COUNT}} comments (testing, imports, documentation) + ## Testing Checklist ### Before Merge @@ -101,4 +175,26 @@ INSTRUCTIONS FOR AGENTS: --- +## Agent Verification Checklist + +**BEFORE FINALIZING - VERIFY EACH ITEM:** +- [ ] ✅ Read ALL {{TOTAL_COMMENTS}} CodeRabbit comments thoroughly +- [ ] ✅ Checked each comment for "✅ Addressed" status +- [ ] ✅ Excluded all nitpick/style/documentation-only suggestions +- [ ] ✅ Only included MEDIUM+ severity issues (security, architecture, functionality) +- [ ] ✅ Verified unresolved count is accurate +- [ ] ✅ If 0 unresolved issues, used "All issues resolved" template +- [ ] ✅ Double-checked that each "unresolved" issue is actually unresolved + +**SEVERITY VERIFICATION:** +- [ ] ✅ CRITICAL: Security, memory leaks, platform compatibility ({{CRITICAL_COUNT}}) +- [ ] ✅ HIGH: API inconsistencies, type safety, architecture ({{HIGH_COUNT}}) +- [ ] ✅ MEDIUM: Test coverage, performance, minor architecture ({{MEDIUM_COUNT}}) +- [ ] ❌ LOW/NITPICK: Style, naming, docs (EXCLUDED - {{EXCLUDED_COUNT}}) + +--- + **Last Updated:** {{DATE}} +**CodeRabbit Comments Analyzed:** {{TOTAL_COMMENTS}} +**Unresolved Medium+ Issues:** {{UNRESOLVED_COUNT}} +**Excluded Low/Nitpick Issues:** {{EXCLUDED_COUNT}} diff --git a/packages/mobile-sdk-alpha/package.json b/packages/mobile-sdk-alpha/package.json index ade92d853..db28cecf4 100644 --- a/packages/mobile-sdk-alpha/package.json +++ b/packages/mobile-sdk-alpha/package.json @@ -1,6 +1,6 @@ { "name": "@selfxyz/mobile-sdk-alpha", - "version": "0.1.0", + "version": "2.6.4", "description": "Self SDK (alpha) for registering and proving. Adapters-first, RN-first with web shims.", "keywords": [ "self", @@ -14,7 +14,6 @@ "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", @@ -54,7 +53,15 @@ "@selfxyz/common": "workspace:*", "tslib": "^2.6.2" }, + "peerDependencies": { + "react": "^18.3.1", + "react-native": "^0.75.4", + "tamagui": "^1.126.0" + }, "devDependencies": { + "@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", @@ -63,7 +70,11 @@ "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-sort-exports": "^0.8.0", + "jsdom": "^24.0.0", "prettier": "^3.5.3", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-native": "0.75.4", "tsup": "^8.0.1", "typescript": "^5.9.2", "vitest": "^1.6.0" diff --git a/packages/mobile-sdk-alpha/src/browser.ts b/packages/mobile-sdk-alpha/src/browser.ts index 31b31266d..617efb01c 100644 --- a/packages/mobile-sdk-alpha/src/browser.ts +++ b/packages/mobile-sdk-alpha/src/browser.ts @@ -39,8 +39,15 @@ export type { QRProofOptions } from './qr'; export type { SdkErrorCategory } from './errors'; export { SCANNER_ERROR_CODES, notImplemented, sdkError } from './errors'; +export { SelfClientContext, SelfClientProvider, useSelfClient } from './context'; +// Browser-only high-level component (DOM-based) +export { SelfMobileSdk as SelfMobileSdkHighLevel } from './components/SelfMobileSdk'; + export { createSelfClient } from './client'; + export { defaultConfig } from './config/defaults'; + +/** @deprecated Use createSelfClient().extractMRZInfo or import from './mrz' */ export { extractMRZInfo, formatDateToYYMMDD, scanMRZ } from './mrz'; // Core functions diff --git a/packages/mobile-sdk-alpha/src/client.ts b/packages/mobile-sdk-alpha/src/client.ts index 61f4f43ea..9c3f9642b 100644 --- a/packages/mobile-sdk-alpha/src/client.ts +++ b/packages/mobile-sdk-alpha/src/client.ts @@ -1,6 +1,7 @@ import { defaultConfig } from './config/defaults'; import { mergeConfig } from './config/merge'; import { notImplemented } from './errors'; +import { extractMRZInfo as parseMRZInfo } from './processing/mrz'; import type { Adapters, Config, @@ -19,6 +20,11 @@ import type { ValidationResult, } from './types/public'; +/** + * Optional adapter implementations used when a consumer does not provide their + * own. These defaults are intentionally minimal no-ops suitable for tests and + * non-production environments. + */ const optionalDefaults: Partial = { storage: { get: async () => null, @@ -36,6 +42,13 @@ const optionalDefaults: Partial = { }, }; +/** + * Creates a fully configured {@link SelfClient} instance. + * + * The function validates that all required adapters are supplied and merges the + * provided configuration with sensible defaults. Missing optional adapters are + * filled with benign no-op implementations. + */ export function createSelfClient({ config, adapters }: { config: Config; adapters: Partial }): SelfClient { const cfg = mergeConfig(defaultConfig, config); const required: (keyof Adapters)[] = ['scanner', 'network', 'crypto']; @@ -77,6 +90,10 @@ export function createSelfClient({ config, adapters }: { config: Config; adapter return { registered: false, reason: 'SELF_REG_STATUS_STUB' }; } + async function registerDocument(_input: RegistrationInput): Promise { + return { registered: false, reason: 'SELF_REG_STATUS_STUB' }; + } + async function generateProof( _req: ProofRequest, opts: { @@ -101,7 +118,9 @@ export function createSelfClient({ config, adapters }: { config: Config; adapter scanDocument, validateDocument, checkRegistration, + registerDocument, generateProof, + extractMRZInfo: parseMRZInfo, on, emit, }; diff --git a/packages/mobile-sdk-alpha/src/components/SelfMobileSdk.tsx b/packages/mobile-sdk-alpha/src/components/SelfMobileSdk.tsx new file mode 100644 index 000000000..3e4b1e3c2 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/SelfMobileSdk.tsx @@ -0,0 +1,64 @@ +import type { ComponentType, ReactNode } from 'react'; + +import { SelfClientProvider } from '../context'; +import { useDocumentManager } from '../hooks/useDocumentManager'; +import type { Adapters, Config } from '../types/public'; +import type { ExternalAdapter, PassportCameraProps, ScreenProps } from '../types/ui'; +import { OnboardingFlow } from './flows/OnboardingFlow'; +import { QRCodeScreen } from './screens/QRCodeScreen'; + +interface SelfMobileSdkProps { + config: Config; + adapters?: Partial; + external: ExternalAdapter; + children?: ReactNode; + // Optional custom components + customScreens?: { + PassportCamera?: ComponentType; + NFCScanner?: ComponentType; + QRScanner?: ReactNode; + }; +} + +const SelfMobileSdkContent = ({ + external, + customScreens = {}, +}: { + external: ExternalAdapter; + customScreens?: SelfMobileSdkProps['customScreens']; +}) => { + const { documents, isLoading, hasRegisteredDocuments } = useDocumentManager(external); + + if (isLoading) { + return
Loading documents...
; + } + + // Check if user has any registered documents + const hasDocuments = Object.keys(documents).length > 0 && hasRegisteredDocuments(); + + if (!hasDocuments) { + return ( + + ); + } + + // Show disclosure flow + return ( + customScreens.QRScanner || ( + + ) + ); +}; + +export const SelfMobileSdk = ({ config, adapters = {}, external, children, customScreens }: SelfMobileSdkProps) => { + return ( + + {children || } + + ); +}; diff --git a/packages/mobile-sdk-alpha/src/components/flows/OnboardingFlow.tsx b/packages/mobile-sdk-alpha/src/components/flows/OnboardingFlow.tsx new file mode 100644 index 000000000..438f9089a --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/flows/OnboardingFlow.tsx @@ -0,0 +1,59 @@ +import type { ComponentType } from 'react'; +import { useCallback, useState } from 'react'; + +import { useSelfClient } from '../../context'; +import type { MRZInfo } from '../../types/public'; +import type { DocumentData, ExternalAdapter, PassportCameraProps, ScreenProps } from '../../types/ui'; +import { NFCScannerScreen } from '../screens/NFCScannerScreen'; +import { PassportCameraScreen } from '../screens/PassportCameraScreen'; + +interface OnboardingFlowProps { + external: ExternalAdapter; + setDocument: (doc: DocumentData, documentId: string) => Promise; + PassportCamera?: ComponentType; + NFCScanner?: ComponentType; +} + +export const OnboardingFlow = ({ external, setDocument, PassportCamera, NFCScanner }: OnboardingFlowProps) => { + const [mrzData, setMrzData] = useState(null); + const client = useSelfClient(); + + const handleMRZDetected = useCallback( + async (mrzData: MRZInfo) => { + try { + const status = await client.registerDocument({ + scan: { + mode: 'mrz', + passportNumber: mrzData.passportNumber, + dateOfBirth: mrzData.dateOfBirth, + dateOfExpiry: mrzData.dateOfExpiry, + issuingCountry: mrzData.issuingCountry, + }, + }); + + if (status.registered) { + setMrzData(mrzData); + } else { + external.onOnboardingFailure(new Error('Registration failed')); + } + } catch (error) { + external.onOnboardingFailure(error as Error); + } + }, + [client, external, setDocument], + ); + + if (!mrzData) { + if (PassportCamera) { + const PCam = PassportCamera as ComponentType; + return ; + } + return ; + } + + if (NFCScanner) { + const NFC = NFCScanner as ComponentType; + return ; + } + return ; +}; diff --git a/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx new file mode 100644 index 000000000..cce129dbc --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/screens/NFCScannerScreen.tsx @@ -0,0 +1,31 @@ +import { useCallback } from 'react'; +import { Button, Text, YStack } from 'tamagui'; + +import { useSelfClient } from '../../context'; +import type { ScreenProps } from '../../types/ui'; + +export const NFCScannerScreen = ({ onSuccess, onFailure }: ScreenProps) => { + const client = useSelfClient(); + + const onNFCScan = useCallback( + async (_nfcData: any) => { + try { + // scan the document + // register the document + onSuccess(); + } catch (error) { + onFailure(error as Error); + } + }, + [client, onSuccess, onFailure], + ); + + return ( + + + NFC Scanner + + + + ); +}; diff --git a/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx new file mode 100644 index 000000000..073323299 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/screens/PassportCameraScreen.tsx @@ -0,0 +1,37 @@ +import { Button, Text, YStack } from 'tamagui'; + +import type { PassportCameraProps } from '../../types/ui'; + +// Simple placeholder component - this would be replaced with actual camera UI +export const PassportCameraScreen = ({ onMRZDetected }: PassportCameraProps) => ( + + + Passport Camera + + + +); diff --git a/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx b/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx new file mode 100644 index 000000000..0979964c1 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/screens/QRCodeScreen.tsx @@ -0,0 +1,15 @@ +import { Button, Text, YStack } from 'tamagui'; + +import type { ScreenProps } from '../../types/ui'; + +export const QRCodeScreen = ({ onSuccess, onFailure }: ScreenProps) => ( + + + QR Code Scanner + + + + +); diff --git a/packages/mobile-sdk-alpha/src/context.tsx b/packages/mobile-sdk-alpha/src/context.tsx new file mode 100644 index 000000000..e0a2cabe3 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/context.tsx @@ -0,0 +1,54 @@ +import { createContext, type PropsWithChildren, useContext, useMemo } from 'react'; + +import { createSelfClient } from './client'; +import type { Adapters, Config, SelfClient } from './types/public'; + +/** + * React context holding a {@link SelfClient} instance. + * + * The context is intentionally initialised with `null` so that consumers + * outside of a {@link SelfClientProvider} can be detected and an informative + * error can be thrown. + */ +const SelfClientContext = createContext(null); + +/** + * Props for {@link SelfClientProvider}. + * + * @public + */ +export interface SelfClientProviderProps { + /** SDK configuration options. */ + config: Config; + /** + * Partial set of adapter implementations. Any missing optional adapters will + * be replaced with default no-op implementations. + */ + adapters?: Partial; +} + +export { SelfClientContext }; + +/** + * Provides a memoised {@link SelfClient} instance to all descendant components + * via {@link SelfClientContext}. + * + * Consumers should ensure that `config` and `adapters` are referentially stable + * (e.g. wrapped in `useMemo`) to avoid recreating the client on every render. + */ +export function SelfClientProvider({ config, adapters = {}, children }: PropsWithChildren) { + const client = useMemo(() => createSelfClient({ config, adapters }), [config, adapters]); + + return {children}; +} + +/** + * Retrieves the current {@link SelfClient} from context. + * + * @throws If used outside of a {@link SelfClientProvider}. + */ +export function useSelfClient(): SelfClient { + const ctx = useContext(SelfClientContext); + if (!ctx) throw new Error('useSelfClient must be used within a SelfClientProvider'); + return ctx; +} diff --git a/packages/mobile-sdk-alpha/src/entry/index.tsx b/packages/mobile-sdk-alpha/src/entry/index.tsx new file mode 100644 index 000000000..3836cd04b --- /dev/null +++ b/packages/mobile-sdk-alpha/src/entry/index.tsx @@ -0,0 +1,16 @@ +import type { ReactNode } from 'react'; + +import { SelfClientProvider } from '../context'; +import type { Adapters, Config } from '../types/public'; + +export interface SelfMobileSdkProps { + config: Config; + adapters?: Partial; + children?: ReactNode; +} + +export const SelfMobileSdk = ({ config, adapters = {}, children }: SelfMobileSdkProps) => ( + + {children} + +); diff --git a/packages/mobile-sdk-alpha/src/hooks/useDocumentManager.ts b/packages/mobile-sdk-alpha/src/hooks/useDocumentManager.ts new file mode 100644 index 000000000..d58e74be6 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/hooks/useDocumentManager.ts @@ -0,0 +1,34 @@ +import { useCallback, useEffect, useState } from 'react'; + +import type { DocumentData, ExternalAdapter } from '../types/ui'; + +export const useDocumentManager = (external: ExternalAdapter) => { + const [documents, setDocuments] = useState<{ + [documentId: string]: DocumentData; + }>({}); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + external + .getAllDocuments() + .then(documents => { + setDocuments(documents); + setIsLoading(false); + }) + .catch(error => { + console.error('Failed to load documents:', error); + setIsLoading(false); + }); + }, [external]); + + const hasRegisteredDocuments = useCallback(() => { + return Object.values(documents).some(doc => doc.metadata.isRegistered); + }, [documents]); + + return { + documents, + isLoading, + hasRegisteredDocuments, + setDocuments, + }; +}; diff --git a/packages/mobile-sdk-alpha/src/index.ts b/packages/mobile-sdk-alpha/src/index.ts index d1b51fb1c..a662109dd 100644 --- a/packages/mobile-sdk-alpha/src/index.ts +++ b/packages/mobile-sdk-alpha/src/index.ts @@ -33,15 +33,18 @@ export type { // MRZ module export type { DG1, DG2, NFCScanOptions, ParsedNFCResponse } from './nfc'; +export type { DocumentData, DocumentMetadata, ExternalAdapter, PassportCameraProps, ScreenProps } from './types/ui'; + export type { MRZScanOptions } from './mrz'; // QR module export type { PassportValidationCallbacks } from './validation/document'; export type { QRProofOptions } from './qr'; - // Error handling export type { SdkErrorCategory } from './errors'; + +// UI Types export { InitError, LivenessError, @@ -53,11 +56,30 @@ export { sdkError, } from './errors'; +export { NFCScannerScreen } from './components/screens/NFCScannerScreen'; + +// Flow Components +export { OnboardingFlow } from './components/flows/OnboardingFlow'; + +// Screen Components +export { PassportCameraScreen } from './components/screens/PassportCameraScreen'; + +export { QRCodeScreen } from './components/screens/QRCodeScreen'; + +// Context and Client +export { SelfClientContext, SelfClientProvider, useSelfClient } from './context'; + +// Components +export { SelfMobileSdk } from './entry'; + export { createSelfClient } from './client'; export { defaultConfig } from './config/defaults'; -export { extractMRZInfo, formatDateToYYMMDD, scanMRZ } from './mrz'; +/** @deprecated Use createSelfClient().extractMRZInfo or import from './mrz' */ +export { extractMRZInfo } from './mrz'; + +export { formatDateToYYMMDD, scanMRZ } from './mrz'; // Core functions export { isPassportDataValid } from './validation/document'; @@ -68,5 +90,8 @@ export { mergeConfig } from './config/merge'; export { parseNFCResponse, scanNFC } from './nfc'; export { scanQRProof } from './qr'; + +// Hooks +export { useDocumentManager } from './hooks/useDocumentManager'; // Error handling export { webScannerShim } from './adapters/web/shims'; diff --git a/packages/mobile-sdk-alpha/src/types/public.ts b/packages/mobile-sdk-alpha/src/types/public.ts index cbf3fefa3..dc13ffbe8 100644 --- a/packages/mobile-sdk-alpha/src/types/public.ts +++ b/packages/mobile-sdk-alpha/src/types/public.ts @@ -120,6 +120,7 @@ export interface SelfClient { scanDocument(opts: ScanOpts & { signal?: AbortSignal }): Promise; validateDocument(input: ValidationInput): Promise; checkRegistration(input: RegistrationInput): Promise; + registerDocument(input: RegistrationInput): Promise; generateProof( req: ProofRequest, opts?: { @@ -128,6 +129,7 @@ export interface SelfClient { timeoutMs?: number; }, ): Promise; + extractMRZInfo(mrz: string): MRZInfo; on(event: E, cb: (payload: SDKEventMap[E]) => void): Unsubscribe; emit(event: E, payload: SDKEventMap[E]): void; } diff --git a/packages/mobile-sdk-alpha/src/types/ui.ts b/packages/mobile-sdk-alpha/src/types/ui.ts new file mode 100644 index 000000000..785e83881 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/types/ui.ts @@ -0,0 +1,46 @@ +import type { DocumentCategory, PassportData } from '@selfxyz/common'; + +import type { MRZInfo } from './public'; + +// Document-related types +/** + * Document metadata - must NOT contain plaintext MRZ/PII + * All sensitive payloads belong only in DocumentData.data (typed as PassportData) + * or in encrypted storage referenced by the opaque token + */ +export interface DocumentMetadata { + id: string; + documentType: string; + documentCategory: DocumentCategory; + encryptedBlobRef?: string; // opaque pointer; no plaintext PII + mock: boolean; + isRegistered?: boolean; +} + +export interface DocumentData { + data: PassportData; + metadata: DocumentMetadata; +} + +// External adapter interface +export interface ExternalAdapter { + getSecret: () => Promise; + getAllDocuments: () => Promise<{ + [documentId: string]: DocumentData; + }>; + setDocument: (doc: DocumentData, documentId: string) => Promise; + onOnboardingSuccess: () => void; + onOnboardingFailure: (error: Error) => void; + onDisclosureSuccess: () => void; + onDisclosureFailure: (error: Error) => void; +} + +// Screen component props +export interface ScreenProps { + onSuccess: () => void; + onFailure: (error: Error) => void; +} + +export interface PassportCameraProps { + onMRZDetected: (mrzData: MRZInfo) => void; +} diff --git a/packages/mobile-sdk-alpha/tests/SelfMobileSdk.test.tsx b/packages/mobile-sdk-alpha/tests/SelfMobileSdk.test.tsx new file mode 100644 index 000000000..57feaacbb --- /dev/null +++ b/packages/mobile-sdk-alpha/tests/SelfMobileSdk.test.tsx @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest'; + +describe('High-Level SelfMobileSdk Component', () => { + it('can be imported successfully', async () => { + const { SelfMobileSdk } = await import('../src'); + expect(SelfMobileSdk).toBeDefined(); + expect(typeof SelfMobileSdk).toBe('function'); + }); + + it('accepts the expected props interface', async () => { + const { SelfMobileSdk } = await import('../src'); + + // Test that the component accepts the expected props structure + const mockExternal = { + getSecret: async () => 'test-secret', + getAllDocuments: async () => ({}), + setDocument: async () => true, + onOnboardingSuccess: () => {}, + onOnboardingFailure: () => {}, + onDisclosureSuccess: () => {}, + onDisclosureFailure: () => {}, + }; + + expect(SelfMobileSdk).toBeDefined(); + // The component should accept these props without throwing + expect(() => { + // This is just a type check - we're not actually rendering + const _props = { config: {}, external: mockExternal }; + }).not.toThrow(); + }); +}); diff --git a/packages/mobile-sdk-alpha/tests/client.test.ts b/packages/mobile-sdk-alpha/tests/client.test.ts index 822fc7795..fd538c71c 100644 --- a/packages/mobile-sdk-alpha/tests/client.test.ts +++ b/packages/mobile-sdk-alpha/tests/client.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import type { CryptoAdapter, NetworkAdapter, ScannerAdapter } from '../src/adapters/index'; +import type { CryptoAdapter, NetworkAdapter, ScannerAdapter } from '../src'; import { createSelfClient } from '../src/index'; describe('createSelfClient', () => { @@ -77,6 +77,22 @@ describe('createSelfClient', () => { eventSet?.forEach(fn => fn({ step: 'two' })); expect(cb).toHaveBeenCalledTimes(1); }); + + it('parses MRZ via client', () => { + const client = createSelfClient({ config: {}, adapters: { scanner, network, crypto } }); + const sample = `P { + const client = createSelfClient({ config: {}, adapters: { scanner, network, crypto } }); + await expect(client.registerDocument({} as any)).resolves.toEqual({ + registered: false, + reason: 'SELF_REG_STATUS_STUB', + }); + }); }); const scanner: ScannerAdapter = { diff --git a/packages/mobile-sdk-alpha/tests/client.test.tsx b/packages/mobile-sdk-alpha/tests/client.test.tsx new file mode 100644 index 000000000..f0ec62a1b --- /dev/null +++ b/packages/mobile-sdk-alpha/tests/client.test.tsx @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest'; + +import { createSelfClient } from '../src/index'; +import { MrzParseError } from '../src/processing/mrz'; +import { badCheckDigitsMRZ, expectedMRZResult, invalidMRZ, mockAdapters, sampleMRZ } from './utils/testHelpers'; + +describe('createSelfClient API', () => { + it('creates a client instance with expected methods', () => { + const client = createSelfClient({ config: {}, adapters: mockAdapters }); + + expect(typeof client.extractMRZInfo).toBe('function'); + expect(typeof client.registerDocument).toBe('function'); + expect(typeof client.validateDocument).toBe('function'); + }); + + it('parses MRZ data correctly', () => { + const client = createSelfClient({ config: {}, adapters: mockAdapters }); + const info = client.extractMRZInfo(sampleMRZ); + + expect(info.passportNumber).toBe(expectedMRZResult.passportNumber); + expect(info.validation.overall).toBe(expectedMRZResult.validation.overall); + }); + + it('accepts different adapter configurations', () => { + const clientWithAllAdapters = createSelfClient({ + config: {}, + adapters: mockAdapters, + }); + + expect(clientWithAllAdapters).toBeDefined(); + expect(typeof clientWithAllAdapters.extractMRZInfo).toBe('function'); + }); + + it('throws MrzParseError for malformed MRZ input', () => { + const client = createSelfClient({ config: {}, adapters: mockAdapters }); + expect(() => client.extractMRZInfo(invalidMRZ)).toThrowError(MrzParseError); + }); + + it('flags invalid check digits', () => { + const client = createSelfClient({ config: {}, adapters: mockAdapters }); + const info = client.extractMRZInfo(badCheckDigitsMRZ); + expect(info.validation.overall).toBe(false); + }); +}); diff --git a/packages/mobile-sdk-alpha/tests/entry.test.tsx b/packages/mobile-sdk-alpha/tests/entry.test.tsx new file mode 100644 index 000000000..0bcd0aaac --- /dev/null +++ b/packages/mobile-sdk-alpha/tests/entry.test.tsx @@ -0,0 +1,37 @@ +/* @vitest-environment jsdom */ +import React from 'react'; +import { describe, expect, it } from 'vitest'; + +import { SelfMobileSdk, useSelfClient } from '../src/index'; +import { expectedMRZResult, mockAdapters, sampleMRZ } from './utils/testHelpers'; + +import { render, screen } from '@testing-library/react'; + +function Consumer() { + const client = useSelfClient(); + const info = client.extractMRZInfo(sampleMRZ); + return {info.passportNumber}; +} + +describe('SelfMobileSdk Entry Component', () => { + it('provides client to children and enables MRZ parsing', () => { + render( + + + , + ); + + expect(screen.getByText(expectedMRZResult.passportNumber)).toBeTruthy(); + }); + + it('renders children correctly', () => { + const testMessage = 'Test Child Component'; + render( + +
{testMessage}
+
, + ); + + expect(screen.getByText(testMessage)).toBeTruthy(); + }); +}); diff --git a/packages/mobile-sdk-alpha/tests/processing/mrz.test.ts b/packages/mobile-sdk-alpha/tests/processing/mrz.test.ts index 4d7ec20ec..4fb3619eb 100644 --- a/packages/mobile-sdk-alpha/tests/processing/mrz.test.ts +++ b/packages/mobile-sdk-alpha/tests/processing/mrz.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { extractMRZInfo, formatDateToYYMMDD, MrzParseError } from '../../src'; +import { formatDateToYYMMDD, MrzParseError } from '../../src'; +import { extractMRZInfo } from '../../src/mrz'; const sample = `P { + it('provides client through context with MRZ parsing capability', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ); + + const { result } = renderHook(() => useSelfClient(), { wrapper }); + const info = result.current.extractMRZInfo(sampleMRZ); + + expect(info.passportNumber).toBe(expectedMRZResult.passportNumber); + expect(info.validation.overall).toBe(expectedMRZResult.validation.overall); + }); + + it('throws error when used outside provider', () => { + expect(() => { + renderHook(() => useSelfClient()); + }).toThrow('useSelfClient must be used within a SelfClientProvider'); + }); + + it('memoises the client instance across re-renders', () => { + const spy = vi.spyOn(clientModule, 'createSelfClient'); + const config = {}; + const adapters = mockAdapters; + const wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ); + + const { result, rerender } = renderHook(() => useSelfClient(), { wrapper }); + const first = result.current; + rerender(); + expect(result.current).toBe(first); + expect(spy).toHaveBeenCalledTimes(1); + spy.mockRestore(); + }); +}); diff --git a/packages/mobile-sdk-alpha/tests/setup.ts b/packages/mobile-sdk-alpha/tests/setup.ts index a1d0e5425..af81ba6ce 100644 --- a/packages/mobile-sdk-alpha/tests/setup.ts +++ b/packages/mobile-sdk-alpha/tests/setup.ts @@ -26,3 +26,20 @@ if (typeof global !== 'undefined') { console.log = originalConsole.log; }; } + +// Mock window.matchMedia for Tamagui components +if (typeof window !== 'undefined') { + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), // deprecated + removeListener: vi.fn(), // deprecated + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), + }); +} diff --git a/packages/mobile-sdk-alpha/tests/utils/testHelpers.ts b/packages/mobile-sdk-alpha/tests/utils/testHelpers.ts new file mode 100644 index 000000000..7aab88013 --- /dev/null +++ b/packages/mobile-sdk-alpha/tests/utils/testHelpers.ts @@ -0,0 +1,53 @@ +/* eslint-disable sort-exports/sort-exports */ +import type { CryptoAdapter, NetworkAdapter, ScannerAdapter } from '../../src'; + +// Shared test data +export const sampleMRZ = `P ({ mode: 'mrz', passportNumber: '', dateOfBirth: '', dateOfExpiry: '' }), +}; + +export const mockNetwork: NetworkAdapter = { + // Return a minimal stub to avoid relying on global Response in JSDOM/Node + http: { + fetch: async () => + ({ + ok: true, + status: 200, + text: async () => '', + json: async () => ({}), + arrayBuffer: async () => new ArrayBuffer(0), + }) as any, + }, + ws: { + connect: () => ({ + send: () => {}, + close: () => {}, + onMessage: () => {}, + onError: () => {}, + onClose: () => {}, + }), + }, +}; + +export const mockCrypto: CryptoAdapter = { + hash: async () => new Uint8Array(), + sign: async () => new Uint8Array(), +}; + +export const mockAdapters = { + scanner: mockScanner, + network: mockNetwork, + crypto: mockCrypto, +}; + +// Shared test expectations +export const expectedMRZResult = { + passportNumber: 'L898902C3', + validation: { overall: true }, +}; diff --git a/packages/mobile-sdk-alpha/tsconfig.json b/packages/mobile-sdk-alpha/tsconfig.json index be080649c..d76b5fac3 100644 --- a/packages/mobile-sdk-alpha/tsconfig.json +++ b/packages/mobile-sdk-alpha/tsconfig.json @@ -16,7 +16,7 @@ "noEmit": true, "resolveJsonModule": true, "verbatimModuleSyntax": false, - "types": ["vitest/globals"] + "types": ["vitest/globals", "react"] }, "include": ["src", "tests"] } diff --git a/packages/mobile-sdk-alpha/vitest.config.ts b/packages/mobile-sdk-alpha/vitest.config.ts index f5d24052e..67f041196 100644 --- a/packages/mobile-sdk-alpha/vitest.config.ts +++ b/packages/mobile-sdk-alpha/vitest.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { globals: true, - environment: 'node', + environment: 'jsdom', setupFiles: ['./tests/setup.ts'], }, }); diff --git a/yarn.lock b/yarn.lock index d1384bcd7..a7cbc21f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31,6 +31,19 @@ __metadata: languageName: node linkType: hard +"@asamuzakjp/css-color@npm:^3.2.0": + version: 3.2.0 + resolution: "@asamuzakjp/css-color@npm:3.2.0" + dependencies: + "@csstools/css-calc": "npm:^2.1.3" + "@csstools/css-color-parser": "npm:^3.0.9" + "@csstools/css-parser-algorithms": "npm:^3.0.4" + "@csstools/css-tokenizer": "npm:^3.0.3" + lru-cache: "npm:^10.4.3" + checksum: 10c0/a4bf1c831751b1fae46b437e37e8a38c0b5bd58d23230157ae210bd1e905fe509b89b7c243e63d1522d852668a6292ed730a160e21342772b4e5b7b8ea14c092 + languageName: node + linkType: hard + "@ashpect/smt@https://github.com/ashpect/smt#main": version: 1.0.0 resolution: "@ashpect/smt@https://github.com/ashpect/smt.git#commit=4f73fd24adb06a7f8efd6fd2d3ed58e9e2f2691a" @@ -38,7 +51,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.24.7, @babel/code-frame@npm:^7.27.1": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.24.7, @babel/code-frame@npm:^7.27.1": version: 7.27.1 resolution: "@babel/code-frame@npm:7.27.1" dependencies: @@ -1108,6 +1121,52 @@ __metadata: languageName: node linkType: hard +"@csstools/color-helpers@npm:^5.0.2": + version: 5.0.2 + resolution: "@csstools/color-helpers@npm:5.0.2" + checksum: 10c0/bebaddb28b9eb58b0449edd5d0c0318fa88f3cb079602ee27e88c9118070d666dcc4e09a5aa936aba2fde6ba419922ade07b7b506af97dd7051abd08dfb2959b + languageName: node + linkType: hard + +"@csstools/css-calc@npm:^2.1.3, @csstools/css-calc@npm:^2.1.4": + version: 2.1.4 + resolution: "@csstools/css-calc@npm:2.1.4" + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10c0/42ce5793e55ec4d772083808a11e9fb2dfe36db3ec168713069a276b4c3882205b3507c4680224c28a5d35fe0bc2d308c77f8f2c39c7c09aad8747708eb8ddd8 + languageName: node + linkType: hard + +"@csstools/css-color-parser@npm:^3.0.9": + version: 3.0.10 + resolution: "@csstools/css-color-parser@npm:3.0.10" + dependencies: + "@csstools/color-helpers": "npm:^5.0.2" + "@csstools/css-calc": "npm:^2.1.4" + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10c0/8f8a2395b117c2f09366b5c9bf49bc740c92a65b6330fe3cc1e76abafd0d1000e42a657d7b0a3814846a66f1d69896142f7e36d7a4aca77de977e5cc5f944747 + languageName: node + linkType: hard + +"@csstools/css-parser-algorithms@npm:^3.0.4": + version: 3.0.5 + resolution: "@csstools/css-parser-algorithms@npm:3.0.5" + peerDependencies: + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10c0/d9a1c888bd43849ae3437ca39251d5c95d2c8fd6b5ccdb7c45491dfd2c1cbdc3075645e80901d120e4d2c1993db9a5b2d83793b779dbbabcfb132adb142eb7f7 + languageName: node + linkType: hard + +"@csstools/css-tokenizer@npm:^3.0.3": + version: 3.0.4 + resolution: "@csstools/css-tokenizer@npm:3.0.4" + checksum: 10c0/3b589f8e9942075a642213b389bab75a2d50d05d203727fcdac6827648a5572674caff07907eff3f9a2389d86a4ee47308fafe4f8588f4a77b7167c588d2559f + languageName: node + linkType: hard + "@egjs/hammerjs@npm:^2.0.17": version: 2.0.17 resolution: "@egjs/hammerjs@npm:2.0.17" @@ -5192,6 +5251,9 @@ __metadata: resolution: "@selfxyz/mobile-sdk-alpha@workspace:packages/mobile-sdk-alpha" dependencies: "@selfxyz/common": "workspace:*" + "@testing-library/react": "npm:^14.1.2" + "@types/react": "npm:^18.3.4" + "@types/react-dom": "npm:^18.3.0" "@typescript-eslint/eslint-plugin": "npm:^8.0.0" "@typescript-eslint/parser": "npm:^8.0.0" eslint: "npm:^8.57.0" @@ -5200,11 +5262,19 @@ __metadata: eslint-plugin-prettier: "npm:^5.5.4" eslint-plugin-simple-import-sort: "npm:^12.1.1" eslint-plugin-sort-exports: "npm:^0.8.0" + jsdom: "npm:^24.0.0" prettier: "npm:^3.5.3" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" + react-native: "npm:0.75.4" tslib: "npm:^2.6.2" tsup: "npm:^8.0.1" typescript: "npm:^5.9.2" vitest: "npm:^1.6.0" + peerDependencies: + react: ^18.3.1 + react-native: ^0.75.4 + tamagui: ^1.126.0 languageName: unknown linkType: soft @@ -9313,6 +9383,22 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^9.0.0": + version: 9.3.4 + resolution: "@testing-library/dom@npm:9.3.4" + dependencies: + "@babel/code-frame": "npm:^7.10.4" + "@babel/runtime": "npm:^7.12.5" + "@types/aria-query": "npm:^5.0.1" + aria-query: "npm:5.1.3" + chalk: "npm:^4.1.0" + dom-accessibility-api: "npm:^0.5.9" + lz-string: "npm:^1.5.0" + pretty-format: "npm:^27.0.2" + checksum: 10c0/147da340e8199d7f98f3a4ad8aa22ed55b914b83957efa5eb22bfea021a979ebe5a5182afa9c1e5b7a5f99a7f6744a5a4d9325ae46ec3b33b5a15aed8750d794 + languageName: node + linkType: hard + "@testing-library/react-hooks@npm:^8.0.1": version: 8.0.1 resolution: "@testing-library/react-hooks@npm:8.0.1" @@ -9355,6 +9441,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^14.1.2": + version: 14.3.1 + resolution: "@testing-library/react@npm:14.3.1" + dependencies: + "@babel/runtime": "npm:^7.12.5" + "@testing-library/dom": "npm:^9.0.0" + "@types/react-dom": "npm:^18.0.0" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10c0/1ccf4eb1510500cc20a805cb0244c9098dca28a8745173a8f71ea1274d63774f0b7898a35c878b43c797b89c13621548909ff37843b835c1a27ee1efbbdd098c + languageName: node + linkType: hard + "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -9474,6 +9574,13 @@ __metadata: languageName: node linkType: hard +"@types/aria-query@npm:^5.0.1": + version: 5.0.4 + resolution: "@types/aria-query@npm:5.0.4" + checksum: 10c0/dc667bc6a3acc7bba2bccf8c23d56cb1f2f4defaa704cfef595437107efaa972d3b3db9ec1d66bc2711bfc35086821edd32c302bffab36f2e79b97f312069f08 + languageName: node + linkType: hard + "@types/babel__core@npm:^7.1.14": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" @@ -9867,7 +9974,7 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.3.0": +"@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.3.0": version: 18.3.7 resolution: "@types/react-dom@npm:18.3.7" peerDependencies: @@ -11286,6 +11393,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:5.1.3": + version: 5.1.3 + resolution: "aria-query@npm:5.1.3" + dependencies: + deep-equal: "npm:^2.0.5" + checksum: 10c0/edcbc8044c4663d6f88f785e983e6784f98cb62b4ba1e9dd8d61b725d0203e4cfca38d676aee984c31f354103461102a3d583aa4fbe4fd0a89b679744f4e5faf + languageName: node + linkType: hard + "array-back@npm:^3.0.1, array-back@npm:^3.1.0": version: 3.1.0 resolution: "array-back@npm:3.1.0" @@ -11300,7 +11416,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" dependencies: @@ -12288,7 +12404,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": version: 1.0.8 resolution: "call-bind@npm:1.0.8" dependencies: @@ -13434,6 +13550,16 @@ __metadata: languageName: node linkType: hard +"cssstyle@npm:^4.0.1": + version: 4.6.0 + resolution: "cssstyle@npm:4.6.0" + dependencies: + "@asamuzakjp/css-color": "npm:^3.2.0" + rrweb-cssom: "npm:^0.8.0" + checksum: 10c0/71add1b0ffafa1bedbef6855db6189b9523d3320e015a0bf3fbd504760efb9a81e1f1a225228d5fa892ee58e56d06994ca372e7f4e461cda7c4c9985fe075f65 + languageName: node + linkType: hard + "csstype@npm:^3.0.2": version: 3.1.3 resolution: "csstype@npm:3.1.3" @@ -13448,6 +13574,16 @@ __metadata: languageName: node linkType: hard +"data-urls@npm:^5.0.0": + version: 5.0.0 + resolution: "data-urls@npm:5.0.0" + dependencies: + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + checksum: 10c0/1b894d7d41c861f3a4ed2ae9b1c3f0909d4575ada02e36d3d3bc584bdd84278e20709070c79c3b3bff7ac98598cb191eb3e86a89a79ea4ee1ef360e1694f92ad + languageName: node + linkType: hard + "data-view-buffer@npm:^1.0.2": version: 1.0.2 resolution: "data-view-buffer@npm:1.0.2" @@ -13570,6 +13706,13 @@ __metadata: languageName: node linkType: hard +"decimal.js@npm:^10.4.3": + version: 10.6.0 + resolution: "decimal.js@npm:10.6.0" + checksum: 10c0/07d69fbcc54167a340d2d97de95f546f9ff1f69d2b45a02fd7a5292412df3cd9eb7e23065e532a318f5474a2e1bccf8392fdf0443ef467f97f3bf8cb0477e5aa + languageName: node + linkType: hard + "decode-uri-component@npm:^0.2.2": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" @@ -13614,6 +13757,32 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^2.0.5": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.0" + call-bind: "npm:^1.0.5" + es-get-iterator: "npm:^1.1.3" + get-intrinsic: "npm:^1.2.2" + is-arguments: "npm:^1.1.1" + is-array-buffer: "npm:^3.0.2" + is-date-object: "npm:^1.0.5" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.2" + isarray: "npm:^2.0.5" + object-is: "npm:^1.1.5" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.4" + regexp.prototype.flags: "npm:^1.5.1" + side-channel: "npm:^1.0.4" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.1" + which-typed-array: "npm:^1.1.13" + checksum: 10c0/a48244f90fa989f63ff5ef0cc6de1e4916b48ea0220a9c89a378561960814794a5800c600254482a2c8fd2e49d6c2e196131dc983976adb024c94a42dfe4949f + languageName: node + linkType: hard + "deep-extend@npm:~0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -13818,6 +13987,13 @@ __metadata: languageName: node linkType: hard +"dom-accessibility-api@npm:^0.5.9": + version: 0.5.16 + resolution: "dom-accessibility-api@npm:0.5.16" + checksum: 10c0/b2c2eda4fae568977cdac27a9f0c001edf4f95a6a6191dfa611e3721db2478d1badc01db5bb4fa8a848aeee13e442a6c2a4386d65ec65a1436f24715a2f8d053 + languageName: node + linkType: hard + "dom-serializer@npm:^2.0.0": version: 2.0.0 resolution: "dom-serializer@npm:2.0.0" @@ -14086,6 +14262,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^6.0.0": + version: 6.0.1 + resolution: "entities@npm:6.0.1" + checksum: 10c0/ed836ddac5acb34341094eb495185d527bd70e8632b6c0d59548cbfa23defdbae70b96f9a405c82904efa421230b5b3fd2283752447d737beffd3f3e6ee74414 + languageName: node + linkType: hard + "env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -14220,6 +14403,23 @@ __metadata: languageName: node linkType: hard +"es-get-iterator@npm:^1.1.3": + version: 1.1.3 + resolution: "es-get-iterator@npm:1.1.3" + dependencies: + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.1.3" + has-symbols: "npm:^1.0.3" + is-arguments: "npm:^1.1.1" + is-map: "npm:^2.0.2" + is-set: "npm:^2.0.2" + is-string: "npm:^1.0.7" + isarray: "npm:^2.0.5" + stop-iteration-iterator: "npm:^1.0.0" + checksum: 10c0/ebd11effa79851ea75d7f079405f9d0dc185559fd65d986c6afea59a0ff2d46c2ed8675f19f03dce7429d7f6c14ff9aede8d121fbab78d75cfda6a263030bac0 + languageName: node + linkType: hard + "es-iterator-helpers@npm:^1.2.1": version: 1.2.1 resolution: "es-iterator-helpers@npm:1.2.1" @@ -16200,7 +16400,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": version: 1.3.0 resolution: "get-intrinsic@npm:1.3.0" dependencies: @@ -16881,6 +17081,15 @@ __metadata: languageName: node linkType: hard +"html-encoding-sniffer@npm:^4.0.0": + version: 4.0.0 + resolution: "html-encoding-sniffer@npm:4.0.0" + dependencies: + whatwg-encoding: "npm:^3.1.1" + checksum: 10c0/523398055dc61ac9b34718a719cb4aa691e4166f29187e211e1607de63dc25ac7af52ca7c9aead0c4b3c0415ffecb17326396e1202e2e86ff4bca4c0ee4c6140 + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -16920,7 +17129,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1": +"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1, http-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -17221,7 +17430,7 @@ __metadata: languageName: node linkType: hard -"is-arguments@npm:^1.0.4": +"is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1": version: 1.2.0 resolution: "is-arguments@npm:1.2.0" dependencies: @@ -17231,7 +17440,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": version: 3.0.5 resolution: "is-array-buffer@npm:3.0.5" dependencies: @@ -17438,7 +17647,7 @@ __metadata: languageName: node linkType: hard -"is-map@npm:^2.0.3": +"is-map@npm:^2.0.2, is-map@npm:^2.0.3": version: 2.0.3 resolution: "is-map@npm:2.0.3" checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc @@ -17492,6 +17701,13 @@ __metadata: languageName: node linkType: hard +"is-potential-custom-element-name@npm:^1.0.1": + version: 1.0.1 + resolution: "is-potential-custom-element-name@npm:1.0.1" + checksum: 10c0/b73e2f22bc863b0939941d369486d308b43d7aef1f9439705e3582bfccaa4516406865e32c968a35f97a99396dac84e2624e67b0a16b0a15086a785e16ce7db9 + languageName: node + linkType: hard + "is-primitive@npm:^3.0.1": version: 3.0.1 resolution: "is-primitive@npm:3.0.1" @@ -17499,7 +17715,7 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.2.1": +"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1": version: 1.2.1 resolution: "is-regex@npm:1.2.1" dependencies: @@ -17511,14 +17727,14 @@ __metadata: languageName: node linkType: hard -"is-set@npm:^2.0.3": +"is-set@npm:^2.0.2, is-set@npm:^2.0.3": version: 2.0.3 resolution: "is-set@npm:2.0.3" checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.4": +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4": version: 1.0.4 resolution: "is-shared-array-buffer@npm:1.0.4" dependencies: @@ -17541,7 +17757,7 @@ __metadata: languageName: node linkType: hard -"is-string@npm:^1.1.1": +"is-string@npm:^1.0.7, is-string@npm:^1.1.1": version: 1.1.1 resolution: "is-string@npm:1.1.1" dependencies: @@ -18458,6 +18674,40 @@ __metadata: languageName: node linkType: hard +"jsdom@npm:^24.0.0": + version: 24.1.3 + resolution: "jsdom@npm:24.1.3" + dependencies: + cssstyle: "npm:^4.0.1" + data-urls: "npm:^5.0.0" + decimal.js: "npm:^10.4.3" + form-data: "npm:^4.0.0" + html-encoding-sniffer: "npm:^4.0.0" + http-proxy-agent: "npm:^7.0.2" + https-proxy-agent: "npm:^7.0.5" + is-potential-custom-element-name: "npm:^1.0.1" + nwsapi: "npm:^2.2.12" + parse5: "npm:^7.1.2" + rrweb-cssom: "npm:^0.7.1" + saxes: "npm:^6.0.0" + symbol-tree: "npm:^3.2.4" + tough-cookie: "npm:^4.1.4" + w3c-xmlserializer: "npm:^5.0.0" + webidl-conversions: "npm:^7.0.0" + whatwg-encoding: "npm:^3.1.1" + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + ws: "npm:^8.18.0" + xml-name-validator: "npm:^5.0.0" + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + checksum: 10c0/e48b342afacd7418a23dac204a62deea729c50f4d072a7c04c09fd32355fdb4335f8779fa79fd0277a2dbeb2d356250a950955719d00047324b251233b11277f + languageName: node + linkType: hard + "jsesc@npm:^3.0.2": version: 3.1.0 resolution: "jsesc@npm:3.1.0" @@ -19087,7 +19337,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.4.3": version: 10.4.3 resolution: "lru-cache@npm:10.4.3" checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb @@ -19126,6 +19376,15 @@ __metadata: languageName: node linkType: hard +"lz-string@npm:^1.5.0": + version: 1.5.0 + resolution: "lz-string@npm:1.5.0" + bin: + lz-string: bin/bin.js + checksum: 10c0/36128e4de34791838abe979b19927c26e67201ca5acf00880377af7d765b38d1c60847e01c5ec61b1a260c48029084ab3893a3925fd6e48a04011364b089991b + languageName: node + linkType: hard + "magic-string@npm:^0.30.17, magic-string@npm:^0.30.5": version: 0.30.17 resolution: "magic-string@npm:0.30.17" @@ -20566,6 +20825,13 @@ __metadata: languageName: node linkType: hard +"nwsapi@npm:^2.2.12": + version: 2.2.21 + resolution: "nwsapi@npm:2.2.21" + checksum: 10c0/dd330cabb886fd417624bd3af368d86c3d507c002c05fb2f7981874204298deec9e8bd5103d8a0c4a0e0dc280276dc4a59a059e1045eeb7a628f79e6cefba6a3 + languageName: node + linkType: hard + "ob1@npm:0.80.12": version: 0.80.12 resolution: "ob1@npm:0.80.12" @@ -20598,6 +20864,16 @@ __metadata: languageName: node linkType: hard +"object-is@npm:^1.1.5": + version: 1.1.6 + resolution: "object-is@npm:1.1.6" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + checksum: 10c0/506af444c4dce7f8e31f34fc549e2fb8152d6b9c4a30c6e62852badd7f520b579c679af433e7a072f9d78eb7808d230dc12e1cf58da9154dfbf8813099ea0fe0 + languageName: node + linkType: hard + "object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" @@ -21127,6 +21403,15 @@ __metadata: languageName: node linkType: hard +"parse5@npm:^7.1.2": + version: 7.3.0 + resolution: "parse5@npm:7.3.0" + dependencies: + entities: "npm:^6.0.0" + checksum: 10c0/7fd2e4e247e85241d6f2a464d0085eed599a26d7b0a5233790c49f53473232eb85350e8133344d9b3fd58b89339e7ad7270fe1f89d28abe50674ec97b87f80b5 + languageName: node + linkType: hard + "parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -21557,6 +21842,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^27.0.2": + version: 27.5.1 + resolution: "pretty-format@npm:27.5.1" + dependencies: + ansi-regex: "npm:^5.0.1" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^17.0.1" + checksum: 10c0/0cbda1031aa30c659e10921fa94e0dd3f903ecbbbe7184a729ad66f2b6e7f17891e8c7d7654c458fa4ccb1a411ffb695b4f17bbcd3fe075fabe181027c4040ed + languageName: node + linkType: hard + "pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" @@ -21684,7 +21980,7 @@ __metadata: languageName: node linkType: hard -"psl@npm:^1.9.0": +"psl@npm:^1.1.33, psl@npm:^1.9.0": version: 1.15.0 resolution: "psl@npm:1.15.0" dependencies: @@ -21703,7 +21999,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0, punycode@npm:^2.3.1": +"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 @@ -21785,6 +22081,13 @@ __metadata: languageName: node linkType: hard +"querystringify@npm:^2.1.1": + version: 2.2.0 + resolution: "querystringify@npm:2.2.0" + checksum: 10c0/3258bc3dbdf322ff2663619afe5947c7926a6ef5fb78ad7d384602974c467fadfc8272af44f5eb8cddd0d011aae8fabf3a929a8eee4b86edcc0a21e6bd10f9aa + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -22633,7 +22936,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": +"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": version: 1.5.4 resolution: "regexp.prototype.flags@npm:1.5.4" dependencies: @@ -22725,6 +23028,13 @@ __metadata: languageName: node linkType: hard +"requires-port@npm:^1.0.0": + version: 1.0.0 + resolution: "requires-port@npm:1.0.0" + checksum: 10c0/b2bfdd09db16c082c4326e573a82c0771daaf7b53b9ce8ad60ea46aa6e30aaf475fe9b164800b89f93b748d2c234d8abff945d2551ba47bf5698e04cd7713267 + languageName: node + linkType: hard + "reselect@npm:^4.1.7": version: 4.1.8 resolution: "reselect@npm:4.1.8" @@ -23136,6 +23446,20 @@ __metadata: languageName: node linkType: hard +"rrweb-cssom@npm:^0.7.1": + version: 0.7.1 + resolution: "rrweb-cssom@npm:0.7.1" + checksum: 10c0/127b8ca6c8aac45e2755abbae6138d4a813b1bedc2caabf79466ae83ab3cfc84b5bfab513b7033f0aa4561c7753edf787d0dd01163ceacdee2e8eb1b6bf7237e + languageName: node + linkType: hard + +"rrweb-cssom@npm:^0.8.0": + version: 0.8.0 + resolution: "rrweb-cssom@npm:0.8.0" + checksum: 10c0/56f2bfd56733adb92c0b56e274c43f864b8dd48784d6fe946ef5ff8d438234015e59ad837fc2ad54714b6421384141c1add4eb569e72054e350d1f8a50b8ac7b + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -23200,6 +23524,15 @@ __metadata: languageName: node linkType: hard +"saxes@npm:^6.0.0": + version: 6.0.0 + resolution: "saxes@npm:6.0.0" + dependencies: + xmlchars: "npm:^2.2.0" + checksum: 10c0/3847b839f060ef3476eb8623d099aa502ad658f5c40fd60c105ebce86d244389b0d76fcae30f4d0c728d7705ceb2f7e9b34bb54717b6a7dbedaf5dad2d9a4b74 + languageName: node + linkType: hard + "sc-istanbul@npm:^0.4.5": version: 0.4.6 resolution: "sc-istanbul@npm:0.4.6" @@ -23579,7 +23912,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.6, side-channel@npm:^1.1.0": +"side-channel@npm:^1.0.4, side-channel@npm:^1.0.6, side-channel@npm:^1.1.0": version: 1.1.0 resolution: "side-channel@npm:1.1.0" dependencies: @@ -24046,7 +24379,7 @@ __metadata: languageName: node linkType: hard -"stop-iteration-iterator@npm:^1.1.0": +"stop-iteration-iterator@npm:^1.0.0, stop-iteration-iterator@npm:^1.1.0": version: 1.1.0 resolution: "stop-iteration-iterator@npm:1.1.0" dependencies: @@ -24518,6 +24851,13 @@ __metadata: languageName: node linkType: hard +"symbol-tree@npm:^3.2.4": + version: 3.2.4 + resolution: "symbol-tree@npm:3.2.4" + checksum: 10c0/dfbe201ae09ac6053d163578778c53aa860a784147ecf95705de0cd23f42c851e1be7889241495e95c37cabb058edb1052f141387bef68f705afc8f9dd358509 + languageName: node + linkType: hard + "sync-request@npm:^6.0.0": version: 6.1.0 resolution: "sync-request@npm:6.1.0" @@ -24991,6 +25331,18 @@ __metadata: languageName: node linkType: hard +"tough-cookie@npm:^4.1.4": + version: 4.1.4 + resolution: "tough-cookie@npm:4.1.4" + dependencies: + psl: "npm:^1.1.33" + punycode: "npm:^2.1.1" + universalify: "npm:^0.2.0" + url-parse: "npm:^1.5.3" + checksum: 10c0/aca7ff96054f367d53d1e813e62ceb7dd2eda25d7752058a74d64b7266fd07be75908f3753a32ccf866a2f997604b414cfb1916d6e7f69bc64d9d9939b0d6c45 + languageName: node + linkType: hard + "tr46@npm:^1.0.1": version: 1.0.1 resolution: "tr46@npm:1.0.1" @@ -25000,6 +25352,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^5.1.0": + version: 5.1.1 + resolution: "tr46@npm:5.1.1" + dependencies: + punycode: "npm:^2.3.1" + checksum: 10c0/ae270e194d52ec67ebd695c1a42876e0f19b96e4aca2ab464ab1d9d17dc3acd3e18764f5034c93897db73421563be27c70c98359c4501136a497e46deda5d5ec + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -25689,6 +26050,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^0.2.0": + version: 0.2.0 + resolution: "universalify@npm:0.2.0" + checksum: 10c0/cedbe4d4ca3967edf24c0800cfc161c5a15e240dac28e3ce575c689abc11f2c81ccc6532c8752af3b40f9120fb5e454abecd359e164f4f6aa44c29cd37e194fe + languageName: node + linkType: hard + "universalify@npm:^2.0.0": version: 2.0.1 resolution: "universalify@npm:2.0.1" @@ -25793,6 +26161,16 @@ __metadata: languageName: node linkType: hard +"url-parse@npm:^1.5.3": + version: 1.5.10 + resolution: "url-parse@npm:1.5.10" + dependencies: + querystringify: "npm:^2.1.1" + requires-port: "npm:^1.0.0" + checksum: 10c0/bd5aa9389f896974beb851c112f63b466505a04b4807cea2e5a3b7092f6fbb75316f0491ea84e44f66fed55f1b440df5195d7e3a8203f64fcefa19d182f5be87 + languageName: node + linkType: hard + "urlpattern-polyfill@npm:10.0.0": version: 10.0.0 resolution: "urlpattern-polyfill@npm:10.0.0" @@ -26160,6 +26538,15 @@ __metadata: languageName: node linkType: hard +"w3c-xmlserializer@npm:^5.0.0": + version: 5.0.0 + resolution: "w3c-xmlserializer@npm:5.0.0" + dependencies: + xml-name-validator: "npm:^5.0.0" + checksum: 10c0/8712774c1aeb62dec22928bf1cdfd11426c2c9383a1a63f2bcae18db87ca574165a0fbe96b312b73652149167ac6c7f4cf5409f2eb101d9c805efe0e4bae798b + languageName: node + linkType: hard + "walk-up-path@npm:^4.0.0": version: 4.0.0 resolution: "walk-up-path@npm:4.0.0" @@ -26290,6 +26677,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^7.0.0": + version: 7.0.0 + resolution: "webidl-conversions@npm:7.0.0" + checksum: 10c0/228d8cb6d270c23b0720cb2d95c579202db3aaf8f633b4e9dd94ec2000a04e7e6e43b76a94509cdb30479bd00ae253ab2371a2da9f81446cc313f89a4213a2c4 + languageName: node + linkType: hard + "webpack-sources@npm:^3.2.3": version: 3.3.3 resolution: "webpack-sources@npm:3.3.3" @@ -26334,6 +26728,15 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^3.1.1": + version: 3.1.1 + resolution: "whatwg-encoding@npm:3.1.1" + dependencies: + iconv-lite: "npm:0.6.3" + checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e + languageName: node + linkType: hard + "whatwg-fetch@npm:^3.0.0": version: 3.6.20 resolution: "whatwg-fetch@npm:3.6.20" @@ -26341,6 +26744,23 @@ __metadata: languageName: node linkType: hard +"whatwg-mimetype@npm:^4.0.0": + version: 4.0.0 + resolution: "whatwg-mimetype@npm:4.0.0" + checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df + languageName: node + linkType: hard + +"whatwg-url@npm:^14.0.0": + version: 14.2.0 + resolution: "whatwg-url@npm:14.2.0" + dependencies: + tr46: "npm:^5.1.0" + webidl-conversions: "npm:^7.0.0" + checksum: 10c0/f746fc2f4c906607d09537de1227b13f9494c171141e5427ed7d2c0dd0b6a48b43d8e71abaae57d368d0c06b673fd8ec63550b32ad5ed64990c7b0266c2b4272 + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" @@ -26362,7 +26782,7 @@ __metadata: languageName: node linkType: hard -"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": +"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" dependencies: @@ -26396,7 +26816,7 @@ __metadata: languageName: node linkType: hard -"which-collection@npm:^1.0.2": +"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2": version: 1.0.2 resolution: "which-collection@npm:1.0.2" dependencies: @@ -26415,7 +26835,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2": version: 1.1.19 resolution: "which-typed-array@npm:1.1.19" dependencies: @@ -26645,6 +27065,35 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.18.0": + version: 8.18.3 + resolution: "ws@npm:8.18.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/eac918213de265ef7cb3d4ca348b891a51a520d839aa51cdb8ca93d4fa7ff9f6ccb339ccee89e4075324097f0a55157c89fa3f7147bde9d8d7e90335dc087b53 + languageName: node + linkType: hard + +"xml-name-validator@npm:^5.0.0": + version: 5.0.0 + resolution: "xml-name-validator@npm:5.0.0" + checksum: 10c0/3fcf44e7b73fb18be917fdd4ccffff3639373c7cb83f8fc35df6001fecba7942f1dbead29d91ebb8315e2f2ff786b508f0c9dc0215b6353f9983c6b7d62cb1f5 + languageName: node + linkType: hard + +"xmlchars@npm:^2.2.0": + version: 2.2.0 + resolution: "xmlchars@npm:2.2.0" + checksum: 10c0/b64b535861a6f310c5d9bfa10834cf49127c71922c297da9d4d1b45eeaae40bf9b4363275876088fbe2667e5db028d2cd4f8ee72eed9bede840a67d57dab7593 + languageName: node + linkType: hard + "xmlhttprequest-ssl@npm:~2.1.1": version: 2.1.2 resolution: "xmlhttprequest-ssl@npm:2.1.2"