@spacesops/wdk-react-native-core 1.0.0-beta.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +551 -0
- package/dist/__mocks__/react.d.ts +40 -0
- package/dist/__mocks__/react.js +33 -0
- package/dist/__mocks__/react.js.map +1 -0
- package/dist/__mocks__/secureStorage.d.ts +26 -0
- package/dist/__mocks__/secureStorage.js +80 -0
- package/dist/__mocks__/secureStorage.js.map +1 -0
- package/dist/__tests__/hooks/useBalance.test.d.ts +6 -0
- package/dist/__tests__/hooks/useBalance.test.js +217 -0
- package/dist/__tests__/hooks/useBalance.test.js.map +1 -0
- package/dist/__tests__/hooks/useWallet.test.d.ts +6 -0
- package/dist/__tests__/hooks/useWallet.test.js +105 -0
- package/dist/__tests__/hooks/useWallet.test.js.map +1 -0
- package/dist/__tests__/hooks/useWalletManager.test.d.ts +6 -0
- package/dist/__tests__/hooks/useWalletManager.test.js +193 -0
- package/dist/__tests__/hooks/useWalletManager.test.js.map +1 -0
- package/dist/__tests__/hooks/useWdkApp.test.d.ts +6 -0
- package/dist/__tests__/hooks/useWdkApp.test.js +55 -0
- package/dist/__tests__/hooks/useWdkApp.test.js.map +1 -0
- package/dist/__tests__/hooks/useWorklet.test.d.ts +6 -0
- package/dist/__tests__/hooks/useWorklet.test.js +168 -0
- package/dist/__tests__/hooks/useWorklet.test.js.map +1 -0
- package/dist/__tests__/provider/WdkAppProvider.test.d.ts +6 -0
- package/dist/__tests__/provider/WdkAppProvider.test.js +57 -0
- package/dist/__tests__/provider/WdkAppProvider.test.js.map +1 -0
- package/dist/__tests__/services/accountService.test.d.ts +6 -0
- package/dist/__tests__/services/accountService.test.js +216 -0
- package/dist/__tests__/services/accountService.test.js.map +1 -0
- package/dist/__tests__/services/addressService.test.d.ts +6 -0
- package/dist/__tests__/services/addressService.test.js +246 -0
- package/dist/__tests__/services/addressService.test.js.map +1 -0
- package/dist/__tests__/services/balanceService.test.d.ts +6 -0
- package/dist/__tests__/services/balanceService.test.js +333 -0
- package/dist/__tests__/services/balanceService.test.js.map +1 -0
- package/dist/__tests__/services/walletSetupService.test.d.ts +6 -0
- package/dist/__tests__/services/walletSetupService.test.js +304 -0
- package/dist/__tests__/services/walletSetupService.test.js.map +1 -0
- package/dist/__tests__/services/walletSwitchingService.test.d.ts +6 -0
- package/dist/__tests__/services/walletSwitchingService.test.js +209 -0
- package/dist/__tests__/services/walletSwitchingService.test.js.map +1 -0
- package/dist/__tests__/services/workletLifecycleService.test.d.ts +6 -0
- package/dist/__tests__/services/workletLifecycleService.test.js +451 -0
- package/dist/__tests__/services/workletLifecycleService.test.js.map +1 -0
- package/dist/__tests__/setup.d.ts +7 -0
- package/dist/__tests__/setup.js +49 -0
- package/dist/__tests__/setup.js.map +1 -0
- package/dist/__tests__/storage/mmkvStorage.test.d.ts +6 -0
- package/dist/__tests__/storage/mmkvStorage.test.js +213 -0
- package/dist/__tests__/storage/mmkvStorage.test.js.map +1 -0
- package/dist/__tests__/store/walletStore.test.d.ts +4 -0
- package/dist/__tests__/store/walletStore.test.js +86 -0
- package/dist/__tests__/store/walletStore.test.js.map +1 -0
- package/dist/__tests__/store/workletStore.test.d.ts +4 -0
- package/dist/__tests__/store/workletStore.test.js +255 -0
- package/dist/__tests__/store/workletStore.test.js.map +1 -0
- package/dist/__tests__/types/hrpc.test.d.ts +4 -0
- package/dist/__tests__/types/hrpc.test.js +99 -0
- package/dist/__tests__/types/hrpc.test.js.map +1 -0
- package/dist/__tests__/utils/balanceUtils.test.d.ts +4 -0
- package/dist/__tests__/utils/balanceUtils.test.js +133 -0
- package/dist/__tests__/utils/balanceUtils.test.js.map +1 -0
- package/dist/__tests__/utils/errorHandling.test.d.ts +6 -0
- package/dist/__tests__/utils/errorHandling.test.js +119 -0
- package/dist/__tests__/utils/errorHandling.test.js.map +1 -0
- package/dist/__tests__/utils/errorUtils.test.d.ts +4 -0
- package/dist/__tests__/utils/errorUtils.test.js +133 -0
- package/dist/__tests__/utils/errorUtils.test.js.map +1 -0
- package/dist/__tests__/utils/initializationState.test.d.ts +6 -0
- package/dist/__tests__/utils/initializationState.test.js +243 -0
- package/dist/__tests__/utils/initializationState.test.js.map +1 -0
- package/dist/__tests__/utils/jsonUtils.test.d.ts +4 -0
- package/dist/__tests__/utils/jsonUtils.test.js +118 -0
- package/dist/__tests__/utils/jsonUtils.test.js.map +1 -0
- package/dist/__tests__/utils/mmkvKeyManager.test.d.ts +6 -0
- package/dist/__tests__/utils/mmkvKeyManager.test.js +158 -0
- package/dist/__tests__/utils/mmkvKeyManager.test.js.map +1 -0
- package/dist/__tests__/utils/mnemonicUtils.test.d.ts +4 -0
- package/dist/__tests__/utils/mnemonicUtils.test.js +40 -0
- package/dist/__tests__/utils/mnemonicUtils.test.js.map +1 -0
- package/dist/__tests__/utils/raceConditions.test.d.ts +6 -0
- package/dist/__tests__/utils/raceConditions.test.js +239 -0
- package/dist/__tests__/utils/raceConditions.test.js.map +1 -0
- package/dist/__tests__/utils/result.test.d.ts +4 -0
- package/dist/__tests__/utils/result.test.js +66 -0
- package/dist/__tests__/utils/result.test.js.map +1 -0
- package/dist/__tests__/utils/schemas.test.d.ts +4 -0
- package/dist/__tests__/utils/schemas.test.js +66 -0
- package/dist/__tests__/utils/schemas.test.js.map +1 -0
- package/dist/__tests__/utils/storeHelpers.test.d.ts +4 -0
- package/dist/__tests__/utils/storeHelpers.test.js +227 -0
- package/dist/__tests__/utils/storeHelpers.test.js.map +1 -0
- package/dist/__tests__/utils/typeGuards.test.d.ts +4 -0
- package/dist/__tests__/utils/typeGuards.test.js +175 -0
- package/dist/__tests__/utils/typeGuards.test.js.map +1 -0
- package/dist/__tests__/utils/validation.test.d.ts +4 -0
- package/dist/__tests__/utils/validation.test.js +126 -0
- package/dist/__tests__/utils/validation.test.js.map +1 -0
- package/dist/__tests__/utils/walletUtils.test.d.ts +4 -0
- package/dist/__tests__/utils/walletUtils.test.js +156 -0
- package/dist/__tests__/utils/walletUtils.test.js.map +1 -0
- package/dist/hooks/useBalance.d.ts +170 -0
- package/dist/hooks/useBalance.js +442 -0
- package/dist/hooks/useBalance.js.map +1 -0
- package/dist/hooks/useWallet.d.ts +43 -0
- package/dist/hooks/useWallet.js +193 -0
- package/dist/hooks/useWallet.js.map +1 -0
- package/dist/hooks/useWalletManager.d.ts +100 -0
- package/dist/hooks/useWalletManager.js +526 -0
- package/dist/hooks/useWalletManager.js.map +1 -0
- package/dist/hooks/useWdkApp.d.ts +66 -0
- package/dist/hooks/useWdkApp.js +74 -0
- package/dist/hooks/useWdkApp.js.map +1 -0
- package/dist/hooks/useWorklet.d.ts +66 -0
- package/dist/hooks/useWorklet.js +42 -0
- package/dist/hooks/useWorklet.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/provider/WdkAppProvider.d.ts +112 -0
- package/dist/provider/WdkAppProvider.js +558 -0
- package/dist/provider/WdkAppProvider.js.map +1 -0
- package/dist/services/accountService.d.ts +52 -0
- package/dist/services/accountService.js +119 -0
- package/dist/services/accountService.js.map +1 -0
- package/dist/services/addressService.d.ts +22 -0
- package/dist/services/addressService.js +110 -0
- package/dist/services/addressService.js.map +1 -0
- package/dist/services/balanceService.d.ts +114 -0
- package/dist/services/balanceService.js +208 -0
- package/dist/services/balanceService.js.map +1 -0
- package/dist/services/walletSetupService.d.ts +217 -0
- package/dist/services/walletSetupService.js +535 -0
- package/dist/services/walletSetupService.js.map +1 -0
- package/dist/services/walletSwitchingService.d.ts +48 -0
- package/dist/services/walletSwitchingService.js +143 -0
- package/dist/services/walletSwitchingService.js.map +1 -0
- package/dist/services/workletLifecycleService.d.ts +117 -0
- package/dist/services/workletLifecycleService.js +433 -0
- package/dist/services/workletLifecycleService.js.map +1 -0
- package/dist/storage/mmkvStorage.d.ts +44 -0
- package/dist/storage/mmkvStorage.js +348 -0
- package/dist/storage/mmkvStorage.js.map +1 -0
- package/dist/store/walletStore.d.ts +130 -0
- package/dist/store/walletStore.js +204 -0
- package/dist/store/walletStore.js.map +1 -0
- package/dist/store/workletStore.d.ts +121 -0
- package/dist/store/workletStore.js +256 -0
- package/dist/store/workletStore.js.map +1 -0
- package/dist/types/hrpc.d.ts +60 -0
- package/dist/types/hrpc.js +26 -0
- package/dist/types/hrpc.js.map +1 -0
- package/dist/types.d.ts +168 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/balanceUtils.d.ts +30 -0
- package/dist/utils/balanceUtils.js +86 -0
- package/dist/utils/balanceUtils.js.map +1 -0
- package/dist/utils/constants.d.ts +66 -0
- package/dist/utils/constants.js +73 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/errorHandling.d.ts +47 -0
- package/dist/utils/errorHandling.js +54 -0
- package/dist/utils/errorHandling.js.map +1 -0
- package/dist/utils/errorUtils.d.ts +89 -0
- package/dist/utils/errorUtils.js +264 -0
- package/dist/utils/errorUtils.js.map +1 -0
- package/dist/utils/initializationState.d.ts +129 -0
- package/dist/utils/initializationState.js +220 -0
- package/dist/utils/initializationState.js.map +1 -0
- package/dist/utils/jsonUtils.d.ts +21 -0
- package/dist/utils/jsonUtils.js +67 -0
- package/dist/utils/jsonUtils.js.map +1 -0
- package/dist/utils/logger.d.ts +27 -0
- package/dist/utils/logger.js +50 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mmkvKeyManager.d.ts +57 -0
- package/dist/utils/mmkvKeyManager.js +214 -0
- package/dist/utils/mmkvKeyManager.js.map +1 -0
- package/dist/utils/mnemonicUtils.d.ts +17 -0
- package/dist/utils/mnemonicUtils.js +28 -0
- package/dist/utils/mnemonicUtils.js.map +1 -0
- package/dist/utils/operationMutex.d.ts +69 -0
- package/dist/utils/operationMutex.js +150 -0
- package/dist/utils/operationMutex.js.map +1 -0
- package/dist/utils/result.d.ts +34 -0
- package/dist/utils/result.js +43 -0
- package/dist/utils/result.js.map +1 -0
- package/dist/utils/schemas.d.ts +365 -0
- package/dist/utils/schemas.js +152 -0
- package/dist/utils/schemas.js.map +1 -0
- package/dist/utils/storeHelpers.d.ts +112 -0
- package/dist/utils/storeHelpers.js +198 -0
- package/dist/utils/storeHelpers.js.map +1 -0
- package/dist/utils/typeGuards.d.ts +70 -0
- package/dist/utils/typeGuards.js +95 -0
- package/dist/utils/typeGuards.js.map +1 -0
- package/dist/utils/validation.d.ts +53 -0
- package/dist/utils/validation.js +152 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/walletStateHelpers.d.ts +62 -0
- package/dist/utils/walletStateHelpers.js +81 -0
- package/dist/utils/walletStateHelpers.js.map +1 -0
- package/dist/utils/walletUtils.d.ts +38 -0
- package/dist/utils/walletUtils.js +87 -0
- package/dist/utils/walletUtils.js.map +1 -0
- package/package.json +79 -0
- package/src/__mocks__/react.ts +34 -0
- package/src/__mocks__/secureStorage.ts +87 -0
- package/src/__tests__/hooks/useBalance.test.ts +286 -0
- package/src/__tests__/hooks/useWallet.test.ts +122 -0
- package/src/__tests__/hooks/useWalletManager.test.ts +271 -0
- package/src/__tests__/hooks/useWdkApp.test.tsx +59 -0
- package/src/__tests__/hooks/useWorklet.test.ts +195 -0
- package/src/__tests__/provider/WdkAppProvider.test.tsx +67 -0
- package/src/__tests__/services/accountService.test.ts +337 -0
- package/src/__tests__/services/addressService.test.ts +312 -0
- package/src/__tests__/services/balanceService.test.ts +411 -0
- package/src/__tests__/services/walletSetupService.test.ts +463 -0
- package/src/__tests__/services/walletSwitchingService.test.ts +258 -0
- package/src/__tests__/services/workletLifecycleService.test.ts +514 -0
- package/src/__tests__/setup.ts +54 -0
- package/src/__tests__/storage/mmkvStorage.test.ts +325 -0
- package/src/__tests__/store/walletStore.test.ts +101 -0
- package/src/__tests__/store/workletStore.test.ts +329 -0
- package/src/__tests__/types/hrpc.test.ts +125 -0
- package/src/__tests__/utils/balanceUtils.test.ts +151 -0
- package/src/__tests__/utils/errorHandling.test.ts +147 -0
- package/src/__tests__/utils/errorUtils.test.ts +170 -0
- package/src/__tests__/utils/initializationState.test.ts +337 -0
- package/src/__tests__/utils/jsonUtils.test.ts +131 -0
- package/src/__tests__/utils/mmkvKeyManager.test.ts +205 -0
- package/src/__tests__/utils/mnemonicUtils.test.ts +48 -0
- package/src/__tests__/utils/raceConditions.test.ts +292 -0
- package/src/__tests__/utils/result.test.ts +73 -0
- package/src/__tests__/utils/schemas.test.ts +82 -0
- package/src/__tests__/utils/storeHelpers.test.ts +323 -0
- package/src/__tests__/utils/typeGuards.test.ts +209 -0
- package/src/__tests__/utils/validation.test.ts +152 -0
- package/src/__tests__/utils/walletUtils.test.ts +195 -0
- package/src/hooks/useBalance.ts +597 -0
- package/src/hooks/useWallet.ts +288 -0
- package/src/hooks/useWalletManager.ts +654 -0
- package/src/hooks/useWdkApp.ts +78 -0
- package/src/hooks/useWorklet.ts +109 -0
- package/src/index.ts +112 -0
- package/src/provider/WdkAppProvider.tsx +764 -0
- package/src/services/accountService.ts +134 -0
- package/src/services/addressService.ts +125 -0
- package/src/services/balanceService.ts +291 -0
- package/src/services/walletSetupService.ts +695 -0
- package/src/services/walletSwitchingService.ts +157 -0
- package/src/services/workletLifecycleService.ts +561 -0
- package/src/storage/mmkvStorage.ts +417 -0
- package/src/store/walletStore.ts +301 -0
- package/src/store/workletStore.ts +340 -0
- package/src/types/hrpc.ts +78 -0
- package/src/types.ts +188 -0
- package/src/utils/balanceUtils.ts +96 -0
- package/src/utils/constants.ts +84 -0
- package/src/utils/errorHandling.md +163 -0
- package/src/utils/errorHandling.ts +61 -0
- package/src/utils/errorUtils.ts +300 -0
- package/src/utils/initializationState.ts +242 -0
- package/src/utils/jsonUtils.ts +75 -0
- package/src/utils/logger.ts +54 -0
- package/src/utils/mmkvKeyManager.ts +256 -0
- package/src/utils/mnemonicUtils.ts +35 -0
- package/src/utils/operationMutex.ts +181 -0
- package/src/utils/result.ts +57 -0
- package/src/utils/schemas.ts +190 -0
- package/src/utils/storeHelpers.ts +236 -0
- package/src/utils/typeGuards.ts +129 -0
- package/src/utils/validation.ts +174 -0
- package/src/utils/walletStateHelpers.ts +104 -0
- package/src/utils/walletUtils.ts +107 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error utility functions for consistent error handling
|
|
3
|
+
*
|
|
4
|
+
* ## Usage Guidelines
|
|
5
|
+
*
|
|
6
|
+
* **For Services**: Use `handleServiceError()` from `errorHandling.ts` - this provides
|
|
7
|
+
* consistent normalization and logging for service-layer errors. Errors are NOT sanitized
|
|
8
|
+
* (sanitizeLevel: false) because services log internally and need full error details.
|
|
9
|
+
*
|
|
10
|
+
* **For Hooks/UI**: Use `normalizeError()` directly with appropriate sanitization level.
|
|
11
|
+
* Hooks should sanitize errors before exposing them to UI components to prevent information leakage.
|
|
12
|
+
*
|
|
13
|
+
* @example Service usage (use handleServiceError instead):
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { handleServiceError } from './errorHandling'
|
|
16
|
+
* try {
|
|
17
|
+
* await someOperation()
|
|
18
|
+
* } catch (error) {
|
|
19
|
+
* handleServiceError(error, 'MyService', 'operationName', { context })
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example Hook/UI usage:
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { normalizeError } from './errorUtils'
|
|
26
|
+
* try {
|
|
27
|
+
* await someOperation()
|
|
28
|
+
* } catch (error) {
|
|
29
|
+
* const normalized = normalizeError(error, true, { component: 'MyHook', operation: 'fetchData' })
|
|
30
|
+
* setError(normalized.message)
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Sanitization levels for error messages
|
|
37
|
+
*/
|
|
38
|
+
export enum SanitizationLevel {
|
|
39
|
+
NONE = 'none', // No sanitization (internal debugging only)
|
|
40
|
+
DEVELOPMENT = 'dev', // Mask sensitive strings but show structure
|
|
41
|
+
PRODUCTION = 'prod', // Aggressive sanitization
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Context-aware sensitive patterns
|
|
46
|
+
* More specific patterns to avoid false positives while catching real sensitive data
|
|
47
|
+
*/
|
|
48
|
+
const SENSITIVE_PATTERNS = [
|
|
49
|
+
// Cryptographic keys and secrets (more specific)
|
|
50
|
+
/\b(encryption[_-]?key|encryptionKey|encrypted[_-]?seed|encryptedSeed|secret[_-]?key|private[_-]?key)\s*[:=]\s*([a-f0-9]{32,}|[A-Za-z0-9+/]{40,})/gi,
|
|
51
|
+
// Mnemonic phrases (12 or 24 words) - allow alphanumeric words
|
|
52
|
+
/\b(mnemonic|seed[_-]?phrase|recovery[_-]?phrase)\s*[:=]\s*([a-zA-Z0-9]+\s+){11,23}[a-zA-Z0-9]+/gi,
|
|
53
|
+
// Base64 encoded keys (long base64 strings)
|
|
54
|
+
/[A-Za-z0-9+/]{40,}={0,2}/g,
|
|
55
|
+
// Hex strings that look like keys (32+ chars, even length)
|
|
56
|
+
/\b0x?[a-f0-9]{32,}\b/gi,
|
|
57
|
+
// File paths with sensitive names
|
|
58
|
+
/file:\/\/[^\s]*(key|secret|password|credential|seed|mnemonic|private)[^\s]*/gi,
|
|
59
|
+
// Paths containing sensitive directories
|
|
60
|
+
/\/(?:private|secret|keys|credentials|seeds|mnemonics)\/[^\s]+/gi,
|
|
61
|
+
// API tokens and keys in various formats
|
|
62
|
+
/\b(api[_-]?key|access[_-]?token|bearer[_-]?token|auth[_-]?token)\s*[:=]\s*[^\s]{20,}/gi,
|
|
63
|
+
// Passwords (but not "password" as a word)
|
|
64
|
+
/\bpassword\s*[:=]\s*[^\s]{8,}/gi,
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whitelist of safe patterns that should NOT be sanitized
|
|
69
|
+
* These are common non-sensitive terms that might match sensitive patterns
|
|
70
|
+
*/
|
|
71
|
+
const SAFE_PATTERNS = [
|
|
72
|
+
/\b(public[_-]?key|publicKey)\b/gi, // Public keys are safe
|
|
73
|
+
/\b(error|Error|ERROR)\b/g, // Error messages themselves
|
|
74
|
+
/\b(function|Function|const|let|var)\b/g, // Code keywords
|
|
75
|
+
/\b(undefined|null|true|false)\b/g, // JavaScript literals
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if a string matches a safe pattern (should not be sanitized)
|
|
80
|
+
*/
|
|
81
|
+
function isSafePattern(text: string): boolean {
|
|
82
|
+
return SAFE_PATTERNS.some(pattern => pattern.test(text))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Mask sensitive string patterns (hex/base64) for development mode
|
|
87
|
+
*/
|
|
88
|
+
function maskSensitiveStrings(message: string): string {
|
|
89
|
+
return message
|
|
90
|
+
.replace(/\b0x?[a-f0-9]{32,}\b/gi, (match) => {
|
|
91
|
+
if (match.length <= 20) return match
|
|
92
|
+
return `${match.substring(0, 8)}...${match.substring(match.length - 4)}`
|
|
93
|
+
})
|
|
94
|
+
.replace(/\b[A-Za-z0-9+/]{40,}={0,2}\b/g, (match) => {
|
|
95
|
+
return `${match.substring(0, 8)}...${match.substring(match.length - 4)}`
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Remove file paths from error messages
|
|
101
|
+
*/
|
|
102
|
+
function removeFilePaths(message: string): string {
|
|
103
|
+
return message
|
|
104
|
+
.replace(/file:\/\/[^\s]+/gi, '[file path]')
|
|
105
|
+
.replace(/\/[^\s]+\/[^\s]+/g, '[path]')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get replacement text for a sensitive pattern match
|
|
110
|
+
*/
|
|
111
|
+
function getSensitiveReplacement(match: string): string {
|
|
112
|
+
const lowerMatch = match.toLowerCase()
|
|
113
|
+
if (lowerMatch.includes('encryption') || lowerMatch.includes('encrypted')) {
|
|
114
|
+
return '[encryption data]'
|
|
115
|
+
}
|
|
116
|
+
if (lowerMatch.includes('mnemonic') || lowerMatch.includes('seed phrase')) {
|
|
117
|
+
return '[mnemonic phrase]'
|
|
118
|
+
}
|
|
119
|
+
if (lowerMatch.includes('key') && !lowerMatch.includes('public')) {
|
|
120
|
+
return '[key]'
|
|
121
|
+
}
|
|
122
|
+
if (lowerMatch.includes('token')) {
|
|
123
|
+
return '[token]'
|
|
124
|
+
}
|
|
125
|
+
if (lowerMatch.includes('password')) {
|
|
126
|
+
return '[password]'
|
|
127
|
+
}
|
|
128
|
+
if (lowerMatch.includes('secret')) {
|
|
129
|
+
return '[secret]'
|
|
130
|
+
}
|
|
131
|
+
if (/[a-f0-9]{32,}/i.test(match) || /[A-Za-z0-9+/]{40,}/.test(match)) {
|
|
132
|
+
return '[sensitive data]'
|
|
133
|
+
}
|
|
134
|
+
return '[sensitive]'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Apply sensitive pattern sanitization
|
|
139
|
+
*/
|
|
140
|
+
function applySensitivePatternSanitization(message: string): string {
|
|
141
|
+
let sanitized = message
|
|
142
|
+
for (const pattern of SENSITIVE_PATTERNS) {
|
|
143
|
+
sanitized = sanitized.replace(pattern, (match) => {
|
|
144
|
+
if (isSafePattern(match)) {
|
|
145
|
+
return match
|
|
146
|
+
}
|
|
147
|
+
return getSensitiveReplacement(match)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
return sanitized
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Sanitize error message to prevent information leakage
|
|
155
|
+
* Removes or masks sensitive information while preserving useful debugging info
|
|
156
|
+
*
|
|
157
|
+
* @param message - Error message to sanitize
|
|
158
|
+
* @param isDevelopment - Whether we're in development mode (less sanitization)
|
|
159
|
+
* @param context - Optional context about where the error occurred (for better sanitization)
|
|
160
|
+
* @returns Sanitized error message
|
|
161
|
+
*/
|
|
162
|
+
export function sanitizeErrorMessage(
|
|
163
|
+
message: string,
|
|
164
|
+
isDevelopment = false,
|
|
165
|
+
context?: { operation?: string; component?: string }
|
|
166
|
+
): string {
|
|
167
|
+
if (isDevelopment) {
|
|
168
|
+
return maskSensitiveStrings(message)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// In production, be more aggressive with sanitization
|
|
172
|
+
let sanitized = removeFilePaths(message)
|
|
173
|
+
sanitized = applySensitivePatternSanitization(sanitized)
|
|
174
|
+
|
|
175
|
+
// Additional cleanup: remove any remaining long hex/base64 strings
|
|
176
|
+
sanitized = sanitized.replace(/\b0x?[a-f0-9]{32,}\b/gi, '[hex string]')
|
|
177
|
+
sanitized = sanitized.replace(/\b[A-Za-z0-9+/]{40,}={0,2}\b/g, '[base64 string]')
|
|
178
|
+
|
|
179
|
+
return sanitized
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Normalize error to Error instance
|
|
184
|
+
* Converts any error-like value to a proper Error object
|
|
185
|
+
* Always sanitizes the error message (with different levels) to prevent information leakage
|
|
186
|
+
*
|
|
187
|
+
* @param error - Error to normalize
|
|
188
|
+
* @param sanitizeLevel - Sanitization level or boolean (default: PRODUCTION in production, DEVELOPMENT in dev)
|
|
189
|
+
* @param context - Optional context about where the error occurred
|
|
190
|
+
* @returns Normalized Error instance
|
|
191
|
+
*/
|
|
192
|
+
export function normalizeError(
|
|
193
|
+
error: unknown,
|
|
194
|
+
sanitizeLevel: SanitizationLevel | boolean = process.env.NODE_ENV === 'production'
|
|
195
|
+
? SanitizationLevel.PRODUCTION
|
|
196
|
+
: SanitizationLevel.DEVELOPMENT,
|
|
197
|
+
context?: { operation?: string; component?: string; [key: string]: unknown }
|
|
198
|
+
): Error {
|
|
199
|
+
let errorMessage: string
|
|
200
|
+
|
|
201
|
+
if (error instanceof Error) {
|
|
202
|
+
errorMessage = error.message
|
|
203
|
+
} else if (typeof error === 'string') {
|
|
204
|
+
errorMessage = error
|
|
205
|
+
} else if (error && typeof error === 'object' && 'message' in error) {
|
|
206
|
+
errorMessage = String(error.message)
|
|
207
|
+
} else {
|
|
208
|
+
errorMessage = String(error)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Always sanitize, but with different levels
|
|
212
|
+
const level = typeof sanitizeLevel === 'boolean'
|
|
213
|
+
? (sanitizeLevel ? SanitizationLevel.PRODUCTION : SanitizationLevel.NONE)
|
|
214
|
+
: sanitizeLevel
|
|
215
|
+
|
|
216
|
+
if (level !== SanitizationLevel.NONE) {
|
|
217
|
+
errorMessage = sanitizeErrorMessage(
|
|
218
|
+
errorMessage,
|
|
219
|
+
level === SanitizationLevel.DEVELOPMENT,
|
|
220
|
+
context
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const normalizedError = new Error(errorMessage)
|
|
225
|
+
|
|
226
|
+
// Preserve error name and stack if available
|
|
227
|
+
if (error instanceof Error) {
|
|
228
|
+
normalizedError.name = error.name
|
|
229
|
+
// Sanitize stack trace based on level
|
|
230
|
+
if (error.stack) {
|
|
231
|
+
if (level !== SanitizationLevel.NONE) {
|
|
232
|
+
// Mask file paths and sensitive data in stack traces
|
|
233
|
+
normalizedError.stack = sanitizeErrorMessage(error.stack, level === SanitizationLevel.DEVELOPMENT, context)
|
|
234
|
+
} else {
|
|
235
|
+
normalizedError.stack = error.stack
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return normalizedError
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get error message from any error-like value
|
|
245
|
+
*/
|
|
246
|
+
export function getErrorMessage(error: unknown): string {
|
|
247
|
+
return normalizeError(error).message
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Check if error is a specific type
|
|
252
|
+
*/
|
|
253
|
+
export function isErrorType(error: unknown, typeName: string): boolean {
|
|
254
|
+
return error instanceof Error && error.name === typeName
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Create a standardized error with context
|
|
259
|
+
*/
|
|
260
|
+
export function createContextualError(
|
|
261
|
+
message: string,
|
|
262
|
+
context?: Record<string, unknown>
|
|
263
|
+
): Error {
|
|
264
|
+
const error = new Error(message)
|
|
265
|
+
if (context) {
|
|
266
|
+
Object.assign(error, { context })
|
|
267
|
+
}
|
|
268
|
+
return error
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Check if an error is an authentication error
|
|
273
|
+
* Used to prevent automatic retries when authentication fails
|
|
274
|
+
*
|
|
275
|
+
* @param error - Error to check
|
|
276
|
+
* @returns true if the error is an authentication error
|
|
277
|
+
*/
|
|
278
|
+
export function isAuthenticationError(error: unknown): boolean {
|
|
279
|
+
// Check if it's an AuthenticationError instance from secure storage
|
|
280
|
+
if (error && typeof error === 'object' && 'constructor' in error) {
|
|
281
|
+
const errorName = error.constructor.name
|
|
282
|
+
if (errorName === 'AuthenticationError') {
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check if it's an Error instance with authentication-related properties
|
|
288
|
+
if (error instanceof Error) {
|
|
289
|
+
const msg = error.message.toLowerCase()
|
|
290
|
+
return (
|
|
291
|
+
error.name === 'AuthenticationError' ||
|
|
292
|
+
msg.includes('authentication') ||
|
|
293
|
+
msg.includes('biometric') ||
|
|
294
|
+
msg.includes('authentication required but failed')
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worklet Initialization State Machine
|
|
3
|
+
*
|
|
4
|
+
* Represents the state of the worklet runtime initialization (global, happens once).
|
|
5
|
+
* This is separate from wallet loading, which happens per-identifier.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Worklet initialization status enum
|
|
10
|
+
*
|
|
11
|
+
* Represents the current state of the worklet runtime initialization.
|
|
12
|
+
* The worklet is initialized once (global), but wallets are loaded per-identifier.
|
|
13
|
+
*
|
|
14
|
+
* Flow:
|
|
15
|
+
* 1. IDLE -> STARTING_WORKLET (worklet initialization begins)
|
|
16
|
+
* 2. STARTING_WORKLET -> WORKLET_READY (worklet runtime ready, can now load wallets)
|
|
17
|
+
*
|
|
18
|
+
* After WORKLET_READY, you can load different wallets (per identifier).
|
|
19
|
+
* Wallet loading state is separate - see walletState in WdkAppContextValue.
|
|
20
|
+
*/
|
|
21
|
+
export enum InitializationStatus {
|
|
22
|
+
/** Initial state - worklet not started */
|
|
23
|
+
IDLE = 'idle',
|
|
24
|
+
/** Worklet runtime is starting (happens once, global) */
|
|
25
|
+
STARTING_WORKLET = 'starting_worklet',
|
|
26
|
+
/** Worklet is ready - can now load wallets (per identifier) */
|
|
27
|
+
WORKLET_READY = 'worklet_ready',
|
|
28
|
+
/** Error state - worklet initialization failed */
|
|
29
|
+
ERROR = 'error',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* App-level status enum
|
|
34
|
+
*
|
|
35
|
+
* Represents the combined state of worklet initialization and wallet loading.
|
|
36
|
+
* This is a convenience enum for app-level "is ready?" checks.
|
|
37
|
+
*
|
|
38
|
+
* For granular control, use workletState and walletState separately.
|
|
39
|
+
*/
|
|
40
|
+
export enum AppStatus {
|
|
41
|
+
/** Worklet not started */
|
|
42
|
+
IDLE = 'idle',
|
|
43
|
+
/** Worklet is starting */
|
|
44
|
+
STARTING_WORKLET = 'starting_worklet',
|
|
45
|
+
/** Worklet ready, no wallet loaded */
|
|
46
|
+
WORKLET_READY = 'worklet_ready',
|
|
47
|
+
/** Loading a wallet (checking existence, decrypting, initializing) */
|
|
48
|
+
LOADING_WALLET = 'loading_wallet',
|
|
49
|
+
/** Fully ready - worklet started, wallet loaded, addresses available */
|
|
50
|
+
READY = 'ready',
|
|
51
|
+
/** Error state - worklet or wallet error */
|
|
52
|
+
ERROR = 'error',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Helper to check if status represents an error state
|
|
57
|
+
*/
|
|
58
|
+
export function isErrorStatus(status: InitializationStatus): boolean {
|
|
59
|
+
return status === InitializationStatus.ERROR
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Helper to check if worklet initialization is complete and ready
|
|
64
|
+
*/
|
|
65
|
+
export function isReadyStatus(status: InitializationStatus): boolean {
|
|
66
|
+
return status === InitializationStatus.WORKLET_READY
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Helper to check if worklet initialization is in progress
|
|
71
|
+
*/
|
|
72
|
+
export function isInProgressStatus(status: InitializationStatus): boolean {
|
|
73
|
+
return status === InitializationStatus.STARTING_WORKLET
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Helper to check if app status represents an in-progress state
|
|
78
|
+
*/
|
|
79
|
+
export function isAppInProgressStatus(status: AppStatus): boolean {
|
|
80
|
+
return [
|
|
81
|
+
AppStatus.STARTING_WORKLET,
|
|
82
|
+
AppStatus.LOADING_WALLET,
|
|
83
|
+
].includes(status)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Helper to check if app is ready (worklet + wallet both ready)
|
|
88
|
+
*/
|
|
89
|
+
export function isAppReadyStatus(status: AppStatus): boolean {
|
|
90
|
+
return status === AppStatus.READY
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Helper to check if worklet has started (worklet runtime is ready)
|
|
95
|
+
* Once worklet is ready, you can load wallets (per identifier)
|
|
96
|
+
*/
|
|
97
|
+
export function hasWorkletStarted(status: InitializationStatus): boolean {
|
|
98
|
+
return [
|
|
99
|
+
InitializationStatus.WORKLET_READY,
|
|
100
|
+
InitializationStatus.ERROR,
|
|
101
|
+
].includes(status)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Helper to check if wallet operations can be performed
|
|
106
|
+
* Returns true when worklet is ready (wallets can be loaded per identifier)
|
|
107
|
+
*/
|
|
108
|
+
export function canLoadWallet(status: InitializationStatus): boolean {
|
|
109
|
+
return status === InitializationStatus.WORKLET_READY
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Helper to check if app status indicates worklet has started
|
|
114
|
+
*/
|
|
115
|
+
export function hasWorkletStartedApp(status: AppStatus): boolean {
|
|
116
|
+
return [
|
|
117
|
+
AppStatus.WORKLET_READY,
|
|
118
|
+
AppStatus.LOADING_WALLET,
|
|
119
|
+
AppStatus.READY,
|
|
120
|
+
AppStatus.ERROR,
|
|
121
|
+
].includes(status)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Helper to check if wallet operations can be performed based on app status
|
|
126
|
+
*/
|
|
127
|
+
export function canLoadWalletApp(status: AppStatus): boolean {
|
|
128
|
+
return [
|
|
129
|
+
AppStatus.WORKLET_READY,
|
|
130
|
+
AppStatus.LOADING_WALLET,
|
|
131
|
+
AppStatus.READY,
|
|
132
|
+
].includes(status)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get human-readable worklet initialization status message
|
|
137
|
+
*/
|
|
138
|
+
export function getStatusMessage(status: InitializationStatus): string {
|
|
139
|
+
switch (status) {
|
|
140
|
+
case InitializationStatus.IDLE:
|
|
141
|
+
return 'Not started'
|
|
142
|
+
case InitializationStatus.STARTING_WORKLET:
|
|
143
|
+
return 'Starting worklet...'
|
|
144
|
+
case InitializationStatus.WORKLET_READY:
|
|
145
|
+
return 'Worklet ready - can load wallets'
|
|
146
|
+
case InitializationStatus.ERROR:
|
|
147
|
+
return 'Worklet error'
|
|
148
|
+
default:
|
|
149
|
+
return 'Unknown'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get human-readable app status message
|
|
155
|
+
*/
|
|
156
|
+
export function getAppStatusMessage(status: AppStatus): string {
|
|
157
|
+
switch (status) {
|
|
158
|
+
case AppStatus.IDLE:
|
|
159
|
+
return 'Not started'
|
|
160
|
+
case AppStatus.STARTING_WORKLET:
|
|
161
|
+
return 'Starting worklet...'
|
|
162
|
+
case AppStatus.WORKLET_READY:
|
|
163
|
+
return 'Worklet ready - can load wallets'
|
|
164
|
+
case AppStatus.LOADING_WALLET:
|
|
165
|
+
return 'Loading wallet...'
|
|
166
|
+
case AppStatus.READY:
|
|
167
|
+
return 'Ready'
|
|
168
|
+
case AppStatus.ERROR:
|
|
169
|
+
return 'Error'
|
|
170
|
+
default:
|
|
171
|
+
return 'Unknown'
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Gets worklet initialization status from worklet state
|
|
177
|
+
*
|
|
178
|
+
* @param workletState - Worklet state (global, from workletStore)
|
|
179
|
+
* @returns Worklet initialization status
|
|
180
|
+
*/
|
|
181
|
+
export function getWorkletStatus(
|
|
182
|
+
workletState: { isWorkletStarted: boolean; isLoading: boolean; error: string | null }
|
|
183
|
+
): InitializationStatus {
|
|
184
|
+
if (workletState.error) {
|
|
185
|
+
return InitializationStatus.ERROR
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!workletState.isWorkletStarted) {
|
|
189
|
+
return workletState.isLoading
|
|
190
|
+
? InitializationStatus.STARTING_WORKLET
|
|
191
|
+
: InitializationStatus.IDLE
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return InitializationStatus.WORKLET_READY
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Derives combined app status from worklet and wallet states
|
|
199
|
+
*
|
|
200
|
+
* This is a convenience function that combines the global worklet state with the
|
|
201
|
+
* per-identifier wallet state to produce a unified app-level status.
|
|
202
|
+
*
|
|
203
|
+
* NOTE: This is for convenience only. For granular control, use workletState and
|
|
204
|
+
* walletState separately. The combined status hides some information (e.g., can't
|
|
205
|
+
* distinguish worklet errors from wallet errors).
|
|
206
|
+
*
|
|
207
|
+
* @param workletState - Worklet state (global, from workletStore)
|
|
208
|
+
* @param walletState - Wallet state (per-identifier, from wallet state machine)
|
|
209
|
+
* @returns Combined app status (convenience helper)
|
|
210
|
+
*/
|
|
211
|
+
export function getCombinedStatus(
|
|
212
|
+
workletState: { isWorkletStarted: boolean; isLoading: boolean; error: string | null },
|
|
213
|
+
walletState: { type: 'not_loaded' | 'checking' | 'loading' | 'ready' | 'error' }
|
|
214
|
+
): AppStatus {
|
|
215
|
+
// Worklet errors take precedence
|
|
216
|
+
if (workletState.error) {
|
|
217
|
+
return AppStatus.ERROR
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Worklet not ready
|
|
221
|
+
if (!workletState.isWorkletStarted) {
|
|
222
|
+
return workletState.isLoading
|
|
223
|
+
? AppStatus.STARTING_WORKLET
|
|
224
|
+
: AppStatus.IDLE
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Worklet ready, check wallet state
|
|
228
|
+
switch (walletState.type) {
|
|
229
|
+
case 'not_loaded':
|
|
230
|
+
return AppStatus.WORKLET_READY
|
|
231
|
+
case 'checking':
|
|
232
|
+
case 'loading':
|
|
233
|
+
return AppStatus.LOADING_WALLET
|
|
234
|
+
case 'ready':
|
|
235
|
+
return AppStatus.READY
|
|
236
|
+
case 'error':
|
|
237
|
+
return AppStatus.ERROR
|
|
238
|
+
default:
|
|
239
|
+
return AppStatus.IDLE
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides safe JSON stringification and validation utilities
|
|
5
|
+
* to prevent security issues and ensure data integrity.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validate that a value has a safe JSON structure
|
|
10
|
+
* Prevents prototype pollution and circular references
|
|
11
|
+
*/
|
|
12
|
+
export function validateJSONStructure(value: unknown): boolean {
|
|
13
|
+
try {
|
|
14
|
+
// Check for circular references
|
|
15
|
+
const seen = new WeakSet()
|
|
16
|
+
|
|
17
|
+
function check(value: unknown): boolean {
|
|
18
|
+
if (value === null || typeof value !== 'object') {
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (seen.has(value as object)) {
|
|
23
|
+
return false // Circular reference
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
seen.add(value as object)
|
|
27
|
+
|
|
28
|
+
if (Array.isArray(value)) {
|
|
29
|
+
return value.every(check)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check for prototype pollution
|
|
33
|
+
const proto = Object.getPrototypeOf(value)
|
|
34
|
+
// Reject objects with non-standard prototypes (not null, Object.prototype, or Array.prototype)
|
|
35
|
+
// Note: Objects created with Object.create(Array.prototype) should be rejected
|
|
36
|
+
if (proto !== null && proto !== Object.prototype) {
|
|
37
|
+
// Allow Array.prototype only for actual arrays (checked above)
|
|
38
|
+
// Reject objects that inherit from Array.prototype but aren't arrays
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return Object.values(value as Record<string, unknown>).every(check)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return check(value)
|
|
46
|
+
} catch {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Safe JSON stringify with validation
|
|
53
|
+
* Validates structure before stringifying to prevent security issues
|
|
54
|
+
*
|
|
55
|
+
* @param value - Value to stringify
|
|
56
|
+
* @param space - Optional spacing for pretty printing
|
|
57
|
+
* @returns JSON string
|
|
58
|
+
* @throws Error if value cannot be safely stringified
|
|
59
|
+
*/
|
|
60
|
+
export function safeStringify(value: unknown, space?: number): string {
|
|
61
|
+
// Validate structure first
|
|
62
|
+
if (!validateJSONStructure(value)) {
|
|
63
|
+
throw new Error('Value contains circular references or unsafe prototype properties')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
return JSON.stringify(value, null, space)
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error instanceof Error) {
|
|
70
|
+
throw new Error(`Failed to stringify value: ${error.message}`)
|
|
71
|
+
}
|
|
72
|
+
throw new Error('Failed to stringify value: Unknown error')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility for development and production
|
|
3
|
+
*
|
|
4
|
+
* Provides controlled logging that can be disabled in production
|
|
5
|
+
* to improve performance and prevent information leakage.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if we're in development mode
|
|
10
|
+
* React Native sets __DEV__ to true in development builds
|
|
11
|
+
*/
|
|
12
|
+
const isDevelopment = typeof __DEV__ !== 'undefined' ? __DEV__ : process.env.NODE_ENV !== 'production'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Log a message (only in development)
|
|
16
|
+
*
|
|
17
|
+
* @param message - Message to log
|
|
18
|
+
* @param args - Additional arguments to log
|
|
19
|
+
*/
|
|
20
|
+
export function log(...args: unknown[]): void {
|
|
21
|
+
if (isDevelopment) {
|
|
22
|
+
console.log(...args)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Log an error (always logged, but sanitized in production)
|
|
28
|
+
*
|
|
29
|
+
* @param message - Error message
|
|
30
|
+
* @param error - Error object or additional data
|
|
31
|
+
*/
|
|
32
|
+
export function logError(message: string, error?: unknown): void {
|
|
33
|
+
if (isDevelopment) {
|
|
34
|
+
console.error(message, error)
|
|
35
|
+
} else {
|
|
36
|
+
// In production, log sanitized errors only
|
|
37
|
+
// This prevents information leakage while still allowing error tracking
|
|
38
|
+
console.error(message)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Log a warning (only in development)
|
|
44
|
+
*
|
|
45
|
+
* @param message - Warning message
|
|
46
|
+
* @param args - Additional arguments to log
|
|
47
|
+
*/
|
|
48
|
+
export function logWarn(...args: unknown[]): void {
|
|
49
|
+
if (isDevelopment) {
|
|
50
|
+
console.warn(...args)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|