@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,772 @@
1
+ /**
2
+ * Crypto Provider Diagnostics
3
+ *
4
+ * Utilities for testing, benchmarking, and diagnosing crypto provider issues.
5
+ * These tools help developers understand provider availability and performance.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // Test if a provider actually works
10
+ * const result = await testProvider('webcrypto');
11
+ * if (result.success) {
12
+ * console.log('WebCrypto is working!');
13
+ * } else {
14
+ * console.error('WebCrypto failed:', result.error);
15
+ * }
16
+ *
17
+ * // Get detailed environment diagnostics
18
+ * const diag = await diagnoseEnvironment();
19
+ * console.log(diag.toMarkdown());
20
+ *
21
+ * // Benchmark providers
22
+ * const bench = await benchmarkProvider('webcrypto');
23
+ * console.log(`ECDH: ${bench.ecdh.avgMs}ms`);
24
+ * ```
25
+ */
26
+
27
+ import type { Logger } from '@bananalink-sdk/logger';
28
+ import type {
29
+ CryptoProvider,
30
+ CryptoProviderType,
31
+ PlatformDetectionResult,
32
+ } from '../types/crypto-provider';
33
+ import { getRegisteredCryptoProviders } from './providers';
34
+ import { detectPlatform } from './utils';
35
+
36
+ /**
37
+ * Result of testing a specific crypto operation
38
+ */
39
+ export interface OperationTestResult {
40
+ /** Operation name */
41
+ operation: string;
42
+ /** Whether the operation succeeded */
43
+ success: boolean;
44
+ /** Execution time in milliseconds */
45
+ durationMs?: number;
46
+ /** Error message if failed */
47
+ error?: string;
48
+ /** Additional details */
49
+ details?: Record<string, unknown>;
50
+ }
51
+
52
+ /**
53
+ * Result of testing a crypto provider
54
+ */
55
+ export interface ProviderTestResult {
56
+ /** Provider type tested */
57
+ provider: CryptoProviderType;
58
+ /** Whether the provider is available */
59
+ available: boolean;
60
+ /** Overall success (all operations passed) */
61
+ success: boolean;
62
+ /** Individual operation test results */
63
+ operations: OperationTestResult[];
64
+ /** Total test duration in milliseconds */
65
+ totalDurationMs: number;
66
+ /** Error message if provider not available */
67
+ unavailableReason?: string;
68
+ }
69
+
70
+ /**
71
+ * Performance benchmark result for a provider
72
+ */
73
+ export interface ProviderBenchmark {
74
+ /** Provider type tested */
75
+ provider: CryptoProviderType;
76
+ /** ECDH key generation benchmark */
77
+ ecdh: {
78
+ /** Average time in milliseconds */
79
+ avgMs: number;
80
+ /** Minimum time in milliseconds */
81
+ minMs: number;
82
+ /** Maximum time in milliseconds */
83
+ maxMs: number;
84
+ /** Number of iterations */
85
+ iterations: number;
86
+ };
87
+ /** Random bytes generation benchmark */
88
+ randomBytes: {
89
+ /** Average time in milliseconds */
90
+ avgMs: number;
91
+ /** Minimum time in milliseconds */
92
+ minMs: number;
93
+ /** Maximum time in milliseconds */
94
+ maxMs: number;
95
+ /** Number of iterations */
96
+ iterations: number;
97
+ };
98
+ /** AES-GCM encryption benchmark */
99
+ aesGcm: {
100
+ /** Average time in milliseconds */
101
+ avgMs: number;
102
+ /** Minimum time in milliseconds */
103
+ minMs: number;
104
+ /** Maximum time in milliseconds */
105
+ maxMs: number;
106
+ /** Number of iterations */
107
+ iterations: number;
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Provider detection summary
113
+ */
114
+ export interface ProviderDetection {
115
+ /** WebCrypto provider available */
116
+ webCrypto: boolean;
117
+ /** Node.js crypto provider available */
118
+ node: boolean;
119
+ /** Noble provider available (pure JS fallback) */
120
+ noble: boolean;
121
+ /** QuickCrypto provider available (React Native) */
122
+ quickCrypto: boolean;
123
+ /** Recommended provider for this environment */
124
+ recommended?: CryptoProviderType;
125
+ /** Detailed capability information */
126
+ capabilities: Record<CryptoProviderType, {
127
+ available: boolean;
128
+ tested: boolean;
129
+ success: boolean;
130
+ }>;
131
+ /** Provider-specific recommendations */
132
+ recommendations: string[];
133
+ }
134
+
135
+ /**
136
+ * Comprehensive environment diagnostics
137
+ */
138
+ export interface EnvironmentDiagnostics {
139
+ /** Timestamp of diagnostics */
140
+ timestamp: string;
141
+ /** Platform detection information */
142
+ platform: PlatformDetectionResult;
143
+ /** Provider detection summary */
144
+ detection: ProviderDetection;
145
+ /** List of registered providers */
146
+ registeredProviders: CryptoProviderType[];
147
+ /** Provider test results */
148
+ providerTests: ProviderTestResult[];
149
+ /** Recommended provider (first successful test) */
150
+ recommendedProvider?: CryptoProviderType;
151
+ /** Warnings and recommendations */
152
+ warnings: string[];
153
+ /** Platform-specific recommendations */
154
+ recommendations: string[];
155
+ /** Convert to human-readable markdown */
156
+ toMarkdown(): string;
157
+ /** Convert to JSON string */
158
+ toJSON(): string;
159
+ }
160
+
161
+ /**
162
+ * Test a specific crypto provider with real operations
163
+ *
164
+ * @param providerType - The provider type to test
165
+ * @param logger - Optional logger for debug output
166
+ * @returns Test result with success status and operation details
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * const result = await testProvider('webcrypto');
171
+ * if (!result.success) {
172
+ * console.error('Provider test failed:', result);
173
+ * result.operations.forEach(op => {
174
+ * if (!op.success) {
175
+ * console.error(` ${op.operation}: ${op.error}`);
176
+ * }
177
+ * });
178
+ * }
179
+ * ```
180
+ */
181
+ export async function testProvider(
182
+ providerType: CryptoProviderType,
183
+ logger?: Logger
184
+ ): Promise<ProviderTestResult> {
185
+ const startTime = performance.now();
186
+ const operations: OperationTestResult[] = [];
187
+
188
+ let provider: CryptoProvider;
189
+ try {
190
+ // Create the specific provider without fallback to test it directly
191
+ switch (providerType) {
192
+ case 'webcrypto':
193
+ provider = new (await import('./providers/webcrypto-provider')).WebCryptoProvider();
194
+ break;
195
+ case 'noble':
196
+ provider = new (await import('./providers/noble-provider')).NobleCryptoProvider(logger);
197
+ break;
198
+ case 'node':
199
+ provider = new (await import('./providers/node-provider')).NodeCryptoProvider(logger);
200
+ break;
201
+ case 'quickcrypto':
202
+ provider = new (await import('./providers/quickcrypto-provider')).QuickCryptoProvider(logger);
203
+ break;
204
+ default:
205
+ throw new Error(`Unknown provider type: ${String(providerType)}`);
206
+ }
207
+
208
+ if (!provider.isAvailable) {
209
+ return {
210
+ provider: providerType,
211
+ available: false,
212
+ success: false,
213
+ operations: [],
214
+ totalDurationMs: performance.now() - startTime,
215
+ unavailableReason: `Provider ${providerType} not available in this environment`,
216
+ };
217
+ }
218
+ } catch (error) {
219
+ return {
220
+ provider: providerType,
221
+ available: false,
222
+ success: false,
223
+ operations: [],
224
+ totalDurationMs: performance.now() - startTime,
225
+ unavailableReason: error instanceof Error ? error.message : String(error),
226
+ };
227
+ }
228
+
229
+ // Test 1: Random bytes generation
230
+ try {
231
+ const opStart = performance.now();
232
+ const bytes = provider.randomBytes(32);
233
+ const duration = performance.now() - opStart;
234
+
235
+ operations.push({
236
+ operation: 'randomBytes',
237
+ success: true,
238
+ durationMs: duration,
239
+ details: {
240
+ bytesLength: bytes.byteLength,
241
+ },
242
+ });
243
+ } catch (error) {
244
+ operations.push({
245
+ operation: 'randomBytes',
246
+ success: false,
247
+ error: error instanceof Error ? error.message : String(error),
248
+ });
249
+ }
250
+
251
+ // Test 2: ECDH key pair generation
252
+ try {
253
+ const opStart = performance.now();
254
+ const keyPair = await provider.generateKeyPair();
255
+ const duration = performance.now() - opStart;
256
+
257
+ operations.push({
258
+ operation: 'generateKeyPair',
259
+ success: true,
260
+ durationMs: duration,
261
+ details: {
262
+ publicKeyType: keyPair.publicKey.type,
263
+ privateKeyType: keyPair.privateKey.type,
264
+ },
265
+ });
266
+ } catch (error) {
267
+ operations.push({
268
+ operation: 'generateKeyPair',
269
+ success: false,
270
+ error: error instanceof Error ? error.message : String(error),
271
+ });
272
+ }
273
+
274
+ // Test 3: Key export/import
275
+ try {
276
+ const opStart = performance.now();
277
+ const keyPair = await provider.generateKeyPair();
278
+ const publicKeyData = await provider.exportPublicKey(keyPair.publicKey);
279
+ const importedPublicKey = await provider.importPublicKey(publicKeyData);
280
+ const duration = performance.now() - opStart;
281
+
282
+ operations.push({
283
+ operation: 'exportImportKey',
284
+ success: true,
285
+ durationMs: duration,
286
+ details: {
287
+ exportedKeySize: publicKeyData.byteLength,
288
+ importedKeyType: importedPublicKey.type,
289
+ },
290
+ });
291
+ } catch (error) {
292
+ operations.push({
293
+ operation: 'exportImportKey',
294
+ success: false,
295
+ error: error instanceof Error ? error.message : String(error),
296
+ });
297
+ }
298
+
299
+ // Test 4: ECDH key agreement
300
+ try {
301
+ const opStart = performance.now();
302
+ const keyPair1 = await provider.generateKeyPair();
303
+ const keyPair2 = await provider.generateKeyPair();
304
+ // Derive shared secrets from both sides (just verify they can be derived)
305
+ await provider.deriveSharedSecret(keyPair1.privateKey, keyPair2.publicKey);
306
+ await provider.deriveSharedSecret(keyPair2.privateKey, keyPair1.publicKey);
307
+ const duration = performance.now() - opStart;
308
+
309
+ // Both shared secrets were derived successfully
310
+ operations.push({
311
+ operation: 'deriveSharedSecret',
312
+ success: true,
313
+ durationMs: duration,
314
+ details: {
315
+ sharedSecretsGenerated: 2,
316
+ },
317
+ });
318
+ } catch (error) {
319
+ operations.push({
320
+ operation: 'deriveSharedSecret',
321
+ success: false,
322
+ error: error instanceof Error ? error.message : String(error),
323
+ });
324
+ }
325
+
326
+ // Test 5: AES-GCM encryption/decryption
327
+ try {
328
+ const opStart = performance.now();
329
+ const keyPair = await provider.generateKeyPair();
330
+ const sharedSecret = await provider.deriveSharedSecret(keyPair.privateKey, keyPair.publicKey);
331
+ const salt = provider.randomBytes(32);
332
+ const info = provider.randomBytes(32);
333
+ const encryptionKey = await provider.deriveEncryptionKey(sharedSecret, salt, info);
334
+
335
+ const plaintext = new TextEncoder().encode('Hello, World!');
336
+ const iv = provider.randomBytes(12);
337
+ const ciphertext = await provider.encrypt(encryptionKey, plaintext, iv);
338
+ const decrypted = await provider.decrypt(encryptionKey, ciphertext, iv);
339
+ const decryptedText = new TextDecoder().decode(decrypted);
340
+ const duration = performance.now() - opStart;
341
+
342
+ operations.push({
343
+ operation: 'aesGcmEncryptDecrypt',
344
+ success: decryptedText === 'Hello, World!',
345
+ durationMs: duration,
346
+ details: {
347
+ plaintextSize: plaintext.byteLength,
348
+ ciphertextSize: ciphertext.byteLength,
349
+ decryptedCorrectly: decryptedText === 'Hello, World!',
350
+ },
351
+ });
352
+ } catch (error) {
353
+ operations.push({
354
+ operation: 'aesGcmEncryptDecrypt',
355
+ success: false,
356
+ error: error instanceof Error ? error.message : String(error),
357
+ });
358
+ }
359
+
360
+ const totalDuration = performance.now() - startTime;
361
+ const allSuccess = operations.every(op => op.success);
362
+
363
+ return {
364
+ provider: providerType,
365
+ available: true,
366
+ success: allSuccess,
367
+ operations,
368
+ totalDurationMs: totalDuration,
369
+ };
370
+ }
371
+
372
+ /**
373
+ * Benchmark a crypto provider's performance
374
+ *
375
+ * @param providerType - The provider type to benchmark
376
+ * @param iterations - Number of iterations for each operation (default: 10)
377
+ * @param logger - Optional logger for debug output
378
+ * @returns Benchmark results with timing statistics
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const bench = await benchmarkProvider('noble', 20);
383
+ * console.log(`ECDH: ${bench.ecdh.avgMs}ms (min: ${bench.ecdh.minMs}ms, max: ${bench.ecdh.maxMs}ms)`);
384
+ * console.log(`Random: ${bench.randomBytes.avgMs}ms`);
385
+ * console.log(`AES-GCM: ${bench.aesGcm.avgMs}ms`);
386
+ * ```
387
+ */
388
+ export async function benchmarkProvider(
389
+ providerType: CryptoProviderType,
390
+ iterations: number = 10,
391
+ logger?: Logger
392
+ ): Promise<ProviderBenchmark> {
393
+ // Create the specific provider without fallback to test it directly
394
+ let provider: CryptoProvider;
395
+ try {
396
+ switch (providerType) {
397
+ case 'webcrypto':
398
+ provider = new (await import('./providers/webcrypto-provider')).WebCryptoProvider();
399
+ break;
400
+ case 'noble':
401
+ provider = new (await import('./providers/noble-provider')).NobleCryptoProvider(logger);
402
+ break;
403
+ case 'node':
404
+ provider = new (await import('./providers/node-provider')).NodeCryptoProvider(logger);
405
+ break;
406
+ case 'quickcrypto':
407
+ provider = new (await import('./providers/quickcrypto-provider')).QuickCryptoProvider(logger);
408
+ break;
409
+ default:
410
+ throw new Error(`Unknown provider type: ${String(providerType)}`);
411
+ }
412
+ } catch (error) {
413
+ throw new Error(
414
+ `Provider ${providerType} failed to initialize: ${error instanceof Error ? error.message : String(error)}`
415
+ );
416
+ }
417
+
418
+ if (!provider.isAvailable) {
419
+ throw new Error(`Provider ${providerType} not available for benchmarking`);
420
+ }
421
+
422
+ // Benchmark ECDH key generation
423
+ const ecdhTimes: number[] = [];
424
+ for (let i = 0; i < iterations; i++) {
425
+ const start = performance.now();
426
+ await provider.generateKeyPair();
427
+ ecdhTimes.push(performance.now() - start);
428
+ }
429
+
430
+ // Benchmark random bytes
431
+ const randomBytesTimes: number[] = [];
432
+ for (let i = 0; i < iterations; i++) {
433
+ const start = performance.now();
434
+ provider.randomBytes(32);
435
+ randomBytesTimes.push(performance.now() - start);
436
+ }
437
+
438
+ // Benchmark AES-GCM
439
+ const aesGcmTimes: number[] = [];
440
+ // Prepare keys once
441
+ const keyPair = await provider.generateKeyPair();
442
+ const sharedSecret = await provider.deriveSharedSecret(keyPair.privateKey, keyPair.publicKey);
443
+ const salt = provider.randomBytes(32);
444
+ const info = provider.randomBytes(32);
445
+ const encryptionKey = await provider.deriveEncryptionKey(sharedSecret, salt, info);
446
+ const plaintext = provider.randomBytes(1024); // 1KB test data
447
+ const iv = provider.randomBytes(12);
448
+
449
+ for (let i = 0; i < iterations; i++) {
450
+ const start = performance.now();
451
+ await provider.encrypt(encryptionKey, plaintext, iv);
452
+ aesGcmTimes.push(performance.now() - start);
453
+ }
454
+
455
+ return {
456
+ provider: providerType,
457
+ ecdh: {
458
+ avgMs: ecdhTimes.reduce((a, b) => a + b, 0) / iterations,
459
+ minMs: Math.min(...ecdhTimes),
460
+ maxMs: Math.max(...ecdhTimes),
461
+ iterations,
462
+ },
463
+ randomBytes: {
464
+ avgMs: randomBytesTimes.reduce((a, b) => a + b, 0) / iterations,
465
+ minMs: Math.min(...randomBytesTimes),
466
+ maxMs: Math.max(...randomBytesTimes),
467
+ iterations,
468
+ },
469
+ aesGcm: {
470
+ avgMs: aesGcmTimes.reduce((a, b) => a + b, 0) / iterations,
471
+ minMs: Math.min(...aesGcmTimes),
472
+ maxMs: Math.max(...aesGcmTimes),
473
+ iterations,
474
+ },
475
+ };
476
+ }
477
+
478
+ /**
479
+ * Get comprehensive environment diagnostics
480
+ *
481
+ * @param logger - Optional logger for debug output
482
+ * @returns Complete diagnostics including registered providers, test results, and recommendations
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * const diag = await diagnoseEnvironment();
487
+ *
488
+ * // Print markdown report
489
+ * console.log(diag.toMarkdown());
490
+ *
491
+ * // Or access data programmatically
492
+ * console.log('Registered:', diag.registeredProviders);
493
+ * console.log('Recommended:', diag.recommendedProvider);
494
+ * diag.providerTests.forEach(test => {
495
+ * console.log(`${test.provider}: ${test.success ? '✅' : '❌'}`);
496
+ * });
497
+ * ```
498
+ */
499
+ export async function diagnoseEnvironment(logger?: Logger): Promise<EnvironmentDiagnostics> {
500
+ const platform = detectPlatform();
501
+ const registeredProviders = getRegisteredCryptoProviders();
502
+ const warnings: string[] = [];
503
+ const recommendations: string[] = [];
504
+
505
+ // Test all registered providers
506
+ const providerTests = await Promise.all(
507
+ registeredProviders.map(type => testProvider(type, logger))
508
+ );
509
+
510
+ // Find recommended provider (first successful test)
511
+ const recommendedProvider = providerTests.find(test => test.success && test.available)?.provider;
512
+
513
+ // Build provider detection summary
514
+ const capabilities: Record<CryptoProviderType, { available: boolean; tested: boolean; success: boolean }> = {
515
+ webcrypto: { available: false, tested: false, success: false },
516
+ node: { available: false, tested: false, success: false },
517
+ noble: { available: false, tested: false, success: false },
518
+ quickcrypto: { available: false, tested: false, success: false },
519
+ };
520
+
521
+ providerTests.forEach(test => {
522
+ capabilities[test.provider] = {
523
+ available: test.available,
524
+ tested: true,
525
+ success: test.success,
526
+ };
527
+ });
528
+
529
+ const detectionRecommendations: string[] = [];
530
+ if (!capabilities.noble.success) {
531
+ detectionRecommendations.push("Noble provider should always work as a fallback");
532
+ }
533
+ if (platform.isNode && !capabilities.node.tested) {
534
+ detectionRecommendations.push("Consider testing 'node' provider for optimal Node.js performance");
535
+ }
536
+ if (platform.isBrowser && !capabilities.webcrypto.tested) {
537
+ detectionRecommendations.push("Consider testing 'webcrypto' provider for browser environments");
538
+ }
539
+
540
+ const detection: ProviderDetection = {
541
+ webCrypto: capabilities.webcrypto.available && capabilities.webcrypto.success,
542
+ node: capabilities.node.available && capabilities.node.success,
543
+ noble: capabilities.noble.available && capabilities.noble.success,
544
+ quickCrypto: capabilities.quickcrypto.available && capabilities.quickcrypto.success,
545
+ recommended: recommendedProvider,
546
+ capabilities,
547
+ recommendations: detectionRecommendations,
548
+ };
549
+
550
+ // Generate warnings
551
+ providerTests.forEach(test => {
552
+ if (!test.success && test.available) {
553
+ warnings.push(`${test.provider} provider registered but failed operation tests`);
554
+ }
555
+ });
556
+
557
+ if (registeredProviders.length === 0) {
558
+ warnings.push('No crypto providers registered. Import at least one provider.');
559
+ }
560
+
561
+ if (!recommendedProvider) {
562
+ warnings.push('No working crypto provider found. All registered providers failed tests.');
563
+ }
564
+
565
+ // Generate platform-specific recommendations
566
+ if (platform.isNode) {
567
+ if (!registeredProviders.includes('node')) {
568
+ recommendations.push("Consider importing 'node' provider for optimal Node.js performance");
569
+ }
570
+ if (!recommendedProvider) {
571
+ recommendations.push("Import '@bananalink-sdk/protocol/crypto/provider/node' for Node.js");
572
+ }
573
+ } else if (platform.isBrowser) {
574
+ if (!registeredProviders.includes('webcrypto')) {
575
+ recommendations.push("Consider importing 'webcrypto' provider for browsers");
576
+ }
577
+ if (!recommendedProvider) {
578
+ recommendations.push("Import '@bananalink-sdk/protocol/crypto/provider/webcrypto' for browsers");
579
+ }
580
+ } else if (platform.isReactNative) {
581
+ if (!registeredProviders.includes('quickcrypto')) {
582
+ recommendations.push("Consider importing 'quickcrypto' provider for React Native");
583
+ }
584
+ if (!recommendedProvider) {
585
+ recommendations.push("Import '@bananalink-sdk/protocol/crypto/provider/quickcrypto' for React Native");
586
+ }
587
+ }
588
+
589
+ // Universal recommendation
590
+ if (!recommendedProvider || registeredProviders.length === 0) {
591
+ recommendations.push("Import '@bananalink-sdk/protocol/crypto/provider/noble' as a universal fallback");
592
+ }
593
+
594
+ const diagnostics: EnvironmentDiagnostics = {
595
+ timestamp: new Date().toISOString(),
596
+ platform,
597
+ detection,
598
+ registeredProviders,
599
+ providerTests,
600
+ recommendedProvider,
601
+ warnings,
602
+ recommendations,
603
+
604
+ toMarkdown(): string {
605
+ let md = '# Crypto Environment Diagnostics\n\n';
606
+ md += `**Generated:** ${this.timestamp}\n\n`;
607
+
608
+ // Platform section
609
+ md += '## Platform\n\n';
610
+ const platformType = this.platform.isNode
611
+ ? 'Node.js'
612
+ : this.platform.isBrowser
613
+ ? 'Browser'
614
+ : this.platform.isReactNative
615
+ ? 'React Native'
616
+ : 'Unknown';
617
+ md += `- **Type:** ${platformType}\n`;
618
+ if (this.platform.platform) {
619
+ md += `- **OS:** ${this.platform.platform}\n`;
620
+ }
621
+ if (this.platform.userAgent) {
622
+ // Truncate long user agents
623
+ const ua = this.platform.userAgent.length > 80
624
+ ? `${this.platform.userAgent.substring(0, 80)}...`
625
+ : this.platform.userAgent;
626
+ md += `- **User Agent:** ${ua}\n`;
627
+ }
628
+ md += '\n';
629
+
630
+ // Provider Detection section
631
+ md += '## Provider Detection\n\n';
632
+ md += `- **WebCrypto:** ${this.detection.webCrypto ? '✅' : '❌'}\n`;
633
+ md += `- **Node.js Crypto:** ${this.detection.node ? '✅' : '❌'}\n`;
634
+ md += `- **Noble (Pure JS):** ${this.detection.noble ? '✅' : '❌'}\n`;
635
+ md += `- **QuickCrypto (RN):** ${this.detection.quickCrypto ? '✅' : '❌'}\n`;
636
+ if (this.detection.recommended) {
637
+ md += `- **Recommended:** ${this.detection.recommended}\n`;
638
+ }
639
+ md += '\n';
640
+
641
+ // Capabilities section
642
+ md += '## Capabilities\n\n';
643
+ Object.entries(this.detection.capabilities).forEach(([provider, capability]) => {
644
+ const status = capability.tested
645
+ ? capability.success ? '✅ Available' : '❌ Failed'
646
+ : '⚪ Not Tested';
647
+ md += `- **${provider}:** ${status}\n`;
648
+ });
649
+ md += '\n';
650
+
651
+ // Registered providers section
652
+ md += '## Registered Providers\n\n';
653
+ if (this.registeredProviders.length === 0) {
654
+ md += 'No providers registered.\n\n';
655
+ } else {
656
+ this.registeredProviders.forEach(provider => {
657
+ md += `- ${provider}${provider === this.recommendedProvider ? ' (recommended)' : ''}\n`;
658
+ });
659
+ md += '\n';
660
+ }
661
+
662
+ // Provider tests section
663
+ md += '## Provider Tests\n\n';
664
+ if (this.providerTests.length === 0) {
665
+ md += 'No tests run (no providers registered).\n\n';
666
+ } else {
667
+ this.providerTests.forEach(test => {
668
+ md += `### ${test.provider}\n\n`;
669
+ md += `- **Available:** ${test.available ? '✅' : '❌'}\n`;
670
+ if (!test.available) {
671
+ md += `- **Reason:** ${test.unavailableReason}\n`;
672
+ } else {
673
+ md += `- **Overall Success:** ${test.success ? '✅' : '❌'}\n`;
674
+ md += `- **Total Duration:** ${test.totalDurationMs.toFixed(2)}ms\n\n`;
675
+ md += '**Operations:**\n\n';
676
+ test.operations.forEach(op => {
677
+ md += `- **${op.operation}:** ${op.success ? '✅' : '❌'}`;
678
+ if (op.durationMs) md += ` (${op.durationMs.toFixed(2)}ms)`;
679
+ if (op.error) md += ` - ${op.error}`;
680
+ md += '\n';
681
+ });
682
+ }
683
+ md += '\n';
684
+ });
685
+ }
686
+
687
+ // Recommendations section
688
+ md += '## Recommendations\n\n';
689
+ if (this.recommendedProvider) {
690
+ md += `- Recommended: Use '${this.recommendedProvider}' provider (passed all tests)\n`;
691
+ }
692
+ if (this.recommendations.length > 0) {
693
+ this.recommendations.forEach(rec => {
694
+ md += `- ${rec}\n`;
695
+ });
696
+ }
697
+ md += '\n';
698
+
699
+ // Warnings section
700
+ if (this.warnings.length > 0) {
701
+ md += '## Warnings\n\n';
702
+ this.warnings.forEach(warning => {
703
+ md += `⚠️ ${warning}\n\n`;
704
+ });
705
+ }
706
+
707
+ return md;
708
+ },
709
+
710
+ toJSON(): string {
711
+ return JSON.stringify(
712
+ {
713
+ timestamp: this.timestamp,
714
+ platform: this.platform,
715
+ detection: this.detection,
716
+ registeredProviders: this.registeredProviders,
717
+ providerTests: this.providerTests,
718
+ recommendedProvider: this.recommendedProvider,
719
+ warnings: this.warnings,
720
+ recommendations: this.recommendations,
721
+ },
722
+ null,
723
+ 2
724
+ );
725
+ },
726
+ };
727
+
728
+ return diagnostics;
729
+ }
730
+
731
+ /**
732
+ * Compare performance of multiple providers
733
+ *
734
+ * @param providers - Array of provider types to compare
735
+ * @param iterations - Number of iterations for each benchmark (default: 10)
736
+ * @param logger - Optional logger for debug output
737
+ * @returns Array of benchmark results for comparison
738
+ *
739
+ * @example
740
+ * ```typescript
741
+ * const results = await compareProviders(['webcrypto', 'noble'], 20);
742
+ * results.forEach(result => {
743
+ * console.log(`\n${result.provider}:`);
744
+ * console.log(` ECDH: ${result.ecdh.avgMs.toFixed(2)}ms`);
745
+ * console.log(` Random: ${result.randomBytes.avgMs.toFixed(2)}ms`);
746
+ * console.log(` AES: ${result.aesGcm.avgMs.toFixed(2)}ms`);
747
+ * });
748
+ * ```
749
+ */
750
+ export async function compareProviders(
751
+ providers: CryptoProviderType[],
752
+ iterations: number = 10,
753
+ logger?: Logger
754
+ ): Promise<ProviderBenchmark[]> {
755
+ const results: ProviderBenchmark[] = [];
756
+
757
+ for (const provider of providers) {
758
+ try {
759
+ const benchmark = await benchmarkProvider(provider, iterations, logger);
760
+ results.push(benchmark);
761
+ } catch (error) {
762
+ logger?.warn(`Failed to benchmark ${provider}:`, {
763
+ error: {
764
+ message: error instanceof Error ? error.message : String(error),
765
+ stack: error instanceof Error ? error.stack : undefined,
766
+ },
767
+ });
768
+ }
769
+ }
770
+
771
+ return results;
772
+ }