@routstr/sdk 0.1.0 → 0.1.2

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,846 @@
1
+ import { createStore } from 'zustand/vanilla';
2
+ import { getDecodedToken } from '@cashu/cashu-ts';
3
+
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
10
+
11
+ // storage/drivers/localStorage.ts
12
+ var canUseLocalStorage = () => {
13
+ return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
14
+ };
15
+ var isQuotaExceeded = (error) => {
16
+ const e = error;
17
+ return !!e && (e?.name === "QuotaExceededError" || e?.code === 22 || e?.code === 1014);
18
+ };
19
+ var NON_CRITICAL_KEYS = /* @__PURE__ */ new Set(["modelsFromAllProviders"]);
20
+ var localStorageDriver = {
21
+ async getItem(key, defaultValue) {
22
+ if (!canUseLocalStorage()) return defaultValue;
23
+ try {
24
+ const item = window.localStorage.getItem(key);
25
+ if (item === null) return defaultValue;
26
+ try {
27
+ return JSON.parse(item);
28
+ } catch (parseError) {
29
+ if (typeof defaultValue === "string") {
30
+ return item;
31
+ }
32
+ throw parseError;
33
+ }
34
+ } catch (error) {
35
+ console.error(`Error retrieving item with key "${key}":`, error);
36
+ if (canUseLocalStorage()) {
37
+ try {
38
+ window.localStorage.removeItem(key);
39
+ } catch (removeError) {
40
+ console.error(
41
+ `Error removing corrupted item with key "${key}":`,
42
+ removeError
43
+ );
44
+ }
45
+ }
46
+ return defaultValue;
47
+ }
48
+ },
49
+ async setItem(key, value) {
50
+ if (!canUseLocalStorage()) return;
51
+ try {
52
+ window.localStorage.setItem(key, JSON.stringify(value));
53
+ } catch (error) {
54
+ if (isQuotaExceeded(error)) {
55
+ if (NON_CRITICAL_KEYS.has(key)) {
56
+ console.warn(
57
+ `Storage quota exceeded; skipping non-critical key "${key}".`
58
+ );
59
+ return;
60
+ }
61
+ try {
62
+ window.localStorage.removeItem("modelsFromAllProviders");
63
+ } catch {
64
+ }
65
+ try {
66
+ window.localStorage.setItem(key, JSON.stringify(value));
67
+ return;
68
+ } catch (retryError) {
69
+ console.warn(
70
+ `Storage quota exceeded; unable to persist key "${key}" after cleanup attempt.`,
71
+ retryError
72
+ );
73
+ return;
74
+ }
75
+ }
76
+ console.error(`Error storing item with key "${key}":`, error);
77
+ }
78
+ },
79
+ async removeItem(key) {
80
+ if (!canUseLocalStorage()) return;
81
+ try {
82
+ window.localStorage.removeItem(key);
83
+ } catch (error) {
84
+ console.error(`Error removing item with key "${key}":`, error);
85
+ }
86
+ }
87
+ };
88
+
89
+ // storage/drivers/memory.ts
90
+ var createMemoryDriver = (seed) => {
91
+ const store = /* @__PURE__ */ new Map();
92
+ if (seed) {
93
+ for (const [key, value] of Object.entries(seed)) {
94
+ store.set(key, value);
95
+ }
96
+ }
97
+ return {
98
+ async getItem(key, defaultValue) {
99
+ const item = store.get(key);
100
+ if (item === void 0) return defaultValue;
101
+ try {
102
+ return JSON.parse(item);
103
+ } catch (parseError) {
104
+ if (typeof defaultValue === "string") {
105
+ return item;
106
+ }
107
+ throw parseError;
108
+ }
109
+ },
110
+ async setItem(key, value) {
111
+ store.set(key, JSON.stringify(value));
112
+ },
113
+ async removeItem(key) {
114
+ store.delete(key);
115
+ }
116
+ };
117
+ };
118
+
119
+ // storage/drivers/sqlite.ts
120
+ var isBun = () => {
121
+ return typeof process.versions.bun !== "undefined";
122
+ };
123
+ var createDatabase = (dbPath) => {
124
+ if (isBun()) {
125
+ throw new Error(
126
+ "SQLite driver not supported in Bun. Use createMemoryDriver() instead."
127
+ );
128
+ }
129
+ let Database = null;
130
+ try {
131
+ Database = __require("better-sqlite3");
132
+ } catch (error) {
133
+ throw new Error(
134
+ `better-sqlite3 is required for sqlite storage. Install it to use sqlite storage. (${error})`
135
+ );
136
+ }
137
+ return new Database(dbPath);
138
+ };
139
+ var createSqliteDriver = (options = {}) => {
140
+ const dbPath = options.dbPath || "routstr.sqlite";
141
+ const tableName = options.tableName || "sdk_storage";
142
+ const db = createDatabase(dbPath);
143
+ db.exec(
144
+ `CREATE TABLE IF NOT EXISTS ${tableName} (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
145
+ );
146
+ const selectStmt = db.prepare(`SELECT value FROM ${tableName} WHERE key = ?`);
147
+ const upsertStmt = db.prepare(
148
+ `INSERT INTO ${tableName} (key, value) VALUES (?, ?)
149
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`
150
+ );
151
+ const deleteStmt = db.prepare(`DELETE FROM ${tableName} WHERE key = ?`);
152
+ return {
153
+ async getItem(key, defaultValue) {
154
+ try {
155
+ const row = selectStmt.get(key);
156
+ if (!row || typeof row.value !== "string") return defaultValue;
157
+ try {
158
+ return JSON.parse(row.value);
159
+ } catch (parseError) {
160
+ if (typeof defaultValue === "string") {
161
+ return row.value;
162
+ }
163
+ throw parseError;
164
+ }
165
+ } catch (error) {
166
+ console.error(`SQLite getItem failed for key "${key}":`, error);
167
+ return defaultValue;
168
+ }
169
+ },
170
+ async setItem(key, value) {
171
+ try {
172
+ upsertStmt.run(key, JSON.stringify(value));
173
+ } catch (error) {
174
+ console.error(`SQLite setItem failed for key "${key}":`, error);
175
+ }
176
+ },
177
+ async removeItem(key) {
178
+ try {
179
+ deleteStmt.run(key);
180
+ } catch (error) {
181
+ console.error(`SQLite removeItem failed for key "${key}":`, error);
182
+ }
183
+ }
184
+ };
185
+ };
186
+
187
+ // storage/drivers/indexedDB.ts
188
+ var openDatabase = (dbName, storeName) => {
189
+ return new Promise((resolve, reject) => {
190
+ const request = indexedDB.open(dbName, 1);
191
+ request.onupgradeneeded = () => {
192
+ const db = request.result;
193
+ if (!db.objectStoreNames.contains(storeName)) {
194
+ db.createObjectStore(storeName);
195
+ }
196
+ };
197
+ request.onsuccess = () => resolve(request.result);
198
+ request.onerror = () => reject(request.error);
199
+ });
200
+ };
201
+ var createIndexedDBDriver = (options = {}) => {
202
+ const dbName = options.dbName || "routstr-sdk";
203
+ const storeName = options.storeName || "sdk_storage";
204
+ let dbPromise = null;
205
+ const getDb = () => {
206
+ if (!dbPromise) {
207
+ dbPromise = openDatabase(dbName, storeName);
208
+ }
209
+ return dbPromise;
210
+ };
211
+ return {
212
+ async getItem(key, defaultValue) {
213
+ try {
214
+ const db = await getDb();
215
+ return new Promise((resolve, reject) => {
216
+ const tx = db.transaction(storeName, "readonly");
217
+ const store = tx.objectStore(storeName);
218
+ const request = store.get(key);
219
+ request.onsuccess = () => {
220
+ const raw = request.result;
221
+ if (raw === void 0) {
222
+ resolve(defaultValue);
223
+ return;
224
+ }
225
+ if (typeof raw === "string") {
226
+ try {
227
+ resolve(JSON.parse(raw));
228
+ } catch {
229
+ if (typeof defaultValue === "string") {
230
+ resolve(raw);
231
+ } else {
232
+ resolve(defaultValue);
233
+ }
234
+ }
235
+ } else {
236
+ resolve(raw);
237
+ }
238
+ };
239
+ request.onerror = () => reject(request.error);
240
+ });
241
+ } catch (error) {
242
+ console.error(`IndexedDB getItem failed for key "${key}":`, error);
243
+ return defaultValue;
244
+ }
245
+ },
246
+ async setItem(key, value) {
247
+ try {
248
+ const db = await getDb();
249
+ return new Promise((resolve, reject) => {
250
+ const tx = db.transaction(storeName, "readwrite");
251
+ const store = tx.objectStore(storeName);
252
+ store.put(JSON.stringify(value), key);
253
+ tx.oncomplete = () => resolve();
254
+ tx.onerror = () => reject(tx.error);
255
+ });
256
+ } catch (error) {
257
+ console.error(`IndexedDB setItem failed for key "${key}":`, error);
258
+ }
259
+ },
260
+ async removeItem(key) {
261
+ try {
262
+ const db = await getDb();
263
+ return new Promise((resolve, reject) => {
264
+ const tx = db.transaction(storeName, "readwrite");
265
+ const store = tx.objectStore(storeName);
266
+ store.delete(key);
267
+ tx.oncomplete = () => resolve();
268
+ tx.onerror = () => reject(tx.error);
269
+ });
270
+ } catch (error) {
271
+ console.error(`IndexedDB removeItem failed for key "${key}":`, error);
272
+ }
273
+ }
274
+ };
275
+ };
276
+
277
+ // storage/keys.ts
278
+ var SDK_STORAGE_KEYS = {
279
+ MODELS_FROM_ALL_PROVIDERS: "modelsFromAllProviders",
280
+ LAST_USED_MODEL: "lastUsedModel",
281
+ BASE_URLS_LIST: "base_urls_list",
282
+ DISABLED_PROVIDERS: "disabled_providers",
283
+ MINTS_FROM_ALL_PROVIDERS: "mints_from_all_providers",
284
+ INFO_FROM_ALL_PROVIDERS: "info_from_all_providers",
285
+ LAST_MODELS_UPDATE: "lastModelsUpdate",
286
+ LAST_BASE_URLS_UPDATE: "lastBaseUrlsUpdate",
287
+ LOCAL_CASHU_TOKENS: "local_cashu_tokens",
288
+ API_KEYS: "api_keys",
289
+ CHILD_KEYS: "child_keys",
290
+ ROUTSTR21_MODELS: "routstr21Models",
291
+ CACHED_RECEIVE_TOKENS: "cached_receive_tokens"
292
+ };
293
+
294
+ // storage/store.ts
295
+ var normalizeBaseUrl = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
296
+ var getTokenBalance = (token) => {
297
+ try {
298
+ const decoded = getDecodedToken(token);
299
+ const unitDivisor = decoded.unit === "msat" ? 1e3 : 1;
300
+ let sum = 0;
301
+ for (const proof of decoded.proofs) {
302
+ sum += proof.amount / unitDivisor;
303
+ }
304
+ return sum;
305
+ } catch {
306
+ return 0;
307
+ }
308
+ };
309
+ var createSdkStore = async ({
310
+ driver
311
+ }) => {
312
+ const [
313
+ rawModels,
314
+ lastUsedModel,
315
+ rawBaseUrls,
316
+ lastBaseUrlsUpdate,
317
+ rawDisabledProviders,
318
+ rawMints,
319
+ rawInfo,
320
+ rawLastModelsUpdate,
321
+ rawCachedTokens,
322
+ rawApiKeys,
323
+ rawChildKeys,
324
+ rawRoutstr21Models,
325
+ rawCachedReceiveTokens
326
+ ] = await Promise.all([
327
+ driver.getItem(
328
+ SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
329
+ {}
330
+ ),
331
+ driver.getItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, null),
332
+ driver.getItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, []),
333
+ driver.getItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, null),
334
+ driver.getItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, []),
335
+ driver.getItem(
336
+ SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS,
337
+ {}
338
+ ),
339
+ driver.getItem(
340
+ SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS,
341
+ {}
342
+ ),
343
+ driver.getItem(
344
+ SDK_STORAGE_KEYS.LAST_MODELS_UPDATE,
345
+ {}
346
+ ),
347
+ driver.getItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, []),
348
+ driver.getItem(SDK_STORAGE_KEYS.API_KEYS, []),
349
+ driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, []),
350
+ driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
351
+ driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, [])
352
+ ]);
353
+ const modelsFromAllProviders = Object.fromEntries(
354
+ Object.entries(rawModels).map(([baseUrl, models]) => [
355
+ normalizeBaseUrl(baseUrl),
356
+ models
357
+ ])
358
+ );
359
+ const baseUrlsList = rawBaseUrls.map((url) => normalizeBaseUrl(url));
360
+ const disabledProviders = rawDisabledProviders.map(
361
+ (url) => normalizeBaseUrl(url)
362
+ );
363
+ const mintsFromAllProviders = Object.fromEntries(
364
+ Object.entries(rawMints).map(([baseUrl, mints]) => [
365
+ normalizeBaseUrl(baseUrl),
366
+ mints.map((mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint)
367
+ ])
368
+ );
369
+ const infoFromAllProviders = Object.fromEntries(
370
+ Object.entries(rawInfo).map(([baseUrl, info]) => [
371
+ normalizeBaseUrl(baseUrl),
372
+ info
373
+ ])
374
+ );
375
+ const lastModelsUpdate = Object.fromEntries(
376
+ Object.entries(rawLastModelsUpdate).map(([baseUrl, timestamp]) => [
377
+ normalizeBaseUrl(baseUrl),
378
+ timestamp
379
+ ])
380
+ );
381
+ const cachedTokens = rawCachedTokens.map((entry) => ({
382
+ ...entry,
383
+ baseUrl: normalizeBaseUrl(entry.baseUrl),
384
+ balance: typeof entry.balance === "number" ? entry.balance : getTokenBalance(entry.token),
385
+ lastUsed: entry.lastUsed ?? null
386
+ }));
387
+ const apiKeys = rawApiKeys.map((entry) => ({
388
+ ...entry,
389
+ baseUrl: normalizeBaseUrl(entry.baseUrl),
390
+ balance: entry.balance ?? 0,
391
+ lastUsed: entry.lastUsed ?? null
392
+ }));
393
+ const childKeys = rawChildKeys.map((entry) => ({
394
+ parentBaseUrl: normalizeBaseUrl(entry.parentBaseUrl),
395
+ childKey: entry.childKey,
396
+ balance: entry.balance ?? 0,
397
+ balanceLimit: entry.balanceLimit,
398
+ validityDate: entry.validityDate,
399
+ createdAt: entry.createdAt ?? Date.now()
400
+ }));
401
+ const routstr21Models = rawRoutstr21Models;
402
+ const cachedReceiveTokens = rawCachedReceiveTokens.map((entry) => ({
403
+ token: entry.token,
404
+ amount: entry.amount,
405
+ unit: entry.unit || "sat",
406
+ createdAt: entry.createdAt ?? Date.now()
407
+ }));
408
+ return createStore((set, get) => ({
409
+ modelsFromAllProviders,
410
+ lastUsedModel,
411
+ baseUrlsList,
412
+ lastBaseUrlsUpdate,
413
+ disabledProviders,
414
+ mintsFromAllProviders,
415
+ infoFromAllProviders,
416
+ lastModelsUpdate,
417
+ cachedTokens,
418
+ apiKeys,
419
+ childKeys,
420
+ routstr21Models,
421
+ cachedReceiveTokens,
422
+ setModelsFromAllProviders: (value) => {
423
+ const normalized = {};
424
+ for (const [baseUrl, models] of Object.entries(value)) {
425
+ normalized[normalizeBaseUrl(baseUrl)] = models;
426
+ }
427
+ void driver.setItem(
428
+ SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
429
+ normalized
430
+ );
431
+ set({ modelsFromAllProviders: normalized });
432
+ },
433
+ setLastUsedModel: (value) => {
434
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, value);
435
+ set({ lastUsedModel: value });
436
+ },
437
+ setBaseUrlsList: (value) => {
438
+ const normalized = value.map((url) => normalizeBaseUrl(url));
439
+ void driver.setItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, normalized);
440
+ set({ baseUrlsList: normalized });
441
+ },
442
+ setBaseUrlsLastUpdate: (value) => {
443
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, value);
444
+ set({ lastBaseUrlsUpdate: value });
445
+ },
446
+ setDisabledProviders: (value) => {
447
+ const normalized = value.map((url) => normalizeBaseUrl(url));
448
+ void driver.setItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, normalized);
449
+ set({ disabledProviders: normalized });
450
+ },
451
+ setMintsFromAllProviders: (value) => {
452
+ const normalized = {};
453
+ for (const [baseUrl, mints] of Object.entries(value)) {
454
+ normalized[normalizeBaseUrl(baseUrl)] = mints.map(
455
+ (mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint
456
+ );
457
+ }
458
+ void driver.setItem(
459
+ SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS,
460
+ normalized
461
+ );
462
+ set({ mintsFromAllProviders: normalized });
463
+ },
464
+ setInfoFromAllProviders: (value) => {
465
+ const normalized = {};
466
+ for (const [baseUrl, info] of Object.entries(value)) {
467
+ normalized[normalizeBaseUrl(baseUrl)] = info;
468
+ }
469
+ void driver.setItem(SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS, normalized);
470
+ set({ infoFromAllProviders: normalized });
471
+ },
472
+ setLastModelsUpdate: (value) => {
473
+ const normalized = {};
474
+ for (const [baseUrl, timestamp] of Object.entries(value)) {
475
+ normalized[normalizeBaseUrl(baseUrl)] = timestamp;
476
+ }
477
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE, normalized);
478
+ set({ lastModelsUpdate: normalized });
479
+ },
480
+ setCachedTokens: (value) => {
481
+ set((state) => {
482
+ const updates = typeof value === "function" ? value(state.cachedTokens) : value;
483
+ const normalized = updates.map((entry) => ({
484
+ ...entry,
485
+ baseUrl: normalizeBaseUrl(entry.baseUrl),
486
+ balance: typeof entry.balance === "number" ? entry.balance : getTokenBalance(entry.token),
487
+ lastUsed: entry.lastUsed ?? null
488
+ }));
489
+ void driver.setItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, normalized);
490
+ return { cachedTokens: normalized };
491
+ });
492
+ },
493
+ setApiKeys: (value) => {
494
+ set((state) => {
495
+ const updates = typeof value === "function" ? value(state.apiKeys) : value;
496
+ const normalized = updates.map((entry) => ({
497
+ ...entry,
498
+ baseUrl: normalizeBaseUrl(entry.baseUrl),
499
+ balance: entry.balance ?? 0,
500
+ lastUsed: entry.lastUsed ?? null
501
+ }));
502
+ void driver.setItem(SDK_STORAGE_KEYS.API_KEYS, normalized);
503
+ return { apiKeys: normalized };
504
+ });
505
+ },
506
+ setChildKeys: (value) => {
507
+ set((state) => {
508
+ const updates = typeof value === "function" ? value(state.childKeys) : value;
509
+ const normalized = updates.map((entry) => ({
510
+ parentBaseUrl: normalizeBaseUrl(entry.parentBaseUrl),
511
+ childKey: entry.childKey,
512
+ balance: entry.balance ?? 0,
513
+ balanceLimit: entry.balanceLimit,
514
+ validityDate: entry.validityDate,
515
+ createdAt: entry.createdAt ?? Date.now()
516
+ }));
517
+ void driver.setItem(SDK_STORAGE_KEYS.CHILD_KEYS, normalized);
518
+ return { childKeys: normalized };
519
+ });
520
+ },
521
+ setRoutstr21Models: (value) => {
522
+ void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, value);
523
+ set({ routstr21Models: value });
524
+ },
525
+ setCachedReceiveTokens: (value) => {
526
+ const normalized = value.map((entry) => ({
527
+ token: entry.token,
528
+ amount: entry.amount,
529
+ unit: entry.unit || "sat",
530
+ createdAt: entry.createdAt ?? Date.now()
531
+ }));
532
+ void driver.setItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, normalized);
533
+ set({ cachedReceiveTokens: normalized });
534
+ }
535
+ }));
536
+ };
537
+ var createDiscoveryAdapterFromStore = (store) => ({
538
+ getCachedModels: () => store.getState().modelsFromAllProviders,
539
+ setCachedModels: (models) => store.getState().setModelsFromAllProviders(models),
540
+ getCachedMints: () => store.getState().mintsFromAllProviders,
541
+ setCachedMints: (mints) => store.getState().setMintsFromAllProviders(mints),
542
+ getCachedProviderInfo: () => store.getState().infoFromAllProviders,
543
+ setCachedProviderInfo: (info) => store.getState().setInfoFromAllProviders(info),
544
+ getProviderLastUpdate: (baseUrl) => {
545
+ const normalized = normalizeBaseUrl(baseUrl);
546
+ const timestamps = store.getState().lastModelsUpdate;
547
+ return timestamps[normalized] || null;
548
+ },
549
+ setProviderLastUpdate: (baseUrl, timestamp) => {
550
+ const normalized = normalizeBaseUrl(baseUrl);
551
+ const timestamps = { ...store.getState().lastModelsUpdate };
552
+ timestamps[normalized] = timestamp;
553
+ store.getState().setLastModelsUpdate(timestamps);
554
+ },
555
+ getLastUsedModel: () => store.getState().lastUsedModel,
556
+ setLastUsedModel: (modelId) => store.getState().setLastUsedModel(modelId),
557
+ getDisabledProviders: () => store.getState().disabledProviders,
558
+ getBaseUrlsList: () => store.getState().baseUrlsList,
559
+ setBaseUrlsList: (urls) => store.getState().setBaseUrlsList(urls),
560
+ getBaseUrlsLastUpdate: () => store.getState().lastBaseUrlsUpdate,
561
+ setBaseUrlsLastUpdate: (timestamp) => store.getState().setBaseUrlsLastUpdate(timestamp),
562
+ getRoutstr21Models: () => store.getState().routstr21Models,
563
+ setRoutstr21Models: (models) => store.getState().setRoutstr21Models(models)
564
+ });
565
+ var createStorageAdapterFromStore = (store) => ({
566
+ getToken: (baseUrl) => {
567
+ const normalized = normalizeBaseUrl(baseUrl);
568
+ const entry = store.getState().cachedTokens.find((token) => token.baseUrl === normalized);
569
+ if (!entry) return null;
570
+ const next = store.getState().cachedTokens.map(
571
+ (token) => token.baseUrl === normalized ? { ...token, lastUsed: Date.now() } : token
572
+ );
573
+ store.getState().setCachedTokens(next);
574
+ return entry.token;
575
+ },
576
+ setToken: (baseUrl, token) => {
577
+ const normalized = normalizeBaseUrl(baseUrl);
578
+ const tokens = store.getState().cachedTokens;
579
+ const balance = getTokenBalance(token);
580
+ const existingIndex = tokens.findIndex(
581
+ (entry) => entry.baseUrl === normalized
582
+ );
583
+ if (existingIndex !== -1) {
584
+ throw new Error(`Token already exists for baseUrl: ${normalized}`);
585
+ }
586
+ const next = [...tokens];
587
+ next.push({
588
+ baseUrl: normalized,
589
+ token,
590
+ balance,
591
+ lastUsed: Date.now()
592
+ });
593
+ store.getState().setCachedTokens(next);
594
+ },
595
+ removeToken: (baseUrl) => {
596
+ const normalized = normalizeBaseUrl(baseUrl);
597
+ const next = store.getState().cachedTokens.filter((entry) => entry.baseUrl !== normalized);
598
+ store.getState().setCachedTokens(next);
599
+ },
600
+ updateTokenBalance: (baseUrl, balance) => {
601
+ const normalized = normalizeBaseUrl(baseUrl);
602
+ const tokens = store.getState().cachedTokens;
603
+ const next = tokens.map(
604
+ (entry) => entry.baseUrl === normalized ? { ...entry, balance } : entry
605
+ );
606
+ store.getState().setCachedTokens(next);
607
+ },
608
+ getCachedTokenDistribution: () => {
609
+ const cachedTokens = store.getState().cachedTokens;
610
+ const distributionMap = {};
611
+ for (const entry of cachedTokens) {
612
+ const sum = entry.balance || 0;
613
+ if (sum > 0) {
614
+ distributionMap[entry.baseUrl] = (distributionMap[entry.baseUrl] || 0) + sum;
615
+ }
616
+ }
617
+ return Object.entries(distributionMap).map(([baseUrl, amt]) => ({ baseUrl, amount: amt })).sort((a, b) => b.amount - a.amount);
618
+ },
619
+ getApiKeyDistribution: () => {
620
+ const apiKeys = store.getState().apiKeys;
621
+ const distributionMap = {};
622
+ for (const entry of apiKeys) {
623
+ const sum = entry.balance || 0;
624
+ if (sum > 0) {
625
+ distributionMap[entry.baseUrl] = (distributionMap[entry.baseUrl] || 0) + sum;
626
+ }
627
+ }
628
+ return Object.entries(distributionMap).map(([baseUrl, amt]) => ({ baseUrl, amount: amt })).sort((a, b) => b.amount - a.amount);
629
+ },
630
+ saveProviderInfo: (baseUrl, info) => {
631
+ const normalized = normalizeBaseUrl(baseUrl);
632
+ const next = { ...store.getState().infoFromAllProviders };
633
+ next[normalized] = info;
634
+ store.getState().setInfoFromAllProviders(next);
635
+ },
636
+ getProviderInfo: (baseUrl) => {
637
+ const normalized = normalizeBaseUrl(baseUrl);
638
+ return store.getState().infoFromAllProviders[normalized] || null;
639
+ },
640
+ // ========== API Keys (for apikeys mode) ==========
641
+ getApiKey: (baseUrl) => {
642
+ const normalized = normalizeBaseUrl(baseUrl);
643
+ const entry = store.getState().apiKeys.find((key) => key.baseUrl === normalized);
644
+ if (!entry) return null;
645
+ const next = store.getState().apiKeys.map(
646
+ (key) => key.baseUrl === normalized ? { ...key, lastUsed: Date.now() } : key
647
+ );
648
+ store.getState().setApiKeys(next);
649
+ return entry;
650
+ },
651
+ setApiKey: (baseUrl, key) => {
652
+ const normalized = normalizeBaseUrl(baseUrl);
653
+ const keys = store.getState().apiKeys;
654
+ const existingIndex = keys.findIndex(
655
+ (entry) => entry.baseUrl === normalized
656
+ );
657
+ if (existingIndex !== -1) {
658
+ throw new Error(`ApiKey already exists for baseUrl: ${normalized}`);
659
+ }
660
+ const next = [...keys];
661
+ next.push({
662
+ baseUrl: normalized,
663
+ key,
664
+ balance: 0,
665
+ lastUsed: Date.now()
666
+ });
667
+ store.getState().setApiKeys(next);
668
+ },
669
+ updateApiKeyBalance: (baseUrl, balance) => {
670
+ const normalized = normalizeBaseUrl(baseUrl);
671
+ const keys = store.getState().apiKeys;
672
+ const next = keys.map(
673
+ (entry) => entry.baseUrl === normalized ? { ...entry, balance } : entry
674
+ );
675
+ store.getState().setApiKeys(next);
676
+ },
677
+ removeApiKey: (baseUrl) => {
678
+ const normalized = normalizeBaseUrl(baseUrl);
679
+ const next = store.getState().apiKeys.filter((entry) => entry.baseUrl !== normalized);
680
+ store.getState().setApiKeys(next);
681
+ },
682
+ getAllApiKeys: () => {
683
+ return store.getState().apiKeys.map((entry) => ({
684
+ baseUrl: entry.baseUrl,
685
+ key: entry.key,
686
+ balance: entry.balance,
687
+ lastUsed: entry.lastUsed
688
+ }));
689
+ },
690
+ // ========== Child Keys ==========
691
+ getChildKey: (parentBaseUrl) => {
692
+ const normalized = normalizeBaseUrl(parentBaseUrl);
693
+ const entry = store.getState().childKeys.find((key) => key.parentBaseUrl === normalized);
694
+ if (!entry) return null;
695
+ return {
696
+ parentBaseUrl: entry.parentBaseUrl,
697
+ childKey: entry.childKey,
698
+ balance: entry.balance,
699
+ balanceLimit: entry.balanceLimit,
700
+ validityDate: entry.validityDate,
701
+ createdAt: entry.createdAt
702
+ };
703
+ },
704
+ setChildKey: (parentBaseUrl, childKey, balance, validityDate, balanceLimit) => {
705
+ const normalized = normalizeBaseUrl(parentBaseUrl);
706
+ const keys = store.getState().childKeys;
707
+ const existingIndex = keys.findIndex(
708
+ (entry) => entry.parentBaseUrl === normalized
709
+ );
710
+ if (existingIndex !== -1) {
711
+ const next = keys.map(
712
+ (entry) => entry.parentBaseUrl === normalized ? {
713
+ ...entry,
714
+ childKey,
715
+ balance: balance ?? 0,
716
+ validityDate,
717
+ balanceLimit,
718
+ createdAt: Date.now()
719
+ } : entry
720
+ );
721
+ store.getState().setChildKeys(next);
722
+ } else {
723
+ const next = [...keys];
724
+ next.push({
725
+ parentBaseUrl: normalized,
726
+ childKey,
727
+ balance: balance ?? 0,
728
+ validityDate,
729
+ balanceLimit,
730
+ createdAt: Date.now()
731
+ });
732
+ store.getState().setChildKeys(next);
733
+ }
734
+ },
735
+ updateChildKeyBalance: (parentBaseUrl, balance) => {
736
+ const normalized = normalizeBaseUrl(parentBaseUrl);
737
+ const keys = store.getState().childKeys;
738
+ const next = keys.map(
739
+ (entry) => entry.parentBaseUrl === normalized ? { ...entry, balance } : entry
740
+ );
741
+ store.getState().setChildKeys(next);
742
+ },
743
+ removeChildKey: (parentBaseUrl) => {
744
+ const normalized = normalizeBaseUrl(parentBaseUrl);
745
+ const next = store.getState().childKeys.filter((entry) => entry.parentBaseUrl !== normalized);
746
+ store.getState().setChildKeys(next);
747
+ },
748
+ getAllChildKeys: () => {
749
+ return store.getState().childKeys.map((entry) => ({
750
+ parentBaseUrl: entry.parentBaseUrl,
751
+ childKey: entry.childKey,
752
+ balance: entry.balance,
753
+ balanceLimit: entry.balanceLimit,
754
+ validityDate: entry.validityDate,
755
+ createdAt: entry.createdAt
756
+ }));
757
+ },
758
+ getCachedReceiveTokens: () => {
759
+ return store.getState().cachedReceiveTokens;
760
+ },
761
+ setCachedReceiveTokens: (tokens) => {
762
+ store.getState().setCachedReceiveTokens(tokens);
763
+ }
764
+ });
765
+ var createProviderRegistryFromStore = (store) => ({
766
+ getModelsForProvider: (baseUrl) => {
767
+ const normalized = normalizeBaseUrl(baseUrl);
768
+ return store.getState().modelsFromAllProviders[normalized] || [];
769
+ },
770
+ getDisabledProviders: () => store.getState().disabledProviders,
771
+ getProviderMints: (baseUrl) => {
772
+ const normalized = normalizeBaseUrl(baseUrl);
773
+ return store.getState().mintsFromAllProviders[normalized] || [];
774
+ },
775
+ getProviderInfo: async (baseUrl) => {
776
+ const normalized = normalizeBaseUrl(baseUrl);
777
+ const cached = store.getState().infoFromAllProviders[normalized];
778
+ if (cached) return cached;
779
+ try {
780
+ const response = await fetch(`${normalized}v1/info`);
781
+ if (!response.ok) {
782
+ throw new Error(`Failed ${response.status}`);
783
+ }
784
+ const info = await response.json();
785
+ const next = { ...store.getState().infoFromAllProviders };
786
+ next[normalized] = info;
787
+ store.getState().setInfoFromAllProviders(next);
788
+ return info;
789
+ } catch (error) {
790
+ console.warn(`Failed to fetch provider info from ${normalized}:`, error);
791
+ return null;
792
+ }
793
+ },
794
+ getAllProvidersModels: () => store.getState().modelsFromAllProviders
795
+ });
796
+
797
+ // storage/index.ts
798
+ var isBrowser = () => {
799
+ try {
800
+ return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
801
+ } catch {
802
+ return false;
803
+ }
804
+ };
805
+ var isNode = () => {
806
+ try {
807
+ return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
808
+ } catch {
809
+ return false;
810
+ }
811
+ };
812
+ var defaultDriver = null;
813
+ var isBun2 = () => {
814
+ return typeof process.versions.bun !== "undefined";
815
+ };
816
+ var getDefaultSdkDriver = () => {
817
+ if (defaultDriver) return defaultDriver;
818
+ if (isBrowser()) {
819
+ defaultDriver = localStorageDriver;
820
+ return defaultDriver;
821
+ }
822
+ if (isBun2()) {
823
+ defaultDriver = createMemoryDriver();
824
+ return defaultDriver;
825
+ }
826
+ if (isNode()) {
827
+ defaultDriver = createSqliteDriver();
828
+ return defaultDriver;
829
+ }
830
+ defaultDriver = createMemoryDriver();
831
+ return defaultDriver;
832
+ };
833
+ var defaultStorePromise = null;
834
+ var getDefaultSdkStore = () => {
835
+ if (!defaultStorePromise) {
836
+ defaultStorePromise = createSdkStore({ driver: getDefaultSdkDriver() });
837
+ }
838
+ return defaultStorePromise;
839
+ };
840
+ var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
841
+ var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
842
+ var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
843
+
844
+ export { SDK_STORAGE_KEYS, createDiscoveryAdapterFromStore, createIndexedDBDriver, createMemoryDriver, createProviderRegistryFromStore, createSdkStore, createSqliteDriver, createStorageAdapterFromStore, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, localStorageDriver };
845
+ //# sourceMappingURL=index.mjs.map
846
+ //# sourceMappingURL=index.mjs.map