@bananalink-sdk/protocol 1.2.8 → 1.4.0

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 (83) hide show
  1. package/dist/{chunk-32OWUOZ3.js → chunk-KJ7QIHAY.js} +11 -7
  2. package/dist/chunk-KJ7QIHAY.js.map +1 -0
  3. package/dist/{chunk-VXLUSU5B.cjs → chunk-MUYKP6UQ.cjs} +63 -8
  4. package/dist/chunk-MUYKP6UQ.cjs.map +1 -0
  5. package/dist/{chunk-MCZG7QEM.cjs → chunk-NGPP7HUR.cjs} +11 -7
  6. package/dist/chunk-NGPP7HUR.cjs.map +1 -0
  7. package/dist/{chunk-LELPCIE7.js → chunk-OBJR2TL4.js} +54 -4
  8. package/dist/chunk-OBJR2TL4.js.map +1 -0
  9. package/dist/{chunk-KNGZKGRS.cjs → chunk-RZPN2GDJ.cjs} +13 -4
  10. package/dist/chunk-RZPN2GDJ.cjs.map +1 -0
  11. package/dist/{chunk-7KYDLL3B.js → chunk-XCMAKN3P.js} +13 -5
  12. package/dist/chunk-XCMAKN3P.js.map +1 -0
  13. package/dist/{client-session-claim-C4lUik3b.d.cts → client-session-claim-CkRKTG50.d.cts} +12 -2
  14. package/dist/{client-session-claim-3QF3noOr.d.ts → client-session-claim-CrIDASkZ.d.ts} +12 -2
  15. package/dist/crypto/providers/noble-provider.cjs +2 -3
  16. package/dist/crypto/providers/noble-provider.d.cts +0 -7
  17. package/dist/crypto/providers/noble-provider.d.ts +0 -7
  18. package/dist/crypto/providers/noble-provider.js +1 -2
  19. package/dist/crypto/providers/node-provider.cjs +7 -29
  20. package/dist/crypto/providers/node-provider.cjs.map +1 -1
  21. package/dist/crypto/providers/node-provider.d.cts +0 -7
  22. package/dist/crypto/providers/node-provider.d.ts +0 -7
  23. package/dist/crypto/providers/node-provider.js +7 -29
  24. package/dist/crypto/providers/node-provider.js.map +1 -1
  25. package/dist/crypto/providers/quickcrypto-provider.cjs +8 -46
  26. package/dist/crypto/providers/quickcrypto-provider.cjs.map +1 -1
  27. package/dist/crypto/providers/quickcrypto-provider.d.cts +0 -9
  28. package/dist/crypto/providers/quickcrypto-provider.d.ts +0 -9
  29. package/dist/crypto/providers/quickcrypto-provider.js +7 -45
  30. package/dist/crypto/providers/quickcrypto-provider.js.map +1 -1
  31. package/dist/crypto/providers/webcrypto-provider.cjs +0 -2
  32. package/dist/crypto/providers/webcrypto-provider.cjs.map +1 -1
  33. package/dist/crypto/providers/webcrypto-provider.d.cts +0 -7
  34. package/dist/crypto/providers/webcrypto-provider.d.ts +0 -7
  35. package/dist/crypto/providers/webcrypto-provider.js +0 -2
  36. package/dist/crypto/providers/webcrypto-provider.js.map +1 -1
  37. package/dist/{crypto-BUS06Qz-.d.cts → crypto-BK0Ile6V.d.cts} +1 -1
  38. package/dist/{crypto-BUS06Qz-.d.ts → crypto-BK0Ile6V.d.ts} +1 -1
  39. package/dist/crypto-export.cjs +50 -51
  40. package/dist/crypto-export.cjs.map +1 -1
  41. package/dist/crypto-export.d.cts +1 -1
  42. package/dist/crypto-export.d.ts +1 -1
  43. package/dist/crypto-export.js +2 -4
  44. package/dist/crypto-export.js.map +1 -1
  45. package/dist/index.cjs +8 -4
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +31 -4
  48. package/dist/index.d.ts +31 -4
  49. package/dist/index.js +7 -4
  50. package/dist/index.js.map +1 -1
  51. package/dist/schemas-export.cjs +76 -72
  52. package/dist/schemas-export.d.cts +116 -1
  53. package/dist/schemas-export.d.ts +116 -1
  54. package/dist/schemas-export.js +1 -1
  55. package/dist/testing.d.cts +2 -2
  56. package/dist/testing.d.ts +2 -2
  57. package/dist/validation-export.cjs +76 -72
  58. package/dist/validation-export.d.cts +1 -1
  59. package/dist/validation-export.d.ts +1 -1
  60. package/dist/validation-export.js +1 -1
  61. package/package.json +1 -1
  62. package/src/crypto/providers/noble-provider.ts +44 -49
  63. package/src/crypto/providers/node-provider.ts +18 -59
  64. package/src/crypto/providers/quickcrypto-provider.ts +25 -84
  65. package/src/crypto/providers/registry.ts +14 -9
  66. package/src/crypto/providers/webcrypto-provider.ts +28 -43
  67. package/src/index.ts +1 -0
  68. package/src/schemas/client-messages.ts +14 -0
  69. package/src/schemas/wallet-messages.ts +4 -0
  70. package/src/types/client-messages.ts +26 -1
  71. package/src/types/index.ts +9 -0
  72. package/src/types/persistence.ts +32 -0
  73. package/src/types/wallet-messages.ts +6 -2
  74. package/dist/chunk-32OWUOZ3.js.map +0 -1
  75. package/dist/chunk-7KYDLL3B.js.map +0 -1
  76. package/dist/chunk-A6FLEJ7R.cjs +0 -62
  77. package/dist/chunk-A6FLEJ7R.cjs.map +0 -1
  78. package/dist/chunk-KNGZKGRS.cjs.map +0 -1
  79. package/dist/chunk-LELPCIE7.js.map +0 -1
  80. package/dist/chunk-MCZG7QEM.cjs.map +0 -1
  81. package/dist/chunk-TCVKC227.js +0 -56
  82. package/dist/chunk-TCVKC227.js.map +0 -1
  83. package/dist/chunk-VXLUSU5B.cjs.map +0 -1
@@ -1,7 +1,6 @@
1
1
  import type { Logger } from '@bananalink-sdk/logger';
2
2
  import type { CryptoProvider, CryptoKeyLike, ProviderKeyPair } from '../../types/crypto-provider';
3
3
  import { NobleCryptoProvider } from './noble-provider';
4
- import { registerCryptoProvider } from './registry';
5
4
 
6
5
  // Global require function type for React Native environments
7
6
  type RequireFunction = (id: string) => unknown;
@@ -51,7 +50,7 @@ class QuickCryptoKey implements CryptoKeyLike {
51
50
  public readonly type: 'public' | 'private' | 'secret',
52
51
  public readonly algorithm: string,
53
52
  public readonly extractable: boolean = true,
54
- public readonly usages: readonly string[] = []
53
+ public readonly usages: readonly string[] = [],
55
54
  ) {}
56
55
  }
57
56
 
@@ -110,8 +109,8 @@ export class QuickCryptoProvider implements CryptoProvider {
110
109
  this.logger?.error('Failed to initialize QuickCrypto', {
111
110
  error: {
112
111
  message: error instanceof Error ? error.message : String(error),
113
- stack: error instanceof Error ? error.stack : undefined
114
- }
112
+ stack: error instanceof Error ? error.stack : undefined,
113
+ },
115
114
  });
116
115
  // Mark as unavailable if we can't actually use it
117
116
  (this as { isAvailable: boolean }).isAvailable = false;
@@ -137,20 +136,8 @@ export class QuickCryptoProvider implements CryptoProvider {
137
136
  }
138
137
 
139
138
  return {
140
- privateKey: new QuickCryptoKey(
141
- nobleKeyPair.privateKey.data,
142
- 'private',
143
- 'ECDH-P256',
144
- true,
145
- ['deriveKey']
146
- ),
147
- publicKey: new QuickCryptoKey(
148
- nobleKeyPair.publicKey.data,
149
- 'public',
150
- 'ECDH-P256',
151
- true,
152
- []
153
- )
139
+ privateKey: new QuickCryptoKey(nobleKeyPair.privateKey.data, 'private', 'ECDH-P256', true, ['deriveKey']),
140
+ publicKey: new QuickCryptoKey(nobleKeyPair.publicKey.data, 'public', 'ECDH-P256', true, []),
154
141
  };
155
142
  }
156
143
 
@@ -179,13 +166,7 @@ export class QuickCryptoProvider implements CryptoProvider {
179
166
  }
180
167
 
181
168
  // Wrap in QuickCryptoKey for consistency
182
- return new QuickCryptoKey(
183
- nobleKey.data,
184
- 'public',
185
- 'ECDH-P256',
186
- true,
187
- []
188
- );
169
+ return new QuickCryptoKey(nobleKey.data, 'public', 'ECDH-P256', true, []);
189
170
  }
190
171
 
191
172
  /**
@@ -199,13 +180,7 @@ export class QuickCryptoProvider implements CryptoProvider {
199
180
  }
200
181
 
201
182
  // Wrap in QuickCryptoKey for consistency
202
- return new QuickCryptoKey(
203
- nobleKey.data,
204
- 'private',
205
- 'ECDH-P256',
206
- true,
207
- ['deriveKey']
208
- );
183
+ return new QuickCryptoKey(nobleKey.data, 'private', 'ECDH-P256', true, ['deriveKey']);
209
184
  }
210
185
 
211
186
  /**
@@ -221,13 +196,7 @@ export class QuickCryptoProvider implements CryptoProvider {
221
196
  }
222
197
 
223
198
  // Wrap in QuickCryptoKey for consistency
224
- return new QuickCryptoKey(
225
- sharedSecret.data,
226
- 'secret',
227
- 'AES-GCM',
228
- true,
229
- ['encrypt', 'decrypt']
230
- );
199
+ return new QuickCryptoKey(sharedSecret.data, 'secret', 'AES-GCM', true, ['encrypt', 'decrypt']);
231
200
  }
232
201
 
233
202
  /**
@@ -241,13 +210,7 @@ export class QuickCryptoProvider implements CryptoProvider {
241
210
  }
242
211
 
243
212
  // Wrap in QuickCryptoKey for consistency
244
- return new QuickCryptoKey(
245
- encryptionKey.data,
246
- 'secret',
247
- 'AES-GCM',
248
- true,
249
- ['encrypt', 'decrypt']
250
- );
213
+ return new QuickCryptoKey(encryptionKey.data, 'secret', 'AES-GCM', true, ['encrypt', 'decrypt']);
251
214
  }
252
215
 
253
216
  /**
@@ -265,8 +228,8 @@ export class QuickCryptoProvider implements CryptoProvider {
265
228
  } catch (error) {
266
229
  this.logger?.error('QuickCrypto randomBytes failed, falling back to Noble', {
267
230
  error: {
268
- message: error instanceof Error ? error.message : String(error)
269
- }
231
+ message: error instanceof Error ? error.message : String(error),
232
+ },
270
233
  });
271
234
  return this.nobleProvider.randomBytes(length);
272
235
  }
@@ -283,7 +246,7 @@ export class QuickCryptoProvider implements CryptoProvider {
283
246
 
284
247
  this.logger?.debug('Encrypting data with AES-GCM using QuickCrypto', {
285
248
  dataSize: data.byteLength,
286
- ivSize: iv.byteLength
249
+ ivSize: iv.byteLength,
287
250
  });
288
251
 
289
252
  const secretKey = key as QuickCryptoKey;
@@ -291,7 +254,7 @@ export class QuickCryptoProvider implements CryptoProvider {
291
254
  if (secretKey.type !== 'secret') {
292
255
  const error = new Error('Expected secret key');
293
256
  this.logger?.error('Encryption failed - invalid key type', {
294
- actualType: secretKey.type
257
+ actualType: secretKey.type,
295
258
  });
296
259
  throw error;
297
260
  }
@@ -313,7 +276,7 @@ export class QuickCryptoProvider implements CryptoProvider {
313
276
  result.set(authTag, encrypted.length + final.length);
314
277
 
315
278
  this.logger?.debug('Encryption completed using QuickCrypto', {
316
- ciphertextSize: result.byteLength
279
+ ciphertextSize: result.byteLength,
317
280
  });
318
281
 
319
282
  return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength);
@@ -321,8 +284,8 @@ export class QuickCryptoProvider implements CryptoProvider {
321
284
  this.logger?.error('QuickCrypto encryption failed, falling back to Noble', {
322
285
  error: {
323
286
  message: error instanceof Error ? error.message : String(error),
324
- stack: error instanceof Error ? error.stack : undefined
325
- }
287
+ stack: error instanceof Error ? error.stack : undefined,
288
+ },
326
289
  });
327
290
  return this.nobleProvider.encrypt(key, data, iv);
328
291
  }
@@ -339,7 +302,7 @@ export class QuickCryptoProvider implements CryptoProvider {
339
302
 
340
303
  this.logger?.debug('Decrypting data with AES-GCM using QuickCrypto', {
341
304
  dataSize: data.byteLength,
342
- ivSize: iv.byteLength
305
+ ivSize: iv.byteLength,
343
306
  });
344
307
 
345
308
  const secretKey = key as QuickCryptoKey;
@@ -347,7 +310,7 @@ export class QuickCryptoProvider implements CryptoProvider {
347
310
  if (secretKey.type !== 'secret') {
348
311
  const error = new Error('Expected secret key');
349
312
  this.logger?.error('Decryption failed - invalid key type', {
350
- actualType: secretKey.type
313
+ actualType: secretKey.type,
351
314
  });
352
315
  throw error;
353
316
  }
@@ -378,7 +341,7 @@ export class QuickCryptoProvider implements CryptoProvider {
378
341
  result.set(final, decrypted.length);
379
342
 
380
343
  this.logger?.debug('Decryption completed using QuickCrypto', {
381
- plaintextSize: result.byteLength
344
+ plaintextSize: result.byteLength,
382
345
  });
383
346
 
384
347
  return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength);
@@ -386,8 +349,8 @@ export class QuickCryptoProvider implements CryptoProvider {
386
349
  this.logger?.error('QuickCrypto decryption failed, falling back to Noble', {
387
350
  error: {
388
351
  message: error instanceof Error ? error.message : String(error),
389
- stack: error instanceof Error ? error.stack : undefined
390
- }
352
+ stack: error instanceof Error ? error.stack : undefined,
353
+ },
391
354
  });
392
355
  return this.nobleProvider.decrypt(key, data, iv);
393
356
  }
@@ -417,8 +380,8 @@ export class QuickCryptoProvider implements CryptoProvider {
417
380
  } catch (error) {
418
381
  this.logger?.error('QuickCrypto HMAC failed, falling back to Noble', {
419
382
  error: {
420
- message: error instanceof Error ? error.message : String(error)
421
- }
383
+ message: error instanceof Error ? error.message : String(error),
384
+ },
422
385
  });
423
386
  return this.nobleProvider.generateHMAC(key, data);
424
387
  }
@@ -452,32 +415,10 @@ export class QuickCryptoProvider implements CryptoProvider {
452
415
  } catch (error) {
453
416
  this.logger?.error('QuickCrypto HMAC verification failed, falling back to Noble', {
454
417
  error: {
455
- message: error instanceof Error ? error.message : String(error)
456
- }
418
+ message: error instanceof Error ? error.message : String(error),
419
+ },
457
420
  });
458
421
  return this.nobleProvider.verifyHMAC(key, data, mac);
459
422
  }
460
423
  }
461
424
  }
462
-
463
- /**
464
- * Self-register QuickCrypto provider on import
465
- * This allows the provider to be available when explicitly imported
466
- *
467
- * Note: QuickCrypto depends on Noble for ECDH operations, so we auto-register
468
- * Noble as well to ensure all required functionality is available.
469
- */
470
- // Auto-register noble as a dependency (QuickCrypto delegates ECDH to Noble)
471
- import './noble-provider';
472
-
473
- registerCryptoProvider('quickcrypto', (logger) => new QuickCryptoProvider(logger));
474
-
475
- // TypeScript module augmentation to track this provider is available
476
- declare global {
477
- // eslint-disable-next-line @typescript-eslint/no-namespace
478
- namespace BananaLink {
479
- interface RegisteredCryptoProviders {
480
- quickcrypto: true;
481
- }
482
- }
483
- }
@@ -1,9 +1,17 @@
1
1
  /**
2
2
  * Global registry for crypto providers
3
3
  *
4
- * This registry allows providers to self-register when imported,
5
- * enabling explicit import patterns while maintaining the factory pattern
6
- * internally within BananaLink and BananaWallet classes.
4
+ * @deprecated This module is deprecated. Use crypto factory functions instead:
5
+ * ```typescript
6
+ * import { BananaLink, webSocketTransport, nobleCrypto } from '@bananalink-sdk/client'
7
+ *
8
+ * const client = new BananaLink({
9
+ * transport: webSocketTransport(),
10
+ * crypto: nobleCrypto(),
11
+ * })
12
+ * ```
13
+ *
14
+ * The registry is kept for backward compatibility but will be removed in a future version.
7
15
  */
8
16
 
9
17
  import type { CryptoProvider, CryptoProviderType } from '../../types/crypto-provider';
@@ -35,10 +43,7 @@ const cryptoProviders = new Map<CryptoProviderType, CryptoProviderFactory>();
35
43
  * registerCryptoProvider('noble', (logger) => new NobleCryptoProvider(logger));
36
44
  * ```
37
45
  */
38
- export function registerCryptoProvider(
39
- type: CryptoProviderType,
40
- factory: CryptoProviderFactory
41
- ): void {
46
+ export function registerCryptoProvider(type: CryptoProviderType, factory: CryptoProviderFactory): void {
42
47
  // Skip if already registered (idempotent)
43
48
  if (cryptoProviders.has(type)) {
44
49
  return;
@@ -62,8 +67,8 @@ export function getCryptoProviderFactory(type: CryptoProviderType): CryptoProvid
62
67
 
63
68
  throw new Error(
64
69
  `Crypto provider '${type}' is not registered. ${importHint}\n` +
65
- `Available providers: ${availableProviders || 'none'}\n` +
66
- `Make sure to import the provider before using it.`
70
+ `Available providers: ${availableProviders || 'none'}\n` +
71
+ `Make sure to import the provider before using it.`,
67
72
  );
68
73
  }
69
74
 
@@ -1,6 +1,5 @@
1
1
  import type { Logger } from '@bananalink-sdk/logger';
2
2
  import type { CryptoProvider, CryptoKeyLike, ProviderKeyPair } from '../../types/crypto-provider';
3
- import { registerCryptoProvider } from './registry';
4
3
 
5
4
  /**
6
5
  * WebCrypto CryptoKey wrapper to implement CryptoKeyLike interface
@@ -74,7 +73,7 @@ export class WebCryptoProvider implements CryptoProvider {
74
73
  namedCurve: 'P-256',
75
74
  },
76
75
  true, // extractable
77
- ['deriveKey']
76
+ ['deriveKey'],
78
77
  );
79
78
 
80
79
  this.logger?.debug('Key pair generation completed');
@@ -110,7 +109,9 @@ export class WebCryptoProvider implements CryptoProvider {
110
109
 
111
110
  this.logger?.debug('importPublicKey called', {
112
111
  keyLength: keyBytes.length,
113
- keyBytesFirst20: Array.from(keyBytes.slice(0, 20)).map(b => b.toString(16).padStart(2, '0')).join(' ')
112
+ keyBytesFirst20: Array.from(keyBytes.slice(0, 20))
113
+ .map((b) => b.toString(16).padStart(2, '0'))
114
+ .join(' '),
114
115
  });
115
116
 
116
117
  try {
@@ -122,7 +123,7 @@ export class WebCryptoProvider implements CryptoProvider {
122
123
  namedCurve: 'P-256',
123
124
  },
124
125
  true,
125
- []
126
+ [],
126
127
  );
127
128
 
128
129
  this.logger?.debug('Public key import successful');
@@ -131,8 +132,8 @@ export class WebCryptoProvider implements CryptoProvider {
131
132
  this.logger?.error('Public key import failed', {
132
133
  error: {
133
134
  message: error instanceof Error ? error.message : String(error),
134
- stack: error instanceof Error ? error.stack : undefined
135
- }
135
+ stack: error instanceof Error ? error.stack : undefined,
136
+ },
136
137
  });
137
138
  throw new Error(`Invalid P-256 public key: ${String(error)}`);
138
139
  }
@@ -143,7 +144,7 @@ export class WebCryptoProvider implements CryptoProvider {
143
144
  */
144
145
  async importPrivateKey(keyData: ArrayBuffer): Promise<CryptoKeyLike> {
145
146
  this.logger?.debug('Importing private key', {
146
- keyLength: keyData.byteLength
147
+ keyLength: keyData.byteLength,
147
148
  });
148
149
 
149
150
  try {
@@ -155,7 +156,7 @@ export class WebCryptoProvider implements CryptoProvider {
155
156
  namedCurve: 'P-256',
156
157
  },
157
158
  true,
158
- ['deriveKey']
159
+ ['deriveKey'],
159
160
  );
160
161
 
161
162
  this.logger?.debug('Private key import successful');
@@ -164,8 +165,8 @@ export class WebCryptoProvider implements CryptoProvider {
164
165
  this.logger?.error('Private key import failed', {
165
166
  error: {
166
167
  message: error instanceof Error ? error.message : String(error),
167
- stack: error instanceof Error ? error.stack : undefined
168
- }
168
+ stack: error instanceof Error ? error.stack : undefined,
169
+ },
169
170
  });
170
171
  throw new Error(`Invalid P-256 private key: ${String(error)}`);
171
172
  }
@@ -189,7 +190,7 @@ export class WebCryptoProvider implements CryptoProvider {
189
190
  length: 256,
190
191
  },
191
192
  true,
192
- ['encrypt', 'decrypt']
193
+ ['encrypt', 'decrypt'],
193
194
  );
194
195
 
195
196
  this.logger?.debug('Shared secret derivation completed');
@@ -198,8 +199,8 @@ export class WebCryptoProvider implements CryptoProvider {
198
199
  this.logger?.error('Shared secret derivation failed', {
199
200
  error: {
200
201
  message: error instanceof Error ? error.message : String(error),
201
- stack: error instanceof Error ? error.stack : undefined
202
- }
202
+ stack: error instanceof Error ? error.stack : undefined,
203
+ },
203
204
  });
204
205
  throw error;
205
206
  }
@@ -215,7 +216,7 @@ export class WebCryptoProvider implements CryptoProvider {
215
216
  await crypto.subtle.exportKey('raw', unwrapCryptoKey(sharedSecret)),
216
217
  { name: 'HKDF' },
217
218
  false,
218
- ['deriveKey']
219
+ ['deriveKey'],
219
220
  );
220
221
 
221
222
  // Derive encryption key using HKDF
@@ -229,7 +230,7 @@ export class WebCryptoProvider implements CryptoProvider {
229
230
  baseKey,
230
231
  { name: 'AES-GCM', length: 256 },
231
232
  true,
232
- ['encrypt', 'decrypt']
233
+ ['encrypt', 'decrypt'],
233
234
  );
234
235
  return new WebCryptoKeyWrapper(derivedKey);
235
236
  }
@@ -250,7 +251,7 @@ export class WebCryptoProvider implements CryptoProvider {
250
251
  async encrypt(key: CryptoKeyLike, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer> {
251
252
  this.logger?.debug('Encrypting data with AES-GCM', {
252
253
  dataSize: data.byteLength,
253
- ivSize: iv.byteLength
254
+ ivSize: iv.byteLength,
254
255
  });
255
256
 
256
257
  try {
@@ -261,19 +262,19 @@ export class WebCryptoProvider implements CryptoProvider {
261
262
  tagLength: 128, // 128-bit tag
262
263
  },
263
264
  unwrapCryptoKey(key),
264
- data
265
+ data,
265
266
  );
266
267
 
267
268
  this.logger?.debug('Encryption completed', {
268
- ciphertextSize: ciphertext.byteLength
269
+ ciphertextSize: ciphertext.byteLength,
269
270
  });
270
271
  return ciphertext;
271
272
  } catch (error) {
272
273
  this.logger?.error('Encryption failed', {
273
274
  error: {
274
275
  message: error instanceof Error ? error.message : String(error),
275
- stack: error instanceof Error ? error.stack : undefined
276
- }
276
+ stack: error instanceof Error ? error.stack : undefined,
277
+ },
277
278
  });
278
279
  throw error;
279
280
  }
@@ -285,7 +286,7 @@ export class WebCryptoProvider implements CryptoProvider {
285
286
  async decrypt(key: CryptoKeyLike, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer> {
286
287
  this.logger?.debug('Decrypting data with AES-GCM', {
287
288
  dataSize: data.byteLength,
288
- ivSize: iv.byteLength
289
+ ivSize: iv.byteLength,
289
290
  });
290
291
 
291
292
  try {
@@ -296,19 +297,19 @@ export class WebCryptoProvider implements CryptoProvider {
296
297
  tagLength: 128, // 128-bit tag
297
298
  },
298
299
  unwrapCryptoKey(key),
299
- data
300
+ data,
300
301
  );
301
302
 
302
303
  this.logger?.debug('Decryption completed', {
303
- plaintextSize: plaintext.byteLength
304
+ plaintextSize: plaintext.byteLength,
304
305
  });
305
306
  return plaintext;
306
307
  } catch (error) {
307
308
  this.logger?.error('Decryption failed', {
308
309
  error: {
309
310
  message: error instanceof Error ? error.message : String(error),
310
- stack: error instanceof Error ? error.stack : undefined
311
- }
311
+ stack: error instanceof Error ? error.stack : undefined,
312
+ },
312
313
  });
313
314
  throw error;
314
315
  }
@@ -324,7 +325,7 @@ export class WebCryptoProvider implements CryptoProvider {
324
325
  await crypto.subtle.exportKey('raw', unwrapCryptoKey(key)),
325
326
  { name: 'HMAC', hash: 'SHA-256' },
326
327
  false,
327
- ['sign']
328
+ ['sign'],
328
329
  );
329
330
 
330
331
  return crypto.subtle.sign('HMAC', hmacKey, data);
@@ -340,25 +341,9 @@ export class WebCryptoProvider implements CryptoProvider {
340
341
  await crypto.subtle.exportKey('raw', unwrapCryptoKey(key)),
341
342
  { name: 'HMAC', hash: 'SHA-256' },
342
343
  false,
343
- ['verify']
344
+ ['verify'],
344
345
  );
345
346
 
346
347
  return crypto.subtle.verify('HMAC', hmacKey, mac, data);
347
348
  }
348
349
  }
349
-
350
- /**
351
- * Self-register WebCrypto provider on import
352
- * This allows the provider to be available when explicitly imported
353
- */
354
- registerCryptoProvider('webcrypto', () => new WebCryptoProvider());
355
-
356
- // TypeScript module augmentation to track this provider is available
357
- declare global {
358
- // eslint-disable-next-line @typescript-eslint/no-namespace
359
- namespace BananaLink {
360
- interface RegisteredCryptoProviders {
361
- webcrypto: true;
362
- }
363
- }
364
- }
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from './types/providers';
17
17
  export * from './types/wallet-messages';
18
18
  export * from './types/client-messages';
19
19
  export * from './types/relay-messages';
20
+ export * from './types/persistence';
20
21
 
21
22
  // Note: Zod validation schemas have been moved to '@bananalink-sdk/protocol/schemas'
22
23
  // For runtime validation, import from '@bananalink-sdk/protocol/schemas'
@@ -15,6 +15,20 @@ export const clientSessionClaimSchema = z.object({
15
15
  // Client message payload schemas
16
16
  export const clientReconnectPayloadSchema = z.object({
17
17
  type: z.literal('client_reconnect'),
18
+ sessionClaim: clientSessionClaimSchema,
19
+ clientPublicKey: z
20
+ .string()
21
+ .min(1, 'Client public key cannot be empty')
22
+ .regex(/^(AES-GCM:|cleartext:)/, 'Client public key must start with "AES-GCM:" or "cleartext:"'),
23
+ });
24
+
25
+ export const clientHandshakeMessageSchema = z.object({
26
+ type: z.literal('client_handshake'),
27
+ clientPublicKey: z
28
+ .string()
29
+ .min(1, 'Client public key cannot be empty')
30
+ .regex(/^(AES-GCM:|cleartext:)/, 'Client public key must start with "AES-GCM:" or "cleartext:"'),
31
+ timestamp: z.number().int().positive('Timestamp must be a positive integer'),
18
32
  });
19
33
 
20
34
  export const closeSessionPayloadSchema = z.object({
@@ -46,6 +46,10 @@ export const authenticateConnectionPayloadSchema = z.object({
46
46
 
47
47
  export const walletReconnectPayloadSchema = z.object({
48
48
  type: z.literal('wallet_reconnect'),
49
+ walletPublicKey: z
50
+ .string()
51
+ .min(1, 'Wallet public key cannot be empty')
52
+ .regex(/^(AES-GCM:|cleartext:)/, 'Wallet public key must start with "AES-GCM:" or "cleartext:"'),
49
53
  });
50
54
 
51
55
  // Union of all wallet message payloads
@@ -29,10 +29,26 @@ export interface ClientSessionClaim {
29
29
 
30
30
  /**
31
31
  * Client reconnect payload
32
- * Used to reconnect after network interruption
32
+ * Used to reconnect after network interruption with re-handshake support
33
33
  */
34
34
  export interface ClientReconnectPayload {
35
35
  type: 'client_reconnect';
36
+ /** Client session claim for authentication */
37
+ sessionClaim: ClientSessionClaim;
38
+ /** Client's public key for ECDH key exchange in format "algorithm:base64_public_key" (e.g., "AES-GCM:...") */
39
+ clientPublicKey: string;
40
+ }
41
+
42
+ /**
43
+ * Client handshake message
44
+ * Sent during re-handshake when client is the connected party responding to wallet reconnect
45
+ */
46
+ export interface ClientHandshakeMessage {
47
+ type: 'client_handshake';
48
+ /** Client's public key for ECDH key exchange in format "algorithm:base64_public_key" (e.g., "AES-GCM:...") */
49
+ clientPublicKey: string;
50
+ /** Message timestamp */
51
+ timestamp: number;
36
52
  }
37
53
 
38
54
  /**
@@ -52,6 +68,8 @@ export type ClientMessagePayload =
52
68
  // Session management payloads
53
69
  | ClientReconnectPayload
54
70
  | CloseSessionPayload
71
+ // Re-handshake payload (sent by connected client when wallet reconnects)
72
+ | ClientHandshakeMessage
55
73
  // Post-authentication operation request payloads (v2.1+)
56
74
  | SignMessageRequestPayload
57
75
  | SignTypedDataRequestPayload
@@ -82,3 +100,10 @@ export function isClientReconnectPayload(payload: ClientMessagePayload): payload
82
100
  export function isCloseSessionPayload(payload: ClientMessagePayload): payload is CloseSessionPayload {
83
101
  return payload?.type === 'close_session';
84
102
  }
103
+
104
+ /**
105
+ * Type guard for client handshake message (used during re-handshake)
106
+ */
107
+ export function isClientHandshakeMessage(message: unknown): message is ClientHandshakeMessage {
108
+ return (message as ClientHandshakeMessage)?.type === 'client_handshake';
109
+ }
@@ -32,6 +32,7 @@ export type {
32
32
  ConnectionRejectedMessage,
33
33
  ConnectionAuthenticatedMessage,
34
34
  RelayToDAppMessage,
35
+ RelayToWalletMessage,
35
36
  PrefetchMetadataResponse,
36
37
  } from './wallet-messages';
37
38
 
@@ -115,12 +116,14 @@ export type {
115
116
  ClientReconnectPayload,
116
117
  CloseSessionPayload,
117
118
  ClientMessagePayload,
119
+ ClientHandshakeMessage,
118
120
  } from './client-messages';
119
121
 
120
122
  // Re-export client message type guards
121
123
  export {
122
124
  isClientReconnectPayload,
123
125
  isCloseSessionPayload,
126
+ isClientHandshakeMessage,
124
127
  } from './client-messages';
125
128
 
126
129
  // Re-export v2.0 relay notification message types
@@ -195,3 +198,9 @@ export type {
195
198
  KeyDerivation,
196
199
  CryptoPayload,
197
200
  } from './crypto';
201
+
202
+ export type {
203
+ PersistedClientSession,
204
+ PersistedWalletSession,
205
+ SessionStorage,
206
+ } from './persistence';
@@ -0,0 +1,32 @@
1
+ import type { ClientSessionClaim } from './client-messages';
2
+ import type { WalletSessionClaim } from './wallet-messages';
3
+ import type { DAppMetadata } from './core';
4
+
5
+ export interface PersistedClientSession {
6
+ sessionId: string;
7
+ clientSessionClaim: ClientSessionClaim;
8
+ peerPublicKey: string;
9
+ createdAt: number;
10
+ lastConnectedAt: number;
11
+ dAppMetadata?: {
12
+ name: string;
13
+ url: string;
14
+ icon?: string;
15
+ };
16
+ }
17
+
18
+ export interface PersistedWalletSession {
19
+ sessionId: string;
20
+ walletSessionClaim: WalletSessionClaim;
21
+ peerPublicKey: string;
22
+ createdAt: number;
23
+ lastConnectedAt: number;
24
+ dAppMetadata?: DAppMetadata;
25
+ }
26
+
27
+ export interface SessionStorage {
28
+ get(key: string): Promise<string | null>;
29
+ set(key: string, value: string): Promise<void>;
30
+ remove(key: string): Promise<void>;
31
+ getAllKeys(): Promise<string[]>;
32
+ }
@@ -5,7 +5,7 @@
5
5
 
6
6
  import type { EncryptedPayload } from './discovery';
7
7
  import type { DAppMetadata, SessionConfig } from './core';
8
- import type { CloseSessionPayload } from './client-messages';
8
+ import type { CloseSessionPayload, ClientHandshakeMessage } from './client-messages';
9
9
  import type {
10
10
  RequestFulfilledPayload,
11
11
  RequestRejectedPayload,
@@ -82,10 +82,12 @@ export interface AuthenticateConnectionPayload {
82
82
 
83
83
  /**
84
84
  * Wallet reconnect payload
85
- * Used to reconnect after network interruption
85
+ * Used to reconnect after network interruption with re-handshake support
86
86
  */
87
87
  export interface WalletReconnectPayload {
88
88
  type: 'wallet_reconnect';
89
+ /** Wallet's public key for ECDH key exchange in format "algorithm:base64_public_key" (e.g., "AES-GCM:...") */
90
+ walletPublicKey: string;
89
91
  }
90
92
 
91
93
  /**
@@ -212,6 +214,8 @@ export type RelayToDAppMessage =
212
214
  | ConnectionRejectedMessage
213
215
  | ConnectionAuthenticatedMessage;
214
216
 
217
+ export type RelayToWalletMessage = ClientHandshakeMessage;
218
+
215
219
  /**
216
220
  * Type guards for wallet messages
217
221
  */