@zendfi/sdk 0.5.8 → 0.7.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.
package/README.md CHANGED
@@ -26,11 +26,11 @@ console.log(payment.payment_url); // Send customer here
26
26
 
27
27
  | Feature | Stripe | PayPal | **ZendFi** |
28
28
  |---------|--------|--------|------------|
29
- | **Fees** | 2.9% + $0.30 | 3.5% + $0.49 | **0.6% flat** |
30
- | **Settlement** | 7 days | 3-5 days | **Instant** |
31
- | **Crypto Native** | Via 3rd party | Via 3rd party | **Built-in** |
32
- | **AI Agent Ready** | | | **✅ Native** |
33
- | **Setup Time** | 30 min | 30 min | **5 min** |
29
+ | **Fees** | 2.9% + $0.30 | 3.5% + $0.49 | **0.6% flat** |
30
+ | **Settlement** | 7 days | 3-5 days | **Instant** |
31
+ | **Crypto Native** | Via 3rd party | Via 3rd party | **Built-in** |
32
+ | **AI Agent Ready** | ACP | NO | **Native** |
33
+ | **Setup Time** | 30 min | 30 min | **5 min** |
34
34
 
35
35
  **Save 81% on fees.** Get paid instantly. Scale to AI when ready.
36
36
 
@@ -38,20 +38,21 @@ console.log(payment.payment_url); // Send customer here
38
38
 
39
39
  ## Features
40
40
 
41
- ### 🚀 **Core Payments** (Start Here)
41
+ ### **Core Payments** (Start Here)
42
+ - **Embedded Checkout** — Drop-in checkout component for your website/app
42
43
  - **Simple Payments** — QR codes, payment links, instant settlements
43
44
  - **Payment Links** — Reusable checkout pages for social/email
44
45
  - **Webhooks** — Real-time notifications with auto-verification
45
46
  - **Test Mode** — Free devnet testing with no real money
46
47
  - **Type-Safe** — Full TypeScript support with auto-completion
47
48
 
48
- ### 💼 **Scale Up** (When You Grow)
49
+ ### **Scale Up** (When You Grow)
49
50
  - **Subscriptions** — Recurring billing with trials
50
51
  - **Installments** — Buy now, pay later flows
51
52
  - **Invoices** — Professional invoicing with email
52
53
  - **Payment Splits** — Revenue sharing for marketplaces
53
54
 
54
- ### 🤖 **AI-Ready** (Optional Advanced)
55
+ ### **AI-Ready** (Optional Advanced)
55
56
  - **Agent Keys** — Scoped API keys for AI with spending limits
56
57
  - **Session Keys** — Pre-funded wallets for autonomous payments
57
58
  - **Payment Intents** — Two-phase commit for reliable checkout
@@ -125,6 +126,66 @@ console.log(payment.payment_url);
125
126
 
126
127
  ---
127
128
 
129
+ ## Embedded Checkout
130
+
131
+ Skip redirects entirely—embed the checkout directly into your website or app. Perfect for seamless user experiences.
132
+
133
+ ### Quick Example
134
+
135
+ ```typescript
136
+ import { ZendFiEmbeddedCheckout } from '@zendfi/sdk';
137
+
138
+ const checkout = new ZendFiEmbeddedCheckout({
139
+ linkCode: 'your-payment-link-code',
140
+ containerId: 'checkout-container',
141
+ mode: 'test',
142
+
143
+ onSuccess: (payment) => {
144
+ console.log('Payment successful!', payment.transactionSignature);
145
+ // Redirect to success page or show confirmation
146
+ },
147
+
148
+ onError: (error) => {
149
+ console.error('Payment failed:', error.message);
150
+ },
151
+ });
152
+
153
+ // Mount the checkout
154
+ await checkout.mount();
155
+ ```
156
+
157
+ ### HTML Setup
158
+
159
+ ```html
160
+ <div id="checkout-container"></div>
161
+ <script type="module">
162
+ import { ZendFiEmbeddedCheckout } from '@zendfi/sdk';
163
+ // ... (setup code above)
164
+ </script>
165
+ ```
166
+
167
+ ### Features
168
+
169
+ - **Drop-in Integration** — Works with React, Vue, Next.js, or vanilla JS
170
+ - **QR Code Generation** — Automatic mobile wallet support
171
+ - **Wallet Connect** — Phantom, Solflare, Backpack support
172
+ - **Real-time Updates** — Live payment confirmation polling
173
+ - **Gasless Transactions** — Optional backend-signed payments
174
+ - **Customizable Theme** — Match your brand colors & styles
175
+ - **TypeScript First** — Full type safety and autocomplete
176
+
177
+ ### Complete Documentation
178
+
179
+ For comprehensive guides, React examples, theming, and advanced usage:
180
+
181
+ - **Quick Start:** [`EMBEDDED_CHECKOUT_QUICKSTART.md`](./EMBEDDED_CHECKOUT_QUICKSTART.md)
182
+ - **Full Guide:** [`EMBEDDED_CHECKOUT.md`](./EMBEDDED_CHECKOUT.md)
183
+ - **Implementation Details:** [`EMBEDDED_CHECKOUT_IMPLEMENTATION.md`](./EMBEDDED_CHECKOUT_IMPLEMENTATION.md)
184
+ - **React Example:** [`examples/embedded-checkout-react.tsx`](./examples/embedded-checkout-react.tsx)
185
+ - **Vanilla JS Example:** [`examples/embedded-checkout-vanilla.html`](./examples/embedded-checkout-vanilla.html)
186
+
187
+ ---
188
+
128
189
  ## API Key Modes
129
190
 
130
191
  ZendFi uses **smart API keys** that automatically route to the correct network:
@@ -172,7 +233,7 @@ This covers:
172
233
 
173
234
  ---
174
235
 
175
- ## 🤖 AI-Ready Features (Optional Advanced)
236
+ ## AI-Ready Features (Optional Advanced)
176
237
 
177
238
  Building AI agents? ZendFi has native support for autonomous payments with cryptographic security and spending limits.
178
239
 
@@ -226,12 +287,15 @@ const payment = await zendfi.agent.pay({
226
287
 
227
288
  ---
228
289
 
229
- ## 📚 Core API Reference
290
+ ## Core API Reference
230
291
 
231
292
  ### Namespaced APIs
232
293
 
233
294
  ```typescript
234
- import { zendfi } from '@zendfi/sdk';
295
+ import { zendfi, ZendFiEmbeddedCheckout } from '@zendfi/sdk';
296
+
297
+ // Embedded Checkout (New!)
298
+ const checkout = new ZendFiEmbeddedCheckout({...});
235
299
 
236
300
  // Traditional Payments (Most Common)
237
301
  zendfi.createPayment(...)
@@ -399,6 +463,111 @@ const suggestion = await zendfi.pricing.getSuggestion({
399
463
  **Supported Countries (27+):**
400
464
  Argentina, Australia, Brazil, Canada, China, Colombia, Egypt, France, Germany, Ghana, Hong Kong, Hungary, India, Indonesia, Israel, Japan, Kenya, Mexico, Nigeria, Philippines, Poland, South Africa, Thailand, Turkey, Ukraine, United Kingdom, Vietnam, and more.
401
465
 
466
+ ---
467
+
468
+ ## Optional Helper Utilities
469
+
470
+ Production-ready utilities to simplify common integration patterns. All helpers are **optional**, **tree-shakeable**, and **zero-config**.
471
+
472
+ ```typescript
473
+ import {
474
+ SessionKeyCache,
475
+ WalletConnector,
476
+ TransactionPoller,
477
+ DevTools
478
+ } from '@zendfi/sdk/helpers';
479
+ ```
480
+
481
+ ### Why Use Helpers?
482
+
483
+ - **Optional**: Import only what you need
484
+ - **Tree-shakeable**: Unused code eliminated by bundlers
485
+ - **Zero config**: Sensible defaults, works out of the box
486
+ - **Pluggable**: Bring your own storage/AI/PIN providers
487
+ - **Production-ready**: Full TypeScript types, error handling
488
+
489
+ ### Available Helpers
490
+
491
+ | Helper | Purpose | Use Case |
492
+ |--------|---------|----------|
493
+ | `SessionKeyCache` | Cache encrypted session keys | Avoid re-prompting for PIN |
494
+ | `WalletConnector` | Detect & connect Solana wallets | Phantom, Solflare, Backpack |
495
+ | `PaymentIntentParser` | Parse natural language to payments | AI chat interfaces |
496
+ | `PINValidator` | Validate PIN strength | Device-bound security |
497
+ | `TransactionPoller` | Poll for confirmations | Wait for on-chain finality |
498
+ | `RetryStrategy` | Exponential backoff retries | Handle network failures |
499
+ | `SessionKeyLifecycle` | High-level session key manager | One-liner setup |
500
+ | `DevTools` | Debug mode & test utilities | Development & testing |
501
+
502
+ ### Session Key Cache
503
+
504
+ Cache encrypted session keys to avoid re-prompting users for their PIN:
505
+
506
+ ```typescript
507
+ import { SessionKeyCache, QuickCaches } from '@zendfi/sdk/helpers';
508
+
509
+ // Use presets
510
+ const cache = QuickCaches.persistent(); // 1 hour localStorage
511
+
512
+ // Use with device-bound session keys
513
+ const keypair = await cache.getCached(
514
+ sessionKeyId,
515
+ async () => {
516
+ const pin = await promptUserForPIN();
517
+ return await decryptKeypair(pin);
518
+ }
519
+ );
520
+ ```
521
+
522
+ ### Wallet Connector
523
+
524
+ Auto-detect and connect to Solana wallets:
525
+
526
+ ```typescript
527
+ import { WalletConnector } from '@zendfi/sdk/helpers';
528
+
529
+ const wallet = await WalletConnector.detectAndConnect();
530
+ console.log(wallet.address);
531
+
532
+ const signedTx = await wallet.signTransaction(transaction);
533
+ ```
534
+
535
+ ### Transaction Polling
536
+
537
+ Wait for confirmations with exponential backoff:
538
+
539
+ ```typescript
540
+ import { TransactionPoller } from '@zendfi/sdk/helpers';
541
+
542
+ const poller = new TransactionPoller({ connection: rpcConnection });
543
+ const result = await poller.waitForConfirmation(signature);
544
+ ```
545
+
546
+ ### Session Key Lifecycle
547
+
548
+ High-level wrapper for complete session key management:
549
+
550
+ ```typescript
551
+ import { SessionKeyLifecycle } from '@zendfi/sdk/helpers';
552
+
553
+ const lifecycle = new SessionKeyLifecycle(zendfi, {
554
+ cache: QuickCaches.persistent(),
555
+ autoCleanup: true,
556
+ });
557
+
558
+ await lifecycle.createAndFund({
559
+ userWallet: userAddress,
560
+ agentId: 'my-agent',
561
+ limitUsdc: 100,
562
+ });
563
+
564
+ await lifecycle.pay(5.00, 'Coffee');
565
+ ```
566
+
567
+ **Full documentation:** See [Helper Utilities Guide](https://docs.zendfi.tech/helpers) for complete API reference and examples.
568
+
569
+ ---
570
+
402
571
  ### Autonomous Delegation
403
572
 
404
573
  Enable agents to make payments without per-transaction approval:
@@ -709,7 +878,7 @@ await zendfi.cancelInstallmentPlan(plan.id);
709
878
 
710
879
  ---
711
880
 
712
- ### 🧾 Invoices
881
+ ### Invoices
713
882
 
714
883
  Professional invoices with crypto payment options.
715
884
 
@@ -1097,6 +1266,45 @@ console.log('Status:', updated.status); // "Confirmed"
1097
1266
 
1098
1267
  ## Examples
1099
1268
 
1269
+ ### Embedded Checkout Integration
1270
+
1271
+ ```typescript
1272
+ import { ZendFiEmbeddedCheckout } from '@zendfi/sdk';
1273
+
1274
+ // React Component
1275
+ function CheckoutPage({ linkCode }) {
1276
+ const containerRef = useRef(null);
1277
+
1278
+ useEffect(() => {
1279
+ const checkout = new ZendFiEmbeddedCheckout({
1280
+ linkCode,
1281
+ containerId: 'checkout-container',
1282
+ mode: 'test',
1283
+
1284
+ onSuccess: (payment) => {
1285
+ // Payment successful - redirect or show confirmation
1286
+ router.push(`/success?payment=${payment.paymentId}`);
1287
+ },
1288
+
1289
+ onError: (error) => {
1290
+ // Handle errors
1291
+ setError(error.message);
1292
+ },
1293
+
1294
+ theme: {
1295
+ primaryColor: '#8b5cf6',
1296
+ borderRadius: '16px',
1297
+ },
1298
+ });
1299
+
1300
+ checkout.mount();
1301
+ return () => checkout.unmount();
1302
+ }, [linkCode]);
1303
+
1304
+ return <div id="checkout-container" ref={containerRef} />;
1305
+ }
1306
+ ```
1307
+
1100
1308
  ### E-commerce Checkout
1101
1309
 
1102
1310
  ```typescript
@@ -0,0 +1,9 @@
1
+ import {
2
+ QuickCaches,
3
+ SessionKeyCache
4
+ } from "./chunk-5O5NAX65.mjs";
5
+ import "./chunk-Y6FXYEAI.mjs";
6
+ export {
7
+ QuickCaches,
8
+ SessionKeyCache
9
+ };
@@ -1,10 +1,3 @@
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
1
  // src/webhook-handler.ts
9
2
  import { createHmac, timingSafeEqual } from "crypto";
10
3
  var processedWebhooks = /* @__PURE__ */ new Set();
@@ -147,6 +140,5 @@ async function processWebhook(a, b, c) {
147
140
  }
148
141
 
149
142
  export {
150
- __require,
151
143
  processWebhook
152
144
  };
@@ -0,0 +1,366 @@
1
+ // src/helpers/cache.ts
2
+ var SessionKeyCache = class {
3
+ memoryCache = /* @__PURE__ */ new Map();
4
+ config;
5
+ refreshTimers = /* @__PURE__ */ new Map();
6
+ constructor(config = {}) {
7
+ this.config = {
8
+ storage: config.storage || "memory",
9
+ ttl: config.ttl || 30 * 60 * 1e3,
10
+ // 30 minutes default
11
+ autoRefresh: config.autoRefresh || false,
12
+ namespace: config.namespace || "zendfi_cache",
13
+ debug: config.debug || false
14
+ };
15
+ }
16
+ /**
17
+ * Get cached keypair or decrypt and cache
18
+ */
19
+ async getCached(sessionKeyId, decryptFn, options) {
20
+ this.log(`getCached: ${sessionKeyId}`);
21
+ const memoryCached = this.memoryCache.get(sessionKeyId);
22
+ if (memoryCached && Date.now() < memoryCached.expiry) {
23
+ this.log(`Memory cache HIT: ${sessionKeyId}`);
24
+ return memoryCached.keypair;
25
+ }
26
+ if (this.config.storage !== "memory") {
27
+ const persistentCached = await this.getFromStorage(sessionKeyId);
28
+ if (persistentCached && Date.now() < persistentCached.expiry) {
29
+ if (options?.deviceFingerprint && persistentCached.deviceFingerprint) {
30
+ if (options.deviceFingerprint !== persistentCached.deviceFingerprint) {
31
+ this.log(`Device fingerprint mismatch for ${sessionKeyId}`);
32
+ await this.invalidate(sessionKeyId);
33
+ return await this.decryptAndCache(sessionKeyId, decryptFn, options);
34
+ }
35
+ }
36
+ this.log(`Persistent cache HIT: ${sessionKeyId}`);
37
+ this.memoryCache.set(sessionKeyId, persistentCached);
38
+ return persistentCached.keypair;
39
+ }
40
+ }
41
+ this.log(`Cache MISS: ${sessionKeyId}`);
42
+ return await this.decryptAndCache(sessionKeyId, decryptFn, options);
43
+ }
44
+ /**
45
+ * Decrypt keypair and cache it
46
+ */
47
+ async decryptAndCache(sessionKeyId, decryptFn, options) {
48
+ const keypair = await decryptFn();
49
+ const expiry = Date.now() + this.config.ttl;
50
+ const cached = {
51
+ keypair,
52
+ expiry,
53
+ sessionKeyId,
54
+ deviceFingerprint: options?.deviceFingerprint
55
+ };
56
+ this.memoryCache.set(sessionKeyId, cached);
57
+ if (this.config.storage !== "memory") {
58
+ await this.setInStorage(sessionKeyId, cached);
59
+ }
60
+ if (this.config.autoRefresh) {
61
+ this.setupAutoRefresh(sessionKeyId, decryptFn, options);
62
+ }
63
+ this.log(`Cached: ${sessionKeyId}, expires in ${this.config.ttl}ms`);
64
+ return keypair;
65
+ }
66
+ /**
67
+ * Invalidate cached keypair
68
+ */
69
+ async invalidate(sessionKeyId) {
70
+ this.log(`Invalidating: ${sessionKeyId}`);
71
+ this.memoryCache.delete(sessionKeyId);
72
+ const timer = this.refreshTimers.get(sessionKeyId);
73
+ if (timer) {
74
+ clearTimeout(timer);
75
+ this.refreshTimers.delete(sessionKeyId);
76
+ }
77
+ if (this.config.storage !== "memory") {
78
+ await this.removeFromStorage(sessionKeyId);
79
+ }
80
+ }
81
+ /**
82
+ * Clear all cached keypairs
83
+ */
84
+ async clear() {
85
+ this.log("Clearing all cache");
86
+ this.memoryCache.clear();
87
+ for (const timer of this.refreshTimers.values()) {
88
+ clearTimeout(timer);
89
+ }
90
+ this.refreshTimers.clear();
91
+ if (this.config.storage !== "memory") {
92
+ await this.clearStorage();
93
+ }
94
+ }
95
+ /**
96
+ * Get cache statistics
97
+ */
98
+ getStats() {
99
+ const entries = Array.from(this.memoryCache.entries()).map(([id, cached]) => ({
100
+ sessionKeyId: id,
101
+ expiresIn: Math.max(0, cached.expiry - Date.now())
102
+ }));
103
+ return { size: this.memoryCache.size, entries };
104
+ }
105
+ /**
106
+ * Check if a session key is cached and valid
107
+ */
108
+ isCached(sessionKeyId) {
109
+ const cached = this.memoryCache.get(sessionKeyId);
110
+ return cached ? Date.now() < cached.expiry : false;
111
+ }
112
+ /**
113
+ * Update TTL for a cached session key
114
+ */
115
+ async extendTTL(sessionKeyId, additionalMs) {
116
+ const cached = this.memoryCache.get(sessionKeyId);
117
+ if (!cached) return false;
118
+ cached.expiry += additionalMs;
119
+ this.memoryCache.set(sessionKeyId, cached);
120
+ if (this.config.storage !== "memory") {
121
+ await this.setInStorage(sessionKeyId, cached);
122
+ }
123
+ this.log(`Extended TTL for ${sessionKeyId} by ${additionalMs}ms`);
124
+ return true;
125
+ }
126
+ // ============================================
127
+ // Storage Backend Implementations
128
+ // ============================================
129
+ async getFromStorage(sessionKeyId) {
130
+ try {
131
+ const key = this.getStorageKey(sessionKeyId);
132
+ if (this.config.storage === "localStorage") {
133
+ const data = localStorage.getItem(key);
134
+ if (!data) return null;
135
+ const parsed = JSON.parse(data);
136
+ return {
137
+ ...parsed,
138
+ keypair: this.deserializeKeypair(parsed.keypair)
139
+ };
140
+ }
141
+ if (this.config.storage === "indexedDB") {
142
+ return await this.getFromIndexedDB(key);
143
+ }
144
+ if (typeof this.config.storage === "object") {
145
+ const data = await this.config.storage.get(key);
146
+ if (!data) return null;
147
+ const parsed = JSON.parse(data);
148
+ return {
149
+ ...parsed,
150
+ keypair: this.deserializeKeypair(parsed.keypair)
151
+ };
152
+ }
153
+ } catch (error) {
154
+ this.log(`Error reading from storage: ${error}`);
155
+ }
156
+ return null;
157
+ }
158
+ async setInStorage(sessionKeyId, cached) {
159
+ try {
160
+ const key = this.getStorageKey(sessionKeyId);
161
+ const serialized = {
162
+ ...cached,
163
+ keypair: this.serializeKeypair(cached.keypair)
164
+ };
165
+ if (this.config.storage === "localStorage") {
166
+ localStorage.setItem(key, JSON.stringify(serialized));
167
+ return;
168
+ }
169
+ if (this.config.storage === "indexedDB") {
170
+ await this.setInIndexedDB(key, serialized);
171
+ return;
172
+ }
173
+ if (typeof this.config.storage === "object") {
174
+ await this.config.storage.set(key, JSON.stringify(serialized));
175
+ }
176
+ } catch (error) {
177
+ this.log(`Error writing to storage: ${error}`);
178
+ }
179
+ }
180
+ async removeFromStorage(sessionKeyId) {
181
+ try {
182
+ const key = this.getStorageKey(sessionKeyId);
183
+ if (this.config.storage === "localStorage") {
184
+ localStorage.removeItem(key);
185
+ return;
186
+ }
187
+ if (this.config.storage === "indexedDB") {
188
+ await this.removeFromIndexedDB(key);
189
+ return;
190
+ }
191
+ if (typeof this.config.storage === "object") {
192
+ await this.config.storage.remove(key);
193
+ }
194
+ } catch (error) {
195
+ this.log(`Error removing from storage: ${error}`);
196
+ }
197
+ }
198
+ async clearStorage() {
199
+ try {
200
+ if (this.config.storage === "localStorage") {
201
+ const keysToRemove = [];
202
+ for (let i = 0; i < localStorage.length; i++) {
203
+ const key = localStorage.key(i);
204
+ if (key?.startsWith(this.config.namespace)) {
205
+ keysToRemove.push(key);
206
+ }
207
+ }
208
+ keysToRemove.forEach((key) => localStorage.removeItem(key));
209
+ return;
210
+ }
211
+ if (this.config.storage === "indexedDB") {
212
+ await this.clearIndexedDB();
213
+ return;
214
+ }
215
+ if (typeof this.config.storage === "object") {
216
+ await this.config.storage.clear();
217
+ }
218
+ } catch (error) {
219
+ this.log(`Error clearing storage: ${error}`);
220
+ }
221
+ }
222
+ // ============================================
223
+ // IndexedDB Helpers
224
+ // ============================================
225
+ async getFromIndexedDB(key) {
226
+ return new Promise((resolve) => {
227
+ const request = indexedDB.open(this.config.namespace, 1);
228
+ request.onerror = () => resolve(null);
229
+ request.onupgradeneeded = (event) => {
230
+ const db = event.target.result;
231
+ if (!db.objectStoreNames.contains("cache")) {
232
+ db.createObjectStore("cache");
233
+ }
234
+ };
235
+ request.onsuccess = (event) => {
236
+ const db = event.target.result;
237
+ const transaction = db.transaction(["cache"], "readonly");
238
+ const store = transaction.objectStore("cache");
239
+ const getRequest = store.get(key);
240
+ getRequest.onsuccess = () => {
241
+ resolve(getRequest.result || null);
242
+ };
243
+ getRequest.onerror = () => resolve(null);
244
+ };
245
+ });
246
+ }
247
+ async setInIndexedDB(key, value) {
248
+ return new Promise((resolve, reject) => {
249
+ const request = indexedDB.open(this.config.namespace, 1);
250
+ request.onerror = () => reject(new Error("IndexedDB error"));
251
+ request.onupgradeneeded = (event) => {
252
+ const db = event.target.result;
253
+ if (!db.objectStoreNames.contains("cache")) {
254
+ db.createObjectStore("cache");
255
+ }
256
+ };
257
+ request.onsuccess = (event) => {
258
+ const db = event.target.result;
259
+ const transaction = db.transaction(["cache"], "readwrite");
260
+ const store = transaction.objectStore("cache");
261
+ store.put(value, key);
262
+ transaction.oncomplete = () => resolve();
263
+ transaction.onerror = () => reject(new Error("IndexedDB transaction error"));
264
+ };
265
+ });
266
+ }
267
+ async removeFromIndexedDB(key) {
268
+ return new Promise((resolve) => {
269
+ const request = indexedDB.open(this.config.namespace, 1);
270
+ request.onsuccess = (event) => {
271
+ const db = event.target.result;
272
+ const transaction = db.transaction(["cache"], "readwrite");
273
+ const store = transaction.objectStore("cache");
274
+ store.delete(key);
275
+ transaction.oncomplete = () => resolve();
276
+ };
277
+ request.onerror = () => resolve();
278
+ });
279
+ }
280
+ async clearIndexedDB() {
281
+ return new Promise((resolve) => {
282
+ const request = indexedDB.open(this.config.namespace, 1);
283
+ request.onsuccess = (event) => {
284
+ const db = event.target.result;
285
+ const transaction = db.transaction(["cache"], "readwrite");
286
+ const store = transaction.objectStore("cache");
287
+ store.clear();
288
+ transaction.oncomplete = () => resolve();
289
+ };
290
+ request.onerror = () => resolve();
291
+ });
292
+ }
293
+ // ============================================
294
+ // Serialization
295
+ // ============================================
296
+ serializeKeypair(keypair) {
297
+ if (keypair && typeof keypair === "object" && "secretKey" in keypair) {
298
+ return {
299
+ type: "solana",
300
+ secretKey: Array.from(keypair.secretKey)
301
+ };
302
+ }
303
+ if (keypair instanceof Uint8Array) {
304
+ return {
305
+ type: "uint8array",
306
+ data: Array.from(keypair)
307
+ };
308
+ }
309
+ return keypair;
310
+ }
311
+ deserializeKeypair(data) {
312
+ if (!data || typeof data !== "object") return data;
313
+ if (data.type === "solana" && data.secretKey) {
314
+ return new Uint8Array(data.secretKey);
315
+ }
316
+ if (data.type === "uint8array" && data.data) {
317
+ return new Uint8Array(data.data);
318
+ }
319
+ return data;
320
+ }
321
+ // ============================================
322
+ // Auto-Refresh
323
+ // ============================================
324
+ setupAutoRefresh(sessionKeyId, decryptFn, options) {
325
+ const existingTimer = this.refreshTimers.get(sessionKeyId);
326
+ if (existingTimer) {
327
+ clearTimeout(existingTimer);
328
+ }
329
+ const refreshIn = Math.max(0, this.config.ttl - 5 * 60 * 1e3);
330
+ const timer = setTimeout(async () => {
331
+ this.log(`Auto-refreshing: ${sessionKeyId}`);
332
+ try {
333
+ await this.decryptAndCache(sessionKeyId, decryptFn, options);
334
+ } catch (error) {
335
+ this.log(`Auto-refresh failed: ${error}`);
336
+ }
337
+ }, refreshIn);
338
+ this.refreshTimers.set(sessionKeyId, timer);
339
+ }
340
+ // ============================================
341
+ // Utilities
342
+ // ============================================
343
+ getStorageKey(sessionKeyId) {
344
+ return `${this.config.namespace}:${sessionKeyId}`;
345
+ }
346
+ log(message) {
347
+ if (this.config.debug) {
348
+ console.log(`[SessionKeyCache] ${message}`);
349
+ }
350
+ }
351
+ };
352
+ var QuickCaches = {
353
+ /** Memory-only cache (30 minutes) */
354
+ memory: () => new SessionKeyCache({ storage: "memory", ttl: 30 * 60 * 1e3 }),
355
+ /** Persistent cache (1 hour, survives reload) */
356
+ persistent: () => new SessionKeyCache({ storage: "localStorage", ttl: 60 * 60 * 1e3 }),
357
+ /** Long-term cache (24 hours, IndexedDB) */
358
+ longTerm: () => new SessionKeyCache({ storage: "indexedDB", ttl: 24 * 60 * 60 * 1e3, autoRefresh: true }),
359
+ /** Secure cache (5 minutes, memory-only) */
360
+ secure: () => new SessionKeyCache({ storage: "memory", ttl: 5 * 60 * 1e3 })
361
+ };
362
+
363
+ export {
364
+ SessionKeyCache,
365
+ QuickCaches
366
+ };