@bananalink-sdk/protocol 1.2.7

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.
Files changed (158) hide show
  1. package/README.md +604 -0
  2. package/dist/chunk-32OWUOZ3.js +308 -0
  3. package/dist/chunk-32OWUOZ3.js.map +1 -0
  4. package/dist/chunk-65HNHRJK.cjs +123 -0
  5. package/dist/chunk-65HNHRJK.cjs.map +1 -0
  6. package/dist/chunk-7KYDLL3B.js +480 -0
  7. package/dist/chunk-7KYDLL3B.js.map +1 -0
  8. package/dist/chunk-A6FLEJ7R.cjs +62 -0
  9. package/dist/chunk-A6FLEJ7R.cjs.map +1 -0
  10. package/dist/chunk-CUJK7ZTS.js +217 -0
  11. package/dist/chunk-CUJK7ZTS.js.map +1 -0
  12. package/dist/chunk-GI3BUPIH.cjs +236 -0
  13. package/dist/chunk-GI3BUPIH.cjs.map +1 -0
  14. package/dist/chunk-JXHV66Q4.js +106 -0
  15. package/dist/chunk-JXHV66Q4.js.map +1 -0
  16. package/dist/chunk-KNGZKGRS.cjs +552 -0
  17. package/dist/chunk-KNGZKGRS.cjs.map +1 -0
  18. package/dist/chunk-LELPCIE7.js +840 -0
  19. package/dist/chunk-LELPCIE7.js.map +1 -0
  20. package/dist/chunk-MCZG7QEM.cjs +310 -0
  21. package/dist/chunk-MCZG7QEM.cjs.map +1 -0
  22. package/dist/chunk-TCVKC227.js +56 -0
  23. package/dist/chunk-TCVKC227.js.map +1 -0
  24. package/dist/chunk-VXLUSU5B.cjs +856 -0
  25. package/dist/chunk-VXLUSU5B.cjs.map +1 -0
  26. package/dist/chunk-WCQVDF3K.js +12 -0
  27. package/dist/chunk-WCQVDF3K.js.map +1 -0
  28. package/dist/chunk-WGEGR3DF.cjs +15 -0
  29. package/dist/chunk-WGEGR3DF.cjs.map +1 -0
  30. package/dist/client-session-claim-3QF3noOr.d.ts +197 -0
  31. package/dist/client-session-claim-C4lUik3b.d.cts +197 -0
  32. package/dist/core-DMhuNfoz.d.cts +62 -0
  33. package/dist/core-DMhuNfoz.d.ts +62 -0
  34. package/dist/crypto/providers/noble-provider.cjs +14 -0
  35. package/dist/crypto/providers/noble-provider.cjs.map +1 -0
  36. package/dist/crypto/providers/noble-provider.d.cts +30 -0
  37. package/dist/crypto/providers/noble-provider.d.ts +30 -0
  38. package/dist/crypto/providers/noble-provider.js +5 -0
  39. package/dist/crypto/providers/noble-provider.js.map +1 -0
  40. package/dist/crypto/providers/node-provider.cjs +308 -0
  41. package/dist/crypto/providers/node-provider.cjs.map +1 -0
  42. package/dist/crypto/providers/node-provider.d.cts +32 -0
  43. package/dist/crypto/providers/node-provider.d.ts +32 -0
  44. package/dist/crypto/providers/node-provider.js +306 -0
  45. package/dist/crypto/providers/node-provider.js.map +1 -0
  46. package/dist/crypto/providers/quickcrypto-provider.cjs +339 -0
  47. package/dist/crypto/providers/quickcrypto-provider.cjs.map +1 -0
  48. package/dist/crypto/providers/quickcrypto-provider.d.cts +34 -0
  49. package/dist/crypto/providers/quickcrypto-provider.d.ts +34 -0
  50. package/dist/crypto/providers/quickcrypto-provider.js +337 -0
  51. package/dist/crypto/providers/quickcrypto-provider.js.map +1 -0
  52. package/dist/crypto/providers/webcrypto-provider.cjs +310 -0
  53. package/dist/crypto/providers/webcrypto-provider.cjs.map +1 -0
  54. package/dist/crypto/providers/webcrypto-provider.d.cts +30 -0
  55. package/dist/crypto/providers/webcrypto-provider.d.ts +30 -0
  56. package/dist/crypto/providers/webcrypto-provider.js +308 -0
  57. package/dist/crypto/providers/webcrypto-provider.js.map +1 -0
  58. package/dist/crypto-BUS06Qz-.d.cts +40 -0
  59. package/dist/crypto-BUS06Qz-.d.ts +40 -0
  60. package/dist/crypto-export.cjs +790 -0
  61. package/dist/crypto-export.cjs.map +1 -0
  62. package/dist/crypto-export.d.cts +257 -0
  63. package/dist/crypto-export.d.ts +257 -0
  64. package/dist/crypto-export.js +709 -0
  65. package/dist/crypto-export.js.map +1 -0
  66. package/dist/crypto-provider-deYoVIxi.d.cts +36 -0
  67. package/dist/crypto-provider-deYoVIxi.d.ts +36 -0
  68. package/dist/index.cjs +615 -0
  69. package/dist/index.cjs.map +1 -0
  70. package/dist/index.d.cts +379 -0
  71. package/dist/index.d.ts +379 -0
  72. package/dist/index.js +504 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/schemas-export.cjs +294 -0
  75. package/dist/schemas-export.cjs.map +1 -0
  76. package/dist/schemas-export.d.cts +1598 -0
  77. package/dist/schemas-export.d.ts +1598 -0
  78. package/dist/schemas-export.js +5 -0
  79. package/dist/schemas-export.js.map +1 -0
  80. package/dist/siwe-export.cjs +237 -0
  81. package/dist/siwe-export.cjs.map +1 -0
  82. package/dist/siwe-export.d.cts +27 -0
  83. package/dist/siwe-export.d.ts +27 -0
  84. package/dist/siwe-export.js +228 -0
  85. package/dist/siwe-export.js.map +1 -0
  86. package/dist/testing.cjs +54 -0
  87. package/dist/testing.cjs.map +1 -0
  88. package/dist/testing.d.cts +20 -0
  89. package/dist/testing.d.ts +20 -0
  90. package/dist/testing.js +51 -0
  91. package/dist/testing.js.map +1 -0
  92. package/dist/validation-export.cjs +359 -0
  93. package/dist/validation-export.cjs.map +1 -0
  94. package/dist/validation-export.d.cts +3 -0
  95. package/dist/validation-export.d.ts +3 -0
  96. package/dist/validation-export.js +6 -0
  97. package/dist/validation-export.js.map +1 -0
  98. package/dist/validators-export.cjs +73 -0
  99. package/dist/validators-export.cjs.map +1 -0
  100. package/dist/validators-export.d.cts +37 -0
  101. package/dist/validators-export.d.ts +37 -0
  102. package/dist/validators-export.js +4 -0
  103. package/dist/validators-export.js.map +1 -0
  104. package/package.json +140 -0
  105. package/src/constants/index.ts +205 -0
  106. package/src/crypto/context.ts +228 -0
  107. package/src/crypto/diagnostics.ts +772 -0
  108. package/src/crypto/errors.ts +114 -0
  109. package/src/crypto/index.ts +89 -0
  110. package/src/crypto/payload-handler.ts +102 -0
  111. package/src/crypto/providers/compliance-provider.ts +579 -0
  112. package/src/crypto/providers/factory.ts +204 -0
  113. package/src/crypto/providers/index.ts +44 -0
  114. package/src/crypto/providers/noble-provider.ts +392 -0
  115. package/src/crypto/providers/node-provider.ts +433 -0
  116. package/src/crypto/providers/quickcrypto-provider.ts +483 -0
  117. package/src/crypto/providers/registry.ts +129 -0
  118. package/src/crypto/providers/webcrypto-provider.ts +364 -0
  119. package/src/crypto/session-security.ts +185 -0
  120. package/src/crypto/types.ts +93 -0
  121. package/src/crypto/utils.ts +190 -0
  122. package/src/crypto-export.ts +21 -0
  123. package/src/index.ts +38 -0
  124. package/src/schemas/auth.ts +60 -0
  125. package/src/schemas/client-messages.ts +57 -0
  126. package/src/schemas/core.ts +144 -0
  127. package/src/schemas/crypto.ts +65 -0
  128. package/src/schemas/discovery.ts +79 -0
  129. package/src/schemas/index.ts +239 -0
  130. package/src/schemas/relay-messages.ts +45 -0
  131. package/src/schemas/wallet-messages.ts +177 -0
  132. package/src/schemas-export.ts +23 -0
  133. package/src/siwe-export.ts +27 -0
  134. package/src/testing.ts +71 -0
  135. package/src/types/auth.ts +60 -0
  136. package/src/types/client-messages.ts +84 -0
  137. package/src/types/core.ts +131 -0
  138. package/src/types/crypto-provider.ts +264 -0
  139. package/src/types/crypto.ts +90 -0
  140. package/src/types/discovery.ts +50 -0
  141. package/src/types/errors.ts +87 -0
  142. package/src/types/index.ts +197 -0
  143. package/src/types/post-auth-operations.ts +363 -0
  144. package/src/types/providers.ts +72 -0
  145. package/src/types/relay-messages.ts +60 -0
  146. package/src/types/request-lifecycle.ts +161 -0
  147. package/src/types/signing-operations.ts +99 -0
  148. package/src/types/wallet-messages.ts +251 -0
  149. package/src/utils/client-session-claim.ts +188 -0
  150. package/src/utils/index.ts +54 -0
  151. package/src/utils/public-keys.ts +49 -0
  152. package/src/utils/siwe.ts +362 -0
  153. package/src/utils/url-decoding.ts +126 -0
  154. package/src/utils/url-encoding.ts +144 -0
  155. package/src/utils/wallet-session-claim.ts +188 -0
  156. package/src/validation-export.ts +32 -0
  157. package/src/validators/index.ts +222 -0
  158. package/src/validators-export.ts +8 -0
@@ -0,0 +1,37 @@
1
+ declare function isValidAddress(address: unknown): address is string;
2
+ declare function isValidUrl(url: unknown): url is string;
3
+ declare function isValidTimestamp(timestamp: unknown): timestamp is string;
4
+ declare function isValidChainId(chainId: unknown): chainId is number;
5
+ declare function isValidNonce(nonce: unknown): nonce is string;
6
+ declare function isValidSessionId(sessionId: unknown): sessionId is string;
7
+ declare function isValidBase64(str: unknown): str is string;
8
+ declare function isValidHex(hex: unknown): hex is string;
9
+ interface ValidationResult<T = unknown> {
10
+ valid: boolean;
11
+ error?: string;
12
+ data?: T;
13
+ }
14
+ declare function isValidMessageType(type: unknown): type is 'auth' | 'tx' | 'sign';
15
+ declare function isValidEncryptionAlgorithm(algo: unknown): algo is 'AES-GCM' | 'plaintext';
16
+ declare function isValidDomain(domain: unknown): domain is string;
17
+ declare function isValidPublicKey(publicKey: unknown): publicKey is string;
18
+ interface EncryptedPayload {
19
+ iv: string;
20
+ ciphertext: string;
21
+ mac: string;
22
+ }
23
+ declare function isValidEncryptedPayload(payload: unknown): payload is EncryptedPayload;
24
+ declare function createValidationResult<T = unknown>(valid: boolean, data?: T, error?: string): ValidationResult<T>;
25
+ declare function hasRequiredFields<T extends object>(obj: unknown, fields: (keyof T)[]): obj is T;
26
+ interface SIWEMessage {
27
+ domain: string;
28
+ address: string;
29
+ uri: string;
30
+ version: string;
31
+ chainId: number;
32
+ nonce: string;
33
+ issuedAt: string;
34
+ }
35
+ declare function isValidSIWEMessage(message: unknown): message is SIWEMessage;
36
+
37
+ export { type EncryptedPayload, type SIWEMessage, type ValidationResult, createValidationResult, hasRequiredFields, isValidAddress, isValidBase64, isValidChainId, isValidDomain, isValidEncryptedPayload, isValidEncryptionAlgorithm, isValidHex, isValidMessageType, isValidNonce, isValidPublicKey, isValidSIWEMessage, isValidSessionId, isValidTimestamp, isValidUrl };
@@ -0,0 +1,37 @@
1
+ declare function isValidAddress(address: unknown): address is string;
2
+ declare function isValidUrl(url: unknown): url is string;
3
+ declare function isValidTimestamp(timestamp: unknown): timestamp is string;
4
+ declare function isValidChainId(chainId: unknown): chainId is number;
5
+ declare function isValidNonce(nonce: unknown): nonce is string;
6
+ declare function isValidSessionId(sessionId: unknown): sessionId is string;
7
+ declare function isValidBase64(str: unknown): str is string;
8
+ declare function isValidHex(hex: unknown): hex is string;
9
+ interface ValidationResult<T = unknown> {
10
+ valid: boolean;
11
+ error?: string;
12
+ data?: T;
13
+ }
14
+ declare function isValidMessageType(type: unknown): type is 'auth' | 'tx' | 'sign';
15
+ declare function isValidEncryptionAlgorithm(algo: unknown): algo is 'AES-GCM' | 'plaintext';
16
+ declare function isValidDomain(domain: unknown): domain is string;
17
+ declare function isValidPublicKey(publicKey: unknown): publicKey is string;
18
+ interface EncryptedPayload {
19
+ iv: string;
20
+ ciphertext: string;
21
+ mac: string;
22
+ }
23
+ declare function isValidEncryptedPayload(payload: unknown): payload is EncryptedPayload;
24
+ declare function createValidationResult<T = unknown>(valid: boolean, data?: T, error?: string): ValidationResult<T>;
25
+ declare function hasRequiredFields<T extends object>(obj: unknown, fields: (keyof T)[]): obj is T;
26
+ interface SIWEMessage {
27
+ domain: string;
28
+ address: string;
29
+ uri: string;
30
+ version: string;
31
+ chainId: number;
32
+ nonce: string;
33
+ issuedAt: string;
34
+ }
35
+ declare function isValidSIWEMessage(message: unknown): message is SIWEMessage;
36
+
37
+ export { type EncryptedPayload, type SIWEMessage, type ValidationResult, createValidationResult, hasRequiredFields, isValidAddress, isValidBase64, isValidChainId, isValidDomain, isValidEncryptedPayload, isValidEncryptionAlgorithm, isValidHex, isValidMessageType, isValidNonce, isValidPublicKey, isValidSIWEMessage, isValidSessionId, isValidTimestamp, isValidUrl };
@@ -0,0 +1,4 @@
1
+ export { createValidationResult, hasRequiredFields, isValidAddress, isValidBase64, isValidChainId, isValidDomain, isValidEncryptedPayload, isValidEncryptionAlgorithm, isValidHex, isValidMessageType, isValidNonce, isValidPublicKey, isValidSIWEMessage, isValidSessionId, isValidTimestamp, isValidUrl } from './chunk-JXHV66Q4.js';
2
+ import './chunk-WCQVDF3K.js';
3
+ //# sourceMappingURL=validators-export.js.map
4
+ //# sourceMappingURL=validators-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"validators-export.js"}
package/package.json ADDED
@@ -0,0 +1,140 @@
1
+ {
2
+ "name": "@bananalink-sdk/protocol",
3
+ "version": "1.2.7",
4
+ "description": "Core protocol definitions for BananaLink. Provides TypeScript types, Zod validation schemas, cryptographic utilities, and shared constants for wallet connections.",
5
+ "type": "module",
6
+ "keywords": [
7
+ "bananalink",
8
+ "protocol",
9
+ "types",
10
+ "schemas",
11
+ "validation",
12
+ "cryptography",
13
+ "web3"
14
+ ],
15
+ "bugs": {
16
+ "url": "https://github.com/bananalink/bananalink/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/bananalink/bananalink.git",
21
+ "directory": "packages/protocol"
22
+ },
23
+ "license": "ISC",
24
+ "author": "BananaLink Team <team@banana.link>",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "react-native": "./dist/index.js",
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs"
31
+ },
32
+ "./schemas": {
33
+ "types": "./dist/schemas-export.d.ts",
34
+ "react-native": "./dist/schemas-export.js",
35
+ "import": "./dist/schemas-export.js",
36
+ "require": "./dist/schemas-export.cjs"
37
+ },
38
+ "./validators": {
39
+ "types": "./dist/validators-export.d.ts",
40
+ "react-native": "./dist/validators-export.js",
41
+ "import": "./dist/validators-export.js",
42
+ "require": "./dist/validators-export.cjs"
43
+ },
44
+ "./validation": {
45
+ "types": "./dist/validation-export.d.ts",
46
+ "react-native": "./dist/validation-export.js",
47
+ "import": "./dist/validation-export.js",
48
+ "require": "./dist/validation-export.cjs"
49
+ },
50
+ "./siwe": {
51
+ "types": "./dist/siwe-export.d.ts",
52
+ "react-native": "./dist/siwe-export.js",
53
+ "import": "./dist/siwe-export.js",
54
+ "require": "./dist/siwe-export.cjs"
55
+ },
56
+ "./crypto": {
57
+ "types": "./dist/crypto-export.d.ts",
58
+ "react-native": "./dist/crypto-export.js",
59
+ "import": "./dist/crypto-export.js",
60
+ "require": "./dist/crypto-export.cjs"
61
+ },
62
+ "./crypto/provider/webcrypto": {
63
+ "types": "./dist/crypto/providers/webcrypto-provider.d.ts",
64
+ "react-native": "./dist/crypto/providers/webcrypto-provider.js",
65
+ "import": "./dist/crypto/providers/webcrypto-provider.js",
66
+ "require": "./dist/crypto/providers/webcrypto-provider.cjs"
67
+ },
68
+ "./crypto/provider/node": {
69
+ "types": "./dist/crypto/providers/node-provider.d.ts",
70
+ "react-native": "./dist/crypto/providers/node-provider.js",
71
+ "import": "./dist/crypto/providers/node-provider.js",
72
+ "require": "./dist/crypto/providers/node-provider.cjs"
73
+ },
74
+ "./crypto/provider/noble": {
75
+ "types": "./dist/crypto/providers/noble-provider.d.ts",
76
+ "react-native": "./dist/crypto/providers/noble-provider.js",
77
+ "import": "./dist/crypto/providers/noble-provider.js",
78
+ "require": "./dist/crypto/providers/noble-provider.cjs"
79
+ },
80
+ "./crypto/provider/quickcrypto": {
81
+ "types": "./dist/crypto/providers/quickcrypto-provider.d.ts",
82
+ "react-native": "./dist/crypto/providers/quickcrypto-provider.js",
83
+ "import": "./dist/crypto/providers/quickcrypto-provider.js",
84
+ "require": "./dist/crypto/providers/quickcrypto-provider.cjs"
85
+ },
86
+ "./testing": {
87
+ "types": "./dist/testing.d.ts",
88
+ "react-native": "./dist/testing.js",
89
+ "import": "./dist/testing.js",
90
+ "require": "./dist/testing.cjs"
91
+ }
92
+ },
93
+ "main": "./dist/index.cjs",
94
+ "module": "./dist/index.js",
95
+ "types": "./dist/index.d.ts",
96
+ "files": [
97
+ "dist",
98
+ "src",
99
+ "!src/__tests__"
100
+ ],
101
+ "publishConfig": {
102
+ "access": "restricted"
103
+ },
104
+ "scripts": {
105
+ "build": "tsup",
106
+ "clean": "rm -rf dist node_modules",
107
+ "dev": "tsup --watch",
108
+ "lint": "eslint src --ext .ts",
109
+ "test": "vitest run",
110
+ "test:watch": "vitest",
111
+ "test:coverage": "vitest run --coverage",
112
+ "typecheck": "tsc --noEmit"
113
+ },
114
+ "dependencies": {
115
+ "@bananalink-sdk/logger": "workspace:*",
116
+ "@noble/ciphers": "^2.0.0",
117
+ "@noble/curves": "^2.0.0",
118
+ "@noble/hashes": "^2.0.0",
119
+ "zod": "^3.25.67"
120
+ },
121
+ "devDependencies": {
122
+ "@bananalink-sdk/eslint-config": "workspace:*",
123
+ "@bananalink-sdk/typescript-config": "workspace:*",
124
+ "@types/node": "^24.1.0",
125
+ "@vitest/coverage-v8": "^3.2.4",
126
+ "eslint": "^9.29.0",
127
+ "tsup": "^8.5.0",
128
+ "typescript": "^5.8.3",
129
+ "vitest": "^3.2.4"
130
+ },
131
+ "peerDependencies": {
132
+ "react-native-quick-crypto": "^0.7.0"
133
+ },
134
+ "peerDependenciesMeta": {
135
+ "react-native-quick-crypto": {
136
+ "optional": true
137
+ }
138
+ },
139
+ "packageManager": "pnpm@10.15.1"
140
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Constants for the BananaLink protocol
3
+ * Contains protocol version, URLs, timeouts, and other configuration values
4
+ */
5
+
6
+ // Protocol version and identification
7
+ export const PROTOCOL_VERSION = 1;
8
+ export const PACKAGE_NAME = '@bananalink-sdk/protocol';
9
+ export const DEEPLINK_SCHEME = 'bananalink';
10
+
11
+ // Default relay server configuration
12
+ export const DEFAULT_RELAY_URL = 'wss://relay.banana.link/v1';
13
+
14
+ // FrutiLink emoji set (16 fruits for 4 bits each)
15
+ export const FRUITS = [
16
+ '🍌', '🍎', '🍊', '🍇', '🍓', '🍑', '🍒', '🍉',
17
+ '🥝', '🍍', '🥭', '🥥', '🫐', '🍈', '🍋', '🥑'
18
+ ] as const;
19
+
20
+ // Timeout configurations (in milliseconds)
21
+ export const DEFAULT_TIMEOUTS = {
22
+ sessionTimeout: 24 * 60 * 60 * 1000, // 24 hours
23
+ requestTimeout: 5 * 60 * 1000, // 5 minutes
24
+ connectionTimeout: 30 * 1000, // 30 seconds
25
+ qrCodeTimeout: 2 * 60 * 1000, // 2 minutes
26
+ } as const;
27
+
28
+ // Protocol v2.0 specific timeouts (in milliseconds)
29
+ // Based on PROTOCOL.md specification
30
+ export const PROTOCOL_V2_TIMEOUTS = {
31
+ sessionCreation: 5 * 1000, // 5 seconds - API Gateway response
32
+ claimSession: 10 * 1000, // 10 seconds - Wallet claims ownership
33
+ prefetchMetadata: 5 * 1000, // 5 seconds - Fetch dApp info
34
+ userApproval: 5 * 60 * 1000, // 5 minutes - User decision time
35
+ authentication: 30 * 1000, // 30 seconds - Sign and send
36
+ sessionTTL: 10800 * 1000, // 10800 seconds (3 hours) - Max session lifetime
37
+ reconnectionWindow: 10800 * 1000, // 10800 seconds (3 hours) - Same as session TTL
38
+ messageQueueRetention: 10800 * 1000, // 10800 seconds (3 hours) - Matches session TTL
39
+ websocketHeartbeat: 30 * 1000, // 30 seconds - Keep-alive ping
40
+ gracefulCloseTimeout: 5 * 1000, // 5 seconds - close_session ack wait
41
+ // Post-authentication operation timeouts (v2.1+)
42
+ signMessage: 60 * 1000, // 60 seconds - personal_sign operation
43
+ signTypedData: 90 * 1000, // 90 seconds - eth_signTypedData_v4 operation
44
+ signTransaction: 120 * 1000, // 120 seconds - Transaction signing
45
+ } as const;
46
+
47
+ // Message queue configuration
48
+ export const MESSAGE_QUEUE_CONFIG = {
49
+ maxQueueSize: 100, // Maximum messages per direction
50
+ retentionTime: 10800 * 1000, // 10800 seconds (3 hours) - Matches session TTL
51
+ overflowStrategy: 'drop_oldest', // Drop oldest messages when queue is full
52
+ } as const;
53
+
54
+ // Security limits and constraints
55
+ export const SECURITY_LIMITS = {
56
+ maxConcurrentSessions: 5,
57
+ maxSessionAge: 7 * 24 * 60 * 60 * 1000, // 7 days
58
+ minNonceLength: 8,
59
+ maxMessageSize: 64 * 1024, // 64KB
60
+ maxResourcesCount: 10,
61
+ rateLimit: {
62
+ requestsPerMinute: 60,
63
+ sessionsPerHour: 100,
64
+ },
65
+ } as const;
66
+
67
+ // Cryptographic constants
68
+ export const CRYPTO_CONSTANTS = {
69
+ keyLength: 256, // AES-256
70
+ ivLength: 12, // GCM IV length
71
+ tagLength: 16, // GCM tag length
72
+ curve: 'P-256', // ECDH curve
73
+ algorithm: 'AES-GCM', // Encryption algorithm
74
+ hashAlgorithm: 'SHA-256', // Hash algorithm
75
+ } as const;
76
+
77
+ // Message format constants
78
+ export const MESSAGE_FORMAT = {
79
+ version: '1',
80
+ encoding: 'utf8',
81
+ lineBreak: '\n',
82
+ resourcePrefix: '- ',
83
+ } as const;
84
+
85
+ // WebSocket message types
86
+ export const WS_MESSAGE_TYPES = {
87
+ subscribe: 'sub',
88
+ publish: 'pub',
89
+ acknowledge: 'ack',
90
+ error: 'error',
91
+ } as const;
92
+
93
+ // HTTP endpoints for fallback
94
+ export const HTTP_ENDPOINTS = {
95
+ messages: '/v1/messages',
96
+ session: '/v1/session',
97
+ health: '/v1/health',
98
+ } as const;
99
+
100
+ // Error messages (organized hierarchically)
101
+ export const ERROR_MESSAGES = {
102
+ // Session errors
103
+ SESSION_EXPIRED: 'Session has expired',
104
+ SESSION_NOT_FOUND: 'Session ID invalid or expired',
105
+ SESSION_ALREADY_CLAIMED: 'Session already claimed by another wallet',
106
+ INVALID_SESSION_CLAIM: 'Wallet does not own this session',
107
+ INVALID_CLIENT_CLAIM: 'Client session claim does not match',
108
+ SESSION_TOKEN_EXPIRED: 'Session token TTL exceeded',
109
+ SESSION_CLOSED: 'Session has been closed',
110
+
111
+ // Encryption errors
112
+ ENCRYPTION_FAILED: 'Message encryption failed',
113
+ DECRYPTION_FAILED: 'Cannot decrypt message',
114
+ INVALID_SIGNATURE: 'Invalid signature',
115
+
116
+ // Network errors
117
+ NETWORK_ERROR: 'Network connection error',
118
+ RELAY_ERROR: 'Internal relay server error',
119
+ MESSAGE_QUEUE_FULL: 'Too many queued messages (>100)',
120
+ RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
121
+ ORIGIN_MISMATCH: 'Origin verification failed',
122
+
123
+ // Request lifecycle errors (v2.1+)
124
+ REQUEST_PENDING: 'Another signing request is already pending',
125
+ REQUEST_NOT_FOUND: 'Request ID not found or already processed',
126
+ REQUEST_EXPIRED: 'Request expired before completion',
127
+ REQUEST_INVALID: 'Request data is invalid or malformed',
128
+ REQUEST_TIMEOUT: 'Request timed out waiting for wallet response',
129
+
130
+ // Operation-specific errors (v2.1+)
131
+ SIGNING_FAILED: 'Cryptographic signing operation failed',
132
+ SIGNING_REJECTED: 'User rejected the signing request',
133
+ TRANSACTION_INVALID: 'Transaction parameters validation failed',
134
+ CHAIN_MISMATCH: 'Requested chain ID does not match wallet chain',
135
+ } as const;
136
+
137
+ // QR code configuration
138
+ export const QR_CONFIG = {
139
+ size: 256,
140
+ margin: 4,
141
+ errorCorrectionLevel: 'M',
142
+ type: 'image/png',
143
+ } as const;
144
+
145
+ // Deep link configuration
146
+ export const DEEP_LINK_CONFIG = {
147
+ scheme: DEEPLINK_SCHEME,
148
+ host: 'connect',
149
+ universal: 'https://bananalink.app',
150
+ } as const;
151
+
152
+ // Network configuration
153
+ export const NETWORK_CONFIG = {
154
+ SUPPORTED_CHAINS: [
155
+ 1, // Ethereum Mainnet
156
+ 5, // Goerli Testnet
157
+ 137, // Polygon Mainnet
158
+ 80001, // Polygon Mumbai Testnet
159
+ 42161, // Arbitrum One
160
+ 421613, // Arbitrum Goerli
161
+ 10, // Optimism
162
+ 420, // Optimism Goerli
163
+ ],
164
+ DEFAULT_CHAIN_ID: 1,
165
+ CHAIN_NAMES: {
166
+ 1: 'Ethereum Mainnet',
167
+ 5: 'Goerli Testnet',
168
+ 137: 'Polygon Mainnet',
169
+ 80001: 'Polygon Mumbai',
170
+ 42161: 'Arbitrum One',
171
+ 421613: 'Arbitrum Goerli',
172
+ 10: 'Optimism',
173
+ 420: 'Optimism Goerli',
174
+ } as const,
175
+ } as const;
176
+
177
+ // Development configuration
178
+ export const DEV_CONFIG = {
179
+ logLevel: 'debug',
180
+ enableMocks: false,
181
+ skipValidation: false,
182
+ allowLocalhost: true,
183
+ } as const;
184
+
185
+ // Export all constants as a single object for convenience
186
+ export const BANANALINK_CONSTANTS = {
187
+ PROTOCOL_VERSION,
188
+ PACKAGE_NAME,
189
+ DEEPLINK_SCHEME,
190
+ DEFAULT_RELAY_URL,
191
+ FRUITS,
192
+ DEFAULT_TIMEOUTS,
193
+ PROTOCOL_V2_TIMEOUTS,
194
+ MESSAGE_QUEUE_CONFIG,
195
+ SECURITY_LIMITS,
196
+ CRYPTO_CONSTANTS,
197
+ MESSAGE_FORMAT,
198
+ WS_MESSAGE_TYPES,
199
+ HTTP_ENDPOINTS,
200
+ ERROR_MESSAGES,
201
+ QR_CONFIG,
202
+ DEEP_LINK_CONFIG,
203
+ NETWORK_CONFIG,
204
+ DEV_CONFIG,
205
+ } as const;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * CryptoContext - Singleton manager for the active crypto provider
3
+ *
4
+ * Provides a global context for crypto operations while maintaining
5
+ * the ability to use custom providers when needed.
6
+ *
7
+ * @example
8
+ * // Automatic initialization with best available provider
9
+ * const bytes = randomBytes(32);
10
+ *
11
+ * @example
12
+ * // Explicit provider configuration
13
+ * CryptoContext.initialize({ type: 'noble' });
14
+ * const bytes = randomBytes(32);
15
+ *
16
+ * @example
17
+ * // Custom provider instance
18
+ * const provider = new NobleCryptoProvider();
19
+ * CryptoContext.setProvider(provider);
20
+ */
21
+
22
+ import type { Logger } from '@bananalink-sdk/logger';
23
+ import type { CryptoProvider, CryptoProviderType } from '../types/crypto-provider';
24
+ import { createCryptoProvider, getRegisteredCryptoProviders } from './providers';
25
+ import { ComplianceCryptoProvider } from './providers/compliance-provider';
26
+ import type { ComplianceConfig } from './providers/compliance-provider';
27
+ import { CryptoProviderUnavailableError } from './errors';
28
+ import { detectPlatform } from './utils';
29
+
30
+ export interface CryptoContextConfig {
31
+ /** Provider type to use (auto-detects if not specified) */
32
+ type?: CryptoProviderType;
33
+ /** Strict mode - throw if preferred provider unavailable (default: false) */
34
+ strict?: boolean;
35
+ /** Compliance configuration (wraps provider if enabled) */
36
+ compliance?: ComplianceConfig;
37
+ /** Logger instance for debug output */
38
+ logger?: Logger;
39
+ }
40
+
41
+ /**
42
+ * Singleton context for managing the active crypto provider
43
+ *
44
+ * Design rationale:
45
+ * - Provides convenient global access to crypto operations
46
+ * - Maintains lazy initialization for tree-shaking
47
+ * - Allows explicit provider configuration when needed
48
+ * - Supports both automatic and manual provider selection
49
+ */
50
+ class CryptoContextManager {
51
+ private provider: CryptoProvider | null = null;
52
+ private config: CryptoContextConfig | null = null;
53
+ private initialized = false;
54
+ private initializing = false;
55
+
56
+ /**
57
+ * Initialize the crypto context with specific configuration
58
+ *
59
+ * @param config - Provider configuration options
60
+ * @throws Error if already initialized (call reset() first)
61
+ * @throws CryptoProviderUnavailableError if strict mode enabled and provider unavailable
62
+ */
63
+ initialize(config: CryptoContextConfig = {}): void {
64
+ if (this.initialized && this.provider) {
65
+ throw new Error(
66
+ 'CryptoContext already initialized. Call CryptoContext.reset() first to reconfigure.'
67
+ );
68
+ }
69
+
70
+ // Strict mode validation - check if provider is registered
71
+ if (config.strict && config.type) {
72
+ const availableProviders = getRegisteredCryptoProviders();
73
+ if (!availableProviders.includes(config.type)) {
74
+ const platform = detectPlatform();
75
+ const recommendations: string[] = [
76
+ `Import the provider: import '@bananalink-sdk/protocol/crypto/provider/${config.type}'`,
77
+ 'Ensure the provider package is installed',
78
+ ];
79
+
80
+ // Add platform-specific recommendations
81
+ if (config.type === 'quickcrypto' && !platform.isReactNative) {
82
+ recommendations.push('QuickCrypto requires React Native environment');
83
+ if (platform.isNode) {
84
+ recommendations.push("Try 'node' provider for Node.js");
85
+ } else if (platform.isBrowser) {
86
+ recommendations.push("Try 'webcrypto' provider for browsers");
87
+ }
88
+ }
89
+
90
+ // Add universal fallback recommendation
91
+ if (availableProviders.includes('noble')) {
92
+ recommendations.push("'noble' provider works in all environments as a fallback");
93
+ }
94
+
95
+ throw new CryptoProviderUnavailableError(
96
+ `Strict mode: Crypto provider '${config.type}' is not registered. Did you forget to import it?`,
97
+ {
98
+ requestedProvider: config.type,
99
+ availableProviders,
100
+ platform,
101
+ recommendations,
102
+ }
103
+ );
104
+ }
105
+ }
106
+
107
+ this.config = config;
108
+ this.provider = this.createProvider(config);
109
+ this.initialized = true;
110
+ }
111
+
112
+ /**
113
+ * Get the active crypto provider
114
+ * Automatically initializes with default config if not already initialized
115
+ *
116
+ * @returns The active crypto provider instance
117
+ * @throws Error if called concurrently during initialization
118
+ *
119
+ * @remarks
120
+ * This method performs lazy initialization on first call. It is NOT safe to call
121
+ * concurrently from multiple contexts before initialization completes (e.g., from
122
+ * Worker threads or Promise.all). For concurrent access scenarios, explicitly call
123
+ * initialize() first to ensure the provider is ready before concurrent operations begin.
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * // Safe: Single-threaded lazy initialization
128
+ * const provider = CryptoContext.getProvider();
129
+ *
130
+ * // Safe: Pre-initialize for concurrent access
131
+ * CryptoContext.initialize({ type: 'noble' });
132
+ * await Promise.all([
133
+ * operation1(CryptoContext.getProvider()),
134
+ * operation2(CryptoContext.getProvider())
135
+ * ]);
136
+ *
137
+ * // Unsafe: Concurrent calls during lazy initialization
138
+ * await Promise.all([
139
+ * operation1(CryptoContext.getProvider()), // May throw if not initialized yet
140
+ * operation2(CryptoContext.getProvider())
141
+ * ]);
142
+ * ```
143
+ */
144
+ getProvider(): CryptoProvider {
145
+ // Check if already initialized
146
+ if (this.provider) {
147
+ return this.provider;
148
+ }
149
+
150
+ // Prevent race condition - if already initializing, throw error
151
+ if (this.initializing) {
152
+ throw new Error(
153
+ 'CryptoContext initialization in progress. Avoid concurrent calls to getProvider() during initialization.'
154
+ );
155
+ }
156
+
157
+ // Lazy initialization with defaults
158
+ this.initializing = true;
159
+ try {
160
+ this.initialize();
161
+ // After initialize(), provider is guaranteed to be set
162
+ if (!this.provider) {
163
+ throw new Error('CryptoContext initialization failed: provider is null');
164
+ }
165
+ return this.provider;
166
+ } finally {
167
+ this.initializing = false;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Set a custom provider instance
173
+ * Useful for testing or advanced use cases
174
+ *
175
+ * @param provider - Custom provider instance
176
+ */
177
+ setProvider(provider: CryptoProvider): void {
178
+ this.provider = provider;
179
+ this.initialized = true;
180
+ }
181
+
182
+ /**
183
+ * Check if context has been explicitly initialized
184
+ */
185
+ isInitialized(): boolean {
186
+ return this.initialized;
187
+ }
188
+
189
+ /**
190
+ * Get current configuration
191
+ */
192
+ getConfig(): CryptoContextConfig | null {
193
+ return this.config;
194
+ }
195
+
196
+ /**
197
+ * Reset the context (for testing or reconfiguration)
198
+ */
199
+ reset(): void {
200
+ this.provider = null;
201
+ this.config = null;
202
+ this.initialized = false;
203
+ this.initializing = false;
204
+ }
205
+
206
+ /**
207
+ * Create a provider instance from configuration
208
+ */
209
+ private createProvider(config: CryptoContextConfig): CryptoProvider {
210
+ const baseProvider = createCryptoProvider(config.type, config.logger, config.strict);
211
+
212
+ if (config.compliance?.enabled) {
213
+ return new ComplianceCryptoProvider(baseProvider, config.compliance);
214
+ }
215
+
216
+ return baseProvider;
217
+ }
218
+
219
+ }
220
+
221
+ /**
222
+ * Global singleton instance
223
+ *
224
+ * Usage:
225
+ * - Most code should use utility functions (randomBytes, generateNonce)
226
+ * - Advanced use cases can access CryptoContext directly
227
+ */
228
+ export const CryptoContext = new CryptoContextManager();