@zendfi/sdk 0.5.8 → 0.6.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.
@@ -0,0 +1,587 @@
1
+ // src/device-bound-crypto.ts
2
+ import { Keypair } from "@solana/web3.js";
3
+ import * as crypto from "crypto";
4
+ var DeviceFingerprintGenerator = class {
5
+ /**
6
+ * Generate a unique device fingerprint
7
+ * Combines multiple browser attributes for uniqueness
8
+ */
9
+ static async generate() {
10
+ const components = {};
11
+ try {
12
+ components.canvas = await this.getCanvasFingerprint();
13
+ components.webgl = await this.getWebGLFingerprint();
14
+ components.audio = await this.getAudioFingerprint();
15
+ components.screen = `${screen.width}x${screen.height}x${screen.colorDepth}`;
16
+ components.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
17
+ components.languages = navigator.languages?.join(",") || navigator.language;
18
+ components.platform = navigator.platform;
19
+ components.hardwareConcurrency = navigator.hardwareConcurrency?.toString() || "unknown";
20
+ const combined = Object.entries(components).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}:${value}`).join("|");
21
+ const fingerprint = await this.sha256(combined);
22
+ return {
23
+ fingerprint,
24
+ generatedAt: Date.now(),
25
+ components
26
+ };
27
+ } catch (error) {
28
+ console.warn("Device fingerprinting failed, using fallback", error);
29
+ return this.generateFallbackFingerprint();
30
+ }
31
+ }
32
+ /**
33
+ * Graceful fallback fingerprint generation
34
+ * Works in headless browsers, SSR, and restricted environments
35
+ */
36
+ static async generateFallbackFingerprint() {
37
+ const components = {};
38
+ try {
39
+ if (typeof navigator !== "undefined") {
40
+ components.platform = navigator.platform || "unknown";
41
+ components.languages = navigator.languages?.join(",") || navigator.language || "unknown";
42
+ components.hardwareConcurrency = navigator.hardwareConcurrency?.toString() || "unknown";
43
+ }
44
+ if (typeof screen !== "undefined") {
45
+ components.screen = `${screen.width || 0}x${screen.height || 0}x${screen.colorDepth || 0}`;
46
+ }
47
+ if (typeof Intl !== "undefined") {
48
+ try {
49
+ components.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
50
+ } catch {
51
+ components.timezone = "unknown";
52
+ }
53
+ }
54
+ } catch {
55
+ components.platform = "fallback";
56
+ }
57
+ let randomEntropy = "";
58
+ try {
59
+ if (typeof window !== "undefined" && window.crypto?.getRandomValues) {
60
+ const arr = new Uint8Array(16);
61
+ window.crypto.getRandomValues(arr);
62
+ randomEntropy = Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("");
63
+ } else if (typeof crypto !== "undefined" && crypto.randomBytes) {
64
+ randomEntropy = crypto.randomBytes(16).toString("hex");
65
+ }
66
+ } catch {
67
+ randomEntropy = Date.now().toString(36) + Math.random().toString(36);
68
+ }
69
+ const combined = Object.entries(components).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}:${value}`).join("|") + "|entropy:" + randomEntropy;
70
+ const fingerprint = await this.sha256(combined);
71
+ return {
72
+ fingerprint,
73
+ generatedAt: Date.now(),
74
+ components
75
+ };
76
+ }
77
+ static async getCanvasFingerprint() {
78
+ const canvas = document.createElement("canvas");
79
+ const ctx = canvas.getContext("2d");
80
+ if (!ctx) return "no-canvas";
81
+ canvas.width = 200;
82
+ canvas.height = 50;
83
+ ctx.textBaseline = "top";
84
+ ctx.font = '14px "Arial"';
85
+ ctx.fillStyle = "#f60";
86
+ ctx.fillRect(0, 0, 100, 50);
87
+ ctx.fillStyle = "#069";
88
+ ctx.fillText("ZendFi \u{1F510}", 2, 2);
89
+ ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
90
+ ctx.fillText("Device-Bound", 4, 17);
91
+ return canvas.toDataURL();
92
+ }
93
+ static async getWebGLFingerprint() {
94
+ const canvas = document.createElement("canvas");
95
+ const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
96
+ if (!gl) return "no-webgl";
97
+ const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
98
+ if (!debugInfo) return "no-debug-info";
99
+ const vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
100
+ const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
101
+ return `${vendor}|${renderer}`;
102
+ }
103
+ static async getAudioFingerprint() {
104
+ try {
105
+ const AudioContext = window.AudioContext || window.webkitAudioContext;
106
+ if (!AudioContext) return "no-audio";
107
+ const context = new AudioContext();
108
+ const oscillator = context.createOscillator();
109
+ const analyser = context.createAnalyser();
110
+ const gainNode = context.createGain();
111
+ const scriptProcessor = context.createScriptProcessor(4096, 1, 1);
112
+ gainNode.gain.value = 0;
113
+ oscillator.connect(analyser);
114
+ analyser.connect(scriptProcessor);
115
+ scriptProcessor.connect(gainNode);
116
+ gainNode.connect(context.destination);
117
+ oscillator.start(0);
118
+ return new Promise((resolve) => {
119
+ scriptProcessor.onaudioprocess = (event) => {
120
+ const output = event.inputBuffer.getChannelData(0);
121
+ const hash = Array.from(output.slice(0, 30)).reduce((acc, val) => acc + Math.abs(val), 0);
122
+ oscillator.stop();
123
+ scriptProcessor.disconnect();
124
+ context.close();
125
+ resolve(hash.toString());
126
+ };
127
+ });
128
+ } catch (error) {
129
+ return "audio-error";
130
+ }
131
+ }
132
+ static async sha256(data) {
133
+ if (typeof window !== "undefined" && window.crypto && window.crypto.subtle) {
134
+ const encoder = new TextEncoder();
135
+ const dataBuffer = encoder.encode(data);
136
+ const hashBuffer = await window.crypto.subtle.digest("SHA-256", dataBuffer);
137
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
138
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
139
+ } else {
140
+ return crypto.createHash("sha256").update(data).digest("hex");
141
+ }
142
+ }
143
+ };
144
+ var SessionKeyCrypto = class {
145
+ /**
146
+ * Encrypt a Solana keypair with PIN + device fingerprint
147
+ * Uses Argon2id for key derivation and AES-256-GCM for encryption
148
+ */
149
+ static async encrypt(keypair, pin, deviceFingerprint) {
150
+ if (!/^\d{6}$/.test(pin)) {
151
+ throw new Error("PIN must be exactly 6 numeric digits");
152
+ }
153
+ const encryptionKey = await this.deriveKey(pin, deviceFingerprint);
154
+ const nonce = this.generateNonce();
155
+ const secretKey = keypair.secretKey;
156
+ const encryptedData = await this.aesEncrypt(secretKey, encryptionKey, nonce);
157
+ return {
158
+ encryptedData: Buffer.from(encryptedData).toString("base64"),
159
+ nonce: Buffer.from(nonce).toString("base64"),
160
+ publicKey: keypair.publicKey.toBase58(),
161
+ deviceFingerprint,
162
+ version: "argon2id-aes256gcm-v1"
163
+ };
164
+ }
165
+ /**
166
+ * Decrypt an encrypted session key with PIN + device fingerprint
167
+ */
168
+ static async decrypt(encrypted, pin, deviceFingerprint) {
169
+ if (!/^\d{6}$/.test(pin)) {
170
+ throw new Error("PIN must be exactly 6 numeric digits");
171
+ }
172
+ if (encrypted.deviceFingerprint !== deviceFingerprint) {
173
+ throw new Error("Device fingerprint mismatch - wrong device or security threat");
174
+ }
175
+ const encryptionKey = await this.deriveKey(pin, deviceFingerprint);
176
+ const encryptedData = Buffer.from(encrypted.encryptedData, "base64");
177
+ const nonce = Buffer.from(encrypted.nonce, "base64");
178
+ try {
179
+ const secretKey = await this.aesDecrypt(encryptedData, encryptionKey, nonce);
180
+ return Keypair.fromSecretKey(secretKey);
181
+ } catch (error) {
182
+ throw new Error("Decryption failed - wrong PIN or corrupted data");
183
+ }
184
+ }
185
+ /**
186
+ * Derive encryption key from PIN + device fingerprint using Argon2id
187
+ *
188
+ * Argon2id parameters (OWASP recommended):
189
+ * - Memory: 64MB (65536 KB)
190
+ * - Iterations: 3
191
+ * - Parallelism: 4
192
+ * - Salt: device fingerprint
193
+ */
194
+ static async deriveKey(pin, deviceFingerprint) {
195
+ if (typeof window !== "undefined" && window.crypto && window.crypto.subtle) {
196
+ const encoder = new TextEncoder();
197
+ const keyMaterial = await window.crypto.subtle.importKey(
198
+ "raw",
199
+ encoder.encode(pin),
200
+ { name: "PBKDF2" },
201
+ false,
202
+ ["deriveBits"]
203
+ );
204
+ const derivedBits = await window.crypto.subtle.deriveBits(
205
+ {
206
+ name: "PBKDF2",
207
+ salt: encoder.encode(deviceFingerprint),
208
+ iterations: 1e5,
209
+ // High iteration count for security
210
+ hash: "SHA-256"
211
+ },
212
+ keyMaterial,
213
+ 256
214
+ // 256 bits = 32 bytes for AES-256
215
+ );
216
+ return new Uint8Array(derivedBits);
217
+ } else {
218
+ const salt = crypto.createHash("sha256").update(deviceFingerprint).digest();
219
+ return crypto.pbkdf2Sync(pin, salt, 1e5, 32, "sha256");
220
+ }
221
+ }
222
+ /**
223
+ * Generate random nonce for AES-GCM (12 bytes)
224
+ */
225
+ static generateNonce() {
226
+ if (typeof window !== "undefined" && window.crypto) {
227
+ return window.crypto.getRandomValues(new Uint8Array(12));
228
+ } else {
229
+ return crypto.randomBytes(12);
230
+ }
231
+ }
232
+ /**
233
+ * Encrypt with AES-256-GCM
234
+ */
235
+ static async aesEncrypt(plaintext, key, nonce) {
236
+ if (typeof window !== "undefined" && window.crypto && window.crypto.subtle) {
237
+ const keyBuffer = key.buffer.slice(key.byteOffset, key.byteOffset + key.byteLength);
238
+ const nonceBuffer = nonce.buffer.slice(nonce.byteOffset, nonce.byteOffset + nonce.byteLength);
239
+ const plaintextBuffer = plaintext.buffer.slice(plaintext.byteOffset, plaintext.byteOffset + plaintext.byteLength);
240
+ const cryptoKey = await window.crypto.subtle.importKey(
241
+ "raw",
242
+ keyBuffer,
243
+ { name: "AES-GCM" },
244
+ false,
245
+ ["encrypt"]
246
+ );
247
+ const encrypted = await window.crypto.subtle.encrypt(
248
+ {
249
+ name: "AES-GCM",
250
+ iv: nonceBuffer
251
+ },
252
+ cryptoKey,
253
+ plaintextBuffer
254
+ );
255
+ return new Uint8Array(encrypted);
256
+ } else {
257
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, nonce);
258
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
259
+ const authTag = cipher.getAuthTag();
260
+ return new Uint8Array(Buffer.concat([encrypted, authTag]));
261
+ }
262
+ }
263
+ /**
264
+ * Decrypt with AES-256-GCM
265
+ */
266
+ static async aesDecrypt(ciphertext, key, nonce) {
267
+ if (typeof window !== "undefined" && window.crypto && window.crypto.subtle) {
268
+ const keyBuffer = key.buffer.slice(key.byteOffset, key.byteOffset + key.byteLength);
269
+ const nonceBuffer = nonce.buffer.slice(nonce.byteOffset, nonce.byteOffset + nonce.byteLength);
270
+ const ciphertextBuffer = ciphertext.buffer.slice(ciphertext.byteOffset, ciphertext.byteOffset + ciphertext.byteLength);
271
+ const cryptoKey = await window.crypto.subtle.importKey(
272
+ "raw",
273
+ keyBuffer,
274
+ { name: "AES-GCM" },
275
+ false,
276
+ ["decrypt"]
277
+ );
278
+ const decrypted = await window.crypto.subtle.decrypt(
279
+ {
280
+ name: "AES-GCM",
281
+ iv: nonceBuffer
282
+ },
283
+ cryptoKey,
284
+ ciphertextBuffer
285
+ );
286
+ return new Uint8Array(decrypted);
287
+ } else {
288
+ const authTag = ciphertext.slice(-16);
289
+ const encrypted = ciphertext.slice(0, -16);
290
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
291
+ decipher.setAuthTag(authTag);
292
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
293
+ return new Uint8Array(decrypted);
294
+ }
295
+ }
296
+ };
297
+ var RecoveryQRGenerator = class {
298
+ /**
299
+ * Generate recovery QR data
300
+ * This allows users to recover their session key on a new device
301
+ */
302
+ static generate(encrypted) {
303
+ return {
304
+ encryptedSessionKey: encrypted.encryptedData,
305
+ nonce: encrypted.nonce,
306
+ publicKey: encrypted.publicKey,
307
+ version: "v1",
308
+ createdAt: Date.now()
309
+ };
310
+ }
311
+ /**
312
+ * Encode recovery QR as JSON string
313
+ */
314
+ static encode(recoveryQR) {
315
+ return JSON.stringify(recoveryQR);
316
+ }
317
+ /**
318
+ * Decode recovery QR from JSON string
319
+ */
320
+ static decode(qrData) {
321
+ try {
322
+ const parsed = JSON.parse(qrData);
323
+ if (!parsed.encryptedSessionKey || !parsed.nonce || !parsed.publicKey) {
324
+ throw new Error("Invalid recovery QR data");
325
+ }
326
+ return parsed;
327
+ } catch (error) {
328
+ throw new Error("Failed to decode recovery QR");
329
+ }
330
+ }
331
+ /**
332
+ * Re-encrypt session key for new device
333
+ */
334
+ static async reEncryptForNewDevice(recoveryQR, oldPin, oldDeviceFingerprint, newPin, newDeviceFingerprint) {
335
+ const oldEncrypted = {
336
+ encryptedData: recoveryQR.encryptedSessionKey,
337
+ nonce: recoveryQR.nonce,
338
+ publicKey: recoveryQR.publicKey,
339
+ deviceFingerprint: oldDeviceFingerprint,
340
+ version: "argon2id-aes256gcm-v1"
341
+ };
342
+ const keypair = await SessionKeyCrypto.decrypt(oldEncrypted, oldPin, oldDeviceFingerprint);
343
+ return await SessionKeyCrypto.encrypt(keypair, newPin, newDeviceFingerprint);
344
+ }
345
+ };
346
+ var DeviceBoundSessionKey = class _DeviceBoundSessionKey {
347
+ encrypted = null;
348
+ deviceFingerprint = null;
349
+ sessionKeyId = null;
350
+ recoveryQR = null;
351
+ // Auto-signing cache: decrypted keypair stored in memory
352
+ // Enables instant signing without re-entering PIN for subsequent payments
353
+ cachedKeypair = null;
354
+ cacheExpiry = null;
355
+ // Timestamp when cache expires
356
+ DEFAULT_CACHE_TTL_MS = 30 * 60 * 1e3;
357
+ // 30 minutes
358
+ /**
359
+ * Create a new device-bound session key
360
+ */
361
+ static async create(options) {
362
+ const deviceFingerprint = await DeviceFingerprintGenerator.generate();
363
+ const keypair = Keypair.generate();
364
+ const encrypted = await SessionKeyCrypto.encrypt(
365
+ keypair,
366
+ options.pin,
367
+ deviceFingerprint.fingerprint
368
+ );
369
+ const instance = new _DeviceBoundSessionKey();
370
+ instance.encrypted = encrypted;
371
+ instance.deviceFingerprint = deviceFingerprint;
372
+ if (options.generateRecoveryQR) {
373
+ instance.recoveryQR = RecoveryQRGenerator.generate(encrypted);
374
+ }
375
+ return instance;
376
+ }
377
+ /**
378
+ * Get encrypted data for backend storage
379
+ */
380
+ getEncryptedData() {
381
+ if (!this.encrypted) {
382
+ throw new Error("Session key not created yet");
383
+ }
384
+ return this.encrypted;
385
+ }
386
+ /**
387
+ * Get device fingerprint
388
+ */
389
+ getDeviceFingerprint() {
390
+ if (!this.deviceFingerprint) {
391
+ throw new Error("Device fingerprint not generated yet");
392
+ }
393
+ return this.deviceFingerprint.fingerprint;
394
+ }
395
+ /**
396
+ * Get public key
397
+ */
398
+ getPublicKey() {
399
+ if (!this.encrypted) {
400
+ throw new Error("Session key not created yet");
401
+ }
402
+ return this.encrypted.publicKey;
403
+ }
404
+ /**
405
+ * Get recovery QR data (if generated)
406
+ */
407
+ getRecoveryQR() {
408
+ return this.recoveryQR;
409
+ }
410
+ /**
411
+ * Decrypt and sign a transaction
412
+ *
413
+ * @param transaction - The transaction to sign
414
+ * @param pin - User's PIN (required only if keypair not cached)
415
+ * @param cacheKeypair - Whether to cache the decrypted keypair for future use (default: true)
416
+ * @param cacheTTL - Cache time-to-live in milliseconds (default: 30 minutes)
417
+ *
418
+ * @example
419
+ * ```typescript
420
+ * // First payment: requires PIN, caches keypair
421
+ * await sessionKey.signTransaction(tx1, '123456', true);
422
+ *
423
+ * // Subsequent payments: uses cached keypair, no PIN needed!
424
+ * await sessionKey.signTransaction(tx2, '', false); // PIN ignored if cached
425
+ *
426
+ * // Clear cache when done
427
+ * sessionKey.clearCache();
428
+ * ```
429
+ */
430
+ async signTransaction(transaction, pin = "", cacheKeypair = true, cacheTTL) {
431
+ if (!this.encrypted || !this.deviceFingerprint) {
432
+ throw new Error("Session key not initialized");
433
+ }
434
+ let keypair;
435
+ if (this.isCached()) {
436
+ keypair = this.cachedKeypair;
437
+ if (typeof console !== "undefined") {
438
+ console.log("\u{1F680} Using cached keypair - instant signing (no PIN required)");
439
+ }
440
+ } else {
441
+ if (!pin) {
442
+ throw new Error("PIN required: no cached keypair available");
443
+ }
444
+ keypair = await SessionKeyCrypto.decrypt(
445
+ this.encrypted,
446
+ pin,
447
+ this.deviceFingerprint.fingerprint
448
+ );
449
+ if (cacheKeypair) {
450
+ const ttl = cacheTTL || this.DEFAULT_CACHE_TTL_MS;
451
+ this.cacheKeypair(keypair, ttl);
452
+ if (typeof console !== "undefined") {
453
+ console.log(`\u2705 Keypair decrypted and cached for ${ttl / 1e3 / 60} minutes`);
454
+ }
455
+ }
456
+ }
457
+ transaction.sign(keypair);
458
+ return transaction;
459
+ }
460
+ /**
461
+ * Check if keypair is cached and valid
462
+ */
463
+ isCached() {
464
+ if (!this.cachedKeypair || !this.cacheExpiry) {
465
+ return false;
466
+ }
467
+ const now = Date.now();
468
+ if (now > this.cacheExpiry) {
469
+ this.clearCache();
470
+ return false;
471
+ }
472
+ return true;
473
+ }
474
+ /**
475
+ * Manually cache a keypair
476
+ * Called internally after PIN decryption
477
+ */
478
+ cacheKeypair(keypair, ttl) {
479
+ this.cachedKeypair = keypair;
480
+ this.cacheExpiry = Date.now() + ttl;
481
+ }
482
+ /**
483
+ * Clear cached keypair
484
+ * Should be called when user logs out or session ends
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * // Clear cache on logout
489
+ * sessionKey.clearCache();
490
+ *
491
+ * // Or clear automatically on tab close
492
+ * window.addEventListener('beforeunload', () => {
493
+ * sessionKey.clearCache();
494
+ * });
495
+ * ```
496
+ */
497
+ clearCache() {
498
+ this.cachedKeypair = null;
499
+ this.cacheExpiry = null;
500
+ if (typeof console !== "undefined") {
501
+ console.log("\u{1F9F9} Keypair cache cleared");
502
+ }
503
+ }
504
+ /**
505
+ * Decrypt and cache keypair without signing a transaction
506
+ * Useful for pre-warming the cache before user makes payments
507
+ *
508
+ * @example
509
+ * ```typescript
510
+ * // After session key creation, decrypt and cache
511
+ * await sessionKey.unlockWithPin('123456');
512
+ *
513
+ * // Now all subsequent payments are instant (no PIN)
514
+ * await sessionKey.signTransaction(tx1, '', false); // Instant!
515
+ * await sessionKey.signTransaction(tx2, '', false); // Instant!
516
+ * ```
517
+ */
518
+ async unlockWithPin(pin, cacheTTL) {
519
+ if (!this.encrypted || !this.deviceFingerprint) {
520
+ throw new Error("Session key not initialized");
521
+ }
522
+ const keypair = await SessionKeyCrypto.decrypt(
523
+ this.encrypted,
524
+ pin,
525
+ this.deviceFingerprint.fingerprint
526
+ );
527
+ const ttl = cacheTTL || this.DEFAULT_CACHE_TTL_MS;
528
+ this.cacheKeypair(keypair, ttl);
529
+ if (typeof console !== "undefined") {
530
+ console.log(`\u{1F513} Session key unlocked and cached for ${ttl / 1e3 / 60} minutes`);
531
+ }
532
+ }
533
+ /**
534
+ * Get time remaining until cache expires (in milliseconds)
535
+ * Returns 0 if not cached
536
+ */
537
+ getCacheTimeRemaining() {
538
+ if (!this.isCached() || !this.cacheExpiry) {
539
+ return 0;
540
+ }
541
+ const remaining = this.cacheExpiry - Date.now();
542
+ return Math.max(0, remaining);
543
+ }
544
+ /**
545
+ * Extend cache expiry time
546
+ * Useful to keep session active during user activity
547
+ *
548
+ * @example
549
+ * ```typescript
550
+ * // Extend cache by 15 minutes on each payment
551
+ * await sessionKey.signTransaction(tx, '');
552
+ * sessionKey.extendCache(15 * 60 * 1000);
553
+ * ```
554
+ */
555
+ extendCache(additionalTTL) {
556
+ if (!this.isCached()) {
557
+ throw new Error("Cannot extend cache: no cached keypair");
558
+ }
559
+ this.cacheExpiry += additionalTTL;
560
+ if (typeof console !== "undefined") {
561
+ const remainingMinutes = this.getCacheTimeRemaining() / 1e3 / 60;
562
+ console.log(`\u23F0 Cache extended - ${remainingMinutes.toFixed(1)} minutes remaining`);
563
+ }
564
+ }
565
+ /**
566
+ * Set session key ID after backend creation
567
+ */
568
+ setSessionKeyId(id) {
569
+ this.sessionKeyId = id;
570
+ }
571
+ /**
572
+ * Get session key ID
573
+ */
574
+ getSessionKeyId() {
575
+ if (!this.sessionKeyId) {
576
+ throw new Error("Session key not registered with backend");
577
+ }
578
+ return this.sessionKeyId;
579
+ }
580
+ };
581
+
582
+ export {
583
+ DeviceFingerprintGenerator,
584
+ SessionKeyCrypto,
585
+ RecoveryQRGenerator,
586
+ DeviceBoundSessionKey
587
+ };
@@ -0,0 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
@@ -0,0 +1,13 @@
1
+ import {
2
+ DeviceBoundSessionKey,
3
+ DeviceFingerprintGenerator,
4
+ RecoveryQRGenerator,
5
+ SessionKeyCrypto
6
+ } from "./chunk-XERHBDUK.mjs";
7
+ import "./chunk-Y6FXYEAI.mjs";
8
+ export {
9
+ DeviceBoundSessionKey,
10
+ DeviceFingerprintGenerator,
11
+ RecoveryQRGenerator,
12
+ SessionKeyCrypto
13
+ };
@@ -1,4 +1,4 @@
1
- import { W as WebhookHandlerConfig, a as WebhookHandlers } from './webhook-handler-D5CigE9G.mjs';
1
+ import { W as WebhookHandlerConfig, a as WebhookHandlers } from './webhook-handler-DGBeCWT-.mjs';
2
2
 
3
3
  /**
4
4
  * Express Webhook Handler
package/dist/express.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { W as WebhookHandlerConfig, a as WebhookHandlers } from './webhook-handler-D5CigE9G.js';
1
+ import { W as WebhookHandlerConfig, a as WebhookHandlers } from './webhook-handler-DGBeCWT-.js';
2
2
 
3
3
  /**
4
4
  * Express Webhook Handler
package/dist/express.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  processWebhook
3
- } from "./chunk-YFOBPGQE.mjs";
3
+ } from "./chunk-3ACJUM6V.mjs";
4
+ import "./chunk-Y6FXYEAI.mjs";
4
5
 
5
6
  // src/express.ts
6
7
  import { createHmac, timingSafeEqual } from "crypto";