@routstr/sdk 0.3.8 → 0.3.9

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/dist/index.mjs CHANGED
@@ -137,6 +137,9 @@ var MintDiscoveryError = class extends Error {
137
137
  }
138
138
  baseUrl;
139
139
  };
140
+ function isBunRuntime() {
141
+ return typeof Bun !== "undefined";
142
+ }
140
143
  var ModelManager = class _ModelManager {
141
144
  constructor(adapter, config = {}) {
142
145
  this.adapter = adapter;
@@ -146,6 +149,7 @@ var ModelManager = class _ModelManager {
146
149
  this.excludeProviderUrls = config.excludeProviderUrls || [];
147
150
  this.routstrPubkey = config.routstrPubkey || "4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8";
148
151
  this.logger = (config.logger ?? consoleLogger).child("ModelManager");
152
+ this.eventStoreDbPath = config.eventStoreDbPath;
149
153
  }
150
154
  adapter;
151
155
  cacheTTL;
@@ -155,6 +159,11 @@ var ModelManager = class _ModelManager {
155
159
  routstrPubkey;
156
160
  logger;
157
161
  providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
162
+ /** Persistent event store for relay-fetched events (null if not configured/initialized) */
163
+ eventStore = null;
164
+ eventStoreDb = null;
165
+ eventStoreInitPromise = null;
166
+ eventStoreDbPath;
158
167
  /**
159
168
  * Get the list of bootstrapped provider base URLs
160
169
  * @returns Array of provider base URLs
@@ -162,6 +171,104 @@ var ModelManager = class _ModelManager {
162
171
  getBaseUrls() {
163
172
  return this.adapter.getBaseUrlsList();
164
173
  }
174
+ /**
175
+ * Lazily initialize the persistent event store.
176
+ * Returns null if no eventStoreDbPath was provided.
177
+ */
178
+ async ensureEventStore() {
179
+ if (!this.eventStoreDbPath) return null;
180
+ if (this.eventStore) return this.eventStore;
181
+ if (!this.eventStoreInitPromise) {
182
+ this.eventStoreInitPromise = (async () => {
183
+ try {
184
+ const db = await this.createPersistentEventDatabase();
185
+ this.eventStoreDb = db;
186
+ this.eventStore = new EventStore({ database: db });
187
+ this.initializeEventStoreMetadata();
188
+ this.logger.log(
189
+ `Persistent event store initialized at ${this.eventStoreDbPath}`
190
+ );
191
+ return this.eventStore;
192
+ } catch (error) {
193
+ this.eventStoreInitPromise = null;
194
+ throw new Error(
195
+ `applesauce-sqlite with a supported SQLite driver is required for persistent Nostr event storage. Bun uses bun:sqlite; Node.js uses better-sqlite3. Install optional dependencies or omit eventStoreDbPath. (${error})`
196
+ );
197
+ }
198
+ })();
199
+ }
200
+ return this.eventStoreInitPromise;
201
+ }
202
+ /**
203
+ * Get the persistent event store, initializing it if configured.
204
+ * Returns null if no eventStoreDbPath was provided.
205
+ */
206
+ async getEventStore() {
207
+ return this.ensureEventStore();
208
+ }
209
+ async createPersistentEventDatabase() {
210
+ if (isBunRuntime()) {
211
+ const { BunSqliteEventDatabase } = await import('applesauce-sqlite/bun');
212
+ return new BunSqliteEventDatabase(
213
+ this.eventStoreDbPath
214
+ );
215
+ }
216
+ const { BetterSqlite3EventDatabase } = await import('applesauce-sqlite/better-sqlite3');
217
+ return new BetterSqlite3EventDatabase(
218
+ this.eventStoreDbPath
219
+ );
220
+ }
221
+ /** Close the persistent event store database handle, if configured. */
222
+ closeEventStore() {
223
+ this.eventStoreDb?.close?.();
224
+ this.eventStore = null;
225
+ this.eventStoreDb = null;
226
+ this.eventStoreInitPromise = null;
227
+ }
228
+ initializeEventStoreMetadata() {
229
+ this.eventStoreDb?.db?.exec(
230
+ `CREATE TABLE IF NOT EXISTS routstr_event_cache_metadata (
231
+ event_id TEXT PRIMARY KEY,
232
+ fetched_at INTEGER NOT NULL
233
+ )`
234
+ );
235
+ }
236
+ markEventFetched(event, fetchedAt = Date.now()) {
237
+ const db = this.eventStoreDb?.db;
238
+ if (!db) return;
239
+ db.prepare(
240
+ `INSERT INTO routstr_event_cache_metadata (event_id, fetched_at)
241
+ VALUES (?, ?)
242
+ ON CONFLICT(event_id) DO UPDATE SET fetched_at = excluded.fetched_at`
243
+ ).run?.(event.id, fetchedAt);
244
+ }
245
+ getEventFetchedAt(event) {
246
+ const db = this.eventStoreDb?.db;
247
+ if (!db) return void 0;
248
+ const row = db.prepare(
249
+ `SELECT fetched_at FROM routstr_event_cache_metadata WHERE event_id = ?`
250
+ ).get?.(event.id);
251
+ return typeof row?.fetched_at === "number" ? row.fetched_at : void 0;
252
+ }
253
+ /**
254
+ * Check the persistent event store for fresh cached events.
255
+ * Returns events from SQLite if they were fetched within `maxAge`, otherwise
256
+ * returns empty array (caller should hit relays). Events without local fetch
257
+ * metadata fall back to Nostr created_at for backwards compatibility.
258
+ */
259
+ async getCachedNostrEvents(filter, maxAge, forceRefresh = false) {
260
+ const eventStore = await this.ensureEventStore();
261
+ if (forceRefresh) return [];
262
+ if (!eventStore) return [];
263
+ const timeline = eventStore.getTimeline(filter);
264
+ if (timeline.length === 0) return [];
265
+ const cutoff = Date.now() - maxAge;
266
+ const freshest = Math.max(
267
+ ...timeline.map((e) => this.getEventFetchedAt(e) ?? e.created_at * 1e3)
268
+ );
269
+ if (freshest < cutoff) return [];
270
+ return timeline;
271
+ }
165
272
  static async init(adapter, config = {}, options = {}) {
166
273
  const manager = new _ModelManager(adapter, config);
167
274
  const torMode = options.torMode ?? false;
@@ -190,19 +297,31 @@ var ModelManager = class _ModelManager {
190
297
  torMode
191
298
  );
192
299
  await this.fetchRoutstr21Models(forceRefresh);
193
- await this.syncReviewedProvidersFromNostr(filteredCachedUrls);
300
+ await this.syncReviewedProvidersFromNostr(
301
+ filteredCachedUrls,
302
+ this.providerNodePubkeysByUrl,
303
+ forceRefresh
304
+ );
194
305
  return filteredCachedUrls;
195
306
  }
196
307
  }
197
308
  }
198
309
  try {
199
- const nostrProviders = await this.bootstrapFromNostr(38421, torMode);
310
+ const nostrProviders = await this.bootstrapFromNostr(
311
+ 38421,
312
+ torMode,
313
+ forceRefresh
314
+ );
200
315
  if (nostrProviders.length > 0) {
201
316
  const filtered = this.filterBaseUrlsForTor(nostrProviders, torMode);
202
317
  this.adapter.setBaseUrlsList(filtered);
203
318
  this.adapter.setBaseUrlsLastUpdate(Date.now());
204
319
  await this.fetchRoutstr21Models(forceRefresh);
205
- await this.syncReviewedProvidersFromNostr(filtered);
320
+ await this.syncReviewedProvidersFromNostr(
321
+ filtered,
322
+ this.providerNodePubkeysByUrl,
323
+ forceRefresh
324
+ );
206
325
  return filtered;
207
326
  }
208
327
  } catch (e) {
@@ -211,42 +330,52 @@ var ModelManager = class _ModelManager {
211
330
  return this.bootstrapFromHttp(torMode, forceRefresh);
212
331
  }
213
332
  /**
214
- * Bootstrap providers from Nostr network (kind 30421)
333
+ * Bootstrap providers from Nostr network (kind 38421)
215
334
  * @param kind The Nostr kind to fetch
216
335
  * @param torMode Whether running in Tor context
217
336
  * @returns Array of provider base URLs
218
337
  */
219
- async bootstrapFromNostr(kind, torMode) {
338
+ async bootstrapFromNostr(kind, torMode, forceRefresh = false) {
220
339
  const DEFAULT_RELAYS = [
221
340
  "wss://relay.primal.net",
222
341
  "wss://nos.lol",
223
342
  "wss://relay.damus.io"
224
343
  ];
225
- const pool = new RelayPool();
226
- const localEventStore = new EventStore();
227
- const timeoutMs = 5e3;
228
- await new Promise((resolve) => {
229
- pool.req(DEFAULT_RELAYS, {
230
- kinds: [kind],
231
- limit: 100
232
- }).pipe(
233
- onlyEvents(),
234
- tap((event) => {
235
- localEventStore.add(event);
236
- })
237
- ).subscribe({
238
- complete: () => {
344
+ const cached = await this.getCachedNostrEvents(
345
+ { kinds: [kind] },
346
+ this.cacheTTL,
347
+ forceRefresh
348
+ );
349
+ let sessionEvents = cached;
350
+ if (cached.length === 0) {
351
+ const pool = new RelayPool();
352
+ const timeoutMs = 5e3;
353
+ await new Promise((resolve) => {
354
+ pool.req(DEFAULT_RELAYS, {
355
+ kinds: [kind],
356
+ limit: 100
357
+ }).pipe(
358
+ onlyEvents(),
359
+ tap((event) => {
360
+ sessionEvents.push(event);
361
+ this.eventStore?.add(event);
362
+ this.markEventFetched(event);
363
+ })
364
+ ).subscribe({
365
+ complete: () => {
366
+ resolve();
367
+ }
368
+ });
369
+ setTimeout(() => {
239
370
  resolve();
240
- }
371
+ }, timeoutMs);
241
372
  });
242
- setTimeout(() => {
243
- resolve();
244
- }, timeoutMs);
245
- });
246
- const timeline = localEventStore.getTimeline({ kinds: [kind] });
373
+ } else {
374
+ this.logger.log(`Using ${cached.length} cached kind ${kind} events from persistent store`);
375
+ }
247
376
  const bases = /* @__PURE__ */ new Set();
248
377
  this.providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
249
- for (const event of timeline) {
378
+ for (const event of sessionEvents) {
250
379
  const eventUrls = [];
251
380
  for (const tag of event.tags) {
252
381
  if (tag[0] === "u" && typeof tag[1] === "string") {
@@ -354,7 +483,11 @@ var ModelManager = class _ModelManager {
354
483
  this.adapter.setBaseUrlsList(list);
355
484
  this.adapter.setBaseUrlsLastUpdate(Date.now());
356
485
  await this.fetchRoutstr21Models(forceRefresh);
357
- await this.syncReviewedProvidersFromNostr(list);
486
+ await this.syncReviewedProvidersFromNostr(
487
+ list,
488
+ this.providerNodePubkeysByUrl,
489
+ forceRefresh
490
+ );
358
491
  }
359
492
  return list;
360
493
  } catch (e) {
@@ -373,7 +506,7 @@ var ModelManager = class _ModelManager {
373
506
  * @param baseUrls Current provider base URLs to evaluate
374
507
  * @returns Array of provider base URLs disabled by the review set
375
508
  */
376
- async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl) {
509
+ async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl, forceRefresh = false) {
377
510
  if (baseUrls.length === 0) return [];
378
511
  if (!this.adapter.setDisabledProviders) {
379
512
  this.logger.warn(
@@ -381,30 +514,43 @@ var ModelManager = class _ModelManager {
381
514
  );
382
515
  return [];
383
516
  }
384
- const LGTM_RELAYS = [
385
- "wss://relay.primal.net",
386
- "wss://nos.lol",
387
- "wss://relay.damus.io",
388
- "wss://relay.routstr.com"
389
- ];
390
517
  const reviewedNodePubkeys = /* @__PURE__ */ new Set();
391
518
  {
392
- const pool = new RelayPool();
393
- const store = new EventStore();
394
- const timeoutMs = 5e3;
395
- await new Promise((resolve) => {
396
- pool.req(LGTM_RELAYS, {
397
- kinds: [38425],
398
- "#t": ["lgtm"],
399
- limit: 500,
400
- authors: [this.routstrPubkey]
401
- }).pipe(
402
- onlyEvents(),
403
- tap((event) => store.add(event))
404
- ).subscribe({ complete: () => resolve() });
405
- setTimeout(() => resolve(), timeoutMs);
406
- });
407
- for (const event of store.getTimeline({ kinds: [38425] })) {
519
+ const cached = await this.getCachedNostrEvents(
520
+ { kinds: [38425], "#t": ["lgtm"], authors: [this.routstrPubkey] },
521
+ this.cacheTTL,
522
+ forceRefresh
523
+ );
524
+ let sessionEvents = cached;
525
+ if (cached.length === 0) {
526
+ const LGTM_RELAYS = [
527
+ "wss://relay.primal.net",
528
+ "wss://nos.lol",
529
+ "wss://relay.damus.io",
530
+ "wss://relay.routstr.com"
531
+ ];
532
+ const pool = new RelayPool();
533
+ const timeoutMs = 5e3;
534
+ await new Promise((resolve) => {
535
+ pool.req(LGTM_RELAYS, {
536
+ kinds: [38425],
537
+ "#t": ["lgtm"],
538
+ limit: 500,
539
+ authors: [this.routstrPubkey]
540
+ }).pipe(
541
+ onlyEvents(),
542
+ tap((event) => {
543
+ sessionEvents.push(event);
544
+ this.eventStore?.add(event);
545
+ this.markEventFetched(event);
546
+ })
547
+ ).subscribe({ complete: () => resolve() });
548
+ setTimeout(() => resolve(), timeoutMs);
549
+ });
550
+ } else {
551
+ this.logger.log(`Using ${cached.length} cached kind 38425 events from persistent store`);
552
+ }
553
+ for (const event of sessionEvents) {
408
554
  const hasLgtmTag = event.tags.some(
409
555
  (tag) => tag[0] === "t" && tag[1]?.toLowerCase() === "lgtm"
410
556
  );
@@ -513,7 +659,7 @@ var ModelManager = class _ModelManager {
513
659
  if (this.isProviderDownError(error)) {
514
660
  this.logger.warn(`Provider ${base} is down right now.`);
515
661
  } else {
516
- this.logger.warn(`Failed to fetch models from ${base}:`, error);
662
+ this.logger.warn(`Provider ${base} unreachable: ${error.message}`);
517
663
  }
518
664
  this.adapter.setProviderLastUpdate(base, Date.now());
519
665
  return { success: false, base };
@@ -633,34 +779,44 @@ var ModelManager = class _ModelManager {
633
779
  "wss://nos.lol",
634
780
  "wss://relay.routstr.com"
635
781
  ];
636
- const pool = new RelayPool();
637
- const localEventStore = new EventStore();
638
- const timeoutMs = 5e3;
639
- await new Promise((resolve) => {
640
- pool.req(DEFAULT_RELAYS, {
641
- kinds: [38423],
642
- "#d": ["routstr-21-models"],
643
- limit: 1,
644
- authors: [this.routstrPubkey]
645
- }).pipe(
646
- onlyEvents(),
647
- tap((event2) => {
648
- localEventStore.add(event2);
649
- })
650
- ).subscribe({
651
- complete: () => {
782
+ const cached = await this.getCachedNostrEvents(
783
+ { kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrPubkey] },
784
+ this.cacheTTL,
785
+ forceRefresh
786
+ );
787
+ let sessionEvents = cached;
788
+ if (cached.length === 0) {
789
+ const pool = new RelayPool();
790
+ const timeoutMs = 5e3;
791
+ await new Promise((resolve) => {
792
+ pool.req(DEFAULT_RELAYS, {
793
+ kinds: [38423],
794
+ "#d": ["routstr-21-models"],
795
+ limit: 1,
796
+ authors: [this.routstrPubkey]
797
+ }).pipe(
798
+ onlyEvents(),
799
+ tap((event2) => {
800
+ sessionEvents.push(event2);
801
+ this.eventStore?.add(event2);
802
+ this.markEventFetched(event2);
803
+ })
804
+ ).subscribe({
805
+ complete: () => {
806
+ resolve();
807
+ }
808
+ });
809
+ setTimeout(() => {
652
810
  resolve();
653
- }
811
+ }, timeoutMs);
654
812
  });
655
- setTimeout(() => {
656
- resolve();
657
- }, timeoutMs);
658
- });
659
- const timeline = localEventStore.getTimeline({ kinds: [38423] });
660
- if (timeline.length === 0) {
813
+ } else {
814
+ this.logger.log(`Using ${cached.length} cached kind 38423 events from persistent store`);
815
+ }
816
+ if (sessionEvents.length === 0) {
661
817
  return cachedModels.length > 0 ? cachedModels : [];
662
818
  }
663
- const event = timeline[0];
819
+ const event = sessionEvents[0];
664
820
  try {
665
821
  const content = JSON.parse(event.content);
666
822
  const models = Array.isArray(content?.models) ? content.models : [];
@@ -1378,7 +1534,7 @@ var CashuSpender = class {
1378
1534
  });
1379
1535
  continue;
1380
1536
  }
1381
- if (balanceResult.amount >= 0) {
1537
+ if (balanceResult.amount >= 0 && !balanceResult.balanceUnknown) {
1382
1538
  const balanceSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.amount / 1e3) : balanceResult.amount;
1383
1539
  this.storageAdapter.updateApiKeyBalance(
1384
1540
  apiKeyEntry.baseUrl,
@@ -2127,17 +2283,24 @@ var BalanceManager = class _BalanceManager {
2127
2283
  this.logger.warn("getTokenBalance: FAILED", data);
2128
2284
  const isInvalidApiKey = response.status === 401 && data?.detail?.error?.code === "invalid_api_key" && data?.detail?.error?.message?.includes("proofs already spent");
2129
2285
  return {
2130
- amount: -1,
2286
+ amount: 0,
2131
2287
  reserved: data.reserved ?? 0,
2132
2288
  unit: "msat",
2133
2289
  apiKey: data.api_key,
2134
- isInvalidApiKey
2290
+ isInvalidApiKey,
2291
+ balanceUnknown: true
2135
2292
  };
2136
2293
  }
2137
2294
  } catch (error) {
2138
2295
  this.logger.error("getTokenBalance error", error);
2139
2296
  }
2140
- return { amount: -1, reserved: 0, unit: "sat", apiKey: "" };
2297
+ return {
2298
+ amount: 0,
2299
+ reserved: 0,
2300
+ unit: "sat",
2301
+ apiKey: "",
2302
+ balanceUnknown: true
2303
+ };
2141
2304
  }
2142
2305
  /**
2143
2306
  * Handle topup errors with specific error types
@@ -4684,6 +4847,277 @@ var createProviderRegistryFromStore = (store, logger) => {
4684
4847
  };
4685
4848
  };
4686
4849
 
4850
+ // storage/shardedDiscoveryAdapter.ts
4851
+ var MODEL_KEY_PREFIX = "models:provider:";
4852
+ var MODEL_TS_KEY_PREFIX = "models:provider_timestamp:";
4853
+ var PROVIDER_INDEX_KEY = "models:provider_index";
4854
+ var MIGRATION_MARKER_KEY4 = "models_sharded_migration_v1";
4855
+ var encodeBaseUrl = (baseUrl) => encodeURIComponent(baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
4856
+ var modelKey = (baseUrl) => `${MODEL_KEY_PREFIX}${encodeBaseUrl(baseUrl)}`;
4857
+ var modelTsKey = (baseUrl) => `${MODEL_TS_KEY_PREFIX}${encodeBaseUrl(baseUrl)}`;
4858
+ var normalizeBaseUrl6 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
4859
+ var createShardedDiscoveryAdapter = async (options) => {
4860
+ const { driver } = options;
4861
+ const legacyModels = await driver.getItem(SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS, {});
4862
+ const legacyTimestamps = await driver.getItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE, {});
4863
+ if (Object.keys(legacyModels).length > 0) {
4864
+ const migratedProviders = [];
4865
+ for (const [baseUrl, models] of Object.entries(legacyModels)) {
4866
+ const normalized = normalizeBaseUrl6(baseUrl);
4867
+ await driver.setItem(modelKey(normalized), models);
4868
+ const ts = legacyTimestamps[normalized] ?? Date.now();
4869
+ await driver.setItem(modelTsKey(normalized), ts);
4870
+ migratedProviders.push(normalized);
4871
+ }
4872
+ const existingIndex = await driver.getItem(
4873
+ PROVIDER_INDEX_KEY,
4874
+ []
4875
+ );
4876
+ const merged = [.../* @__PURE__ */ new Set([...existingIndex, ...migratedProviders])];
4877
+ await driver.setItem(PROVIDER_INDEX_KEY, merged);
4878
+ await driver.removeItem(SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS);
4879
+ await driver.removeItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE);
4880
+ }
4881
+ await driver.setItem(MIGRATION_MARKER_KEY4, true);
4882
+ const [
4883
+ rawMints,
4884
+ rawInfo,
4885
+ lastUsedModel,
4886
+ rawDisabled,
4887
+ rawBaseUrls,
4888
+ lastBaseUrlsUpdate,
4889
+ rawRoutstr21Models,
4890
+ lastRoutstr21ModelsUpdate
4891
+ ] = await Promise.all([
4892
+ driver.getItem(
4893
+ SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS,
4894
+ {}
4895
+ ),
4896
+ driver.getItem(
4897
+ SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS,
4898
+ {}
4899
+ ),
4900
+ driver.getItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, null),
4901
+ driver.getItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, []),
4902
+ driver.getItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, []),
4903
+ driver.getItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, null),
4904
+ driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
4905
+ driver.getItem(
4906
+ SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
4907
+ null
4908
+ )
4909
+ ]);
4910
+ const modelsByBaseUrl = /* @__PURE__ */ new Map();
4911
+ const timestampsByBaseUrl = /* @__PURE__ */ new Map();
4912
+ const providerIndex = /* @__PURE__ */ new Set();
4913
+ const knownProviders = /* @__PURE__ */ new Set();
4914
+ for (const baseUrl of Object.keys(rawInfo)) {
4915
+ knownProviders.add(normalizeBaseUrl6(baseUrl));
4916
+ }
4917
+ for (const baseUrl of Object.keys(rawMints)) {
4918
+ knownProviders.add(normalizeBaseUrl6(baseUrl));
4919
+ }
4920
+ for (const baseUrl of rawBaseUrls) {
4921
+ knownProviders.add(normalizeBaseUrl6(baseUrl));
4922
+ }
4923
+ for (const baseUrl of rawDisabled) {
4924
+ knownProviders.add(normalizeBaseUrl6(baseUrl));
4925
+ }
4926
+ for (const baseUrl of Object.keys(legacyModels)) {
4927
+ knownProviders.add(normalizeBaseUrl6(baseUrl));
4928
+ }
4929
+ const indexProviders = await driver.getItem(
4930
+ PROVIDER_INDEX_KEY,
4931
+ []
4932
+ );
4933
+ for (const baseUrl of indexProviders) {
4934
+ const normalized = normalizeBaseUrl6(baseUrl);
4935
+ providerIndex.add(normalized);
4936
+ knownProviders.add(normalized);
4937
+ }
4938
+ for (const baseUrl of knownProviders) {
4939
+ const normalized = normalizeBaseUrl6(baseUrl);
4940
+ const models = await driver.getItem(
4941
+ modelKey(normalized),
4942
+ null
4943
+ );
4944
+ const ts = await driver.getItem(
4945
+ modelTsKey(normalized),
4946
+ null
4947
+ );
4948
+ if (models !== null) {
4949
+ modelsByBaseUrl.set(normalized, models);
4950
+ }
4951
+ if (ts !== null) {
4952
+ timestampsByBaseUrl.set(normalized, ts);
4953
+ }
4954
+ if (models !== null || ts !== null) {
4955
+ providerIndex.add(normalized);
4956
+ }
4957
+ }
4958
+ let mints = Object.fromEntries(
4959
+ Object.entries(rawMints).map(([baseUrl, mintList]) => [
4960
+ normalizeBaseUrl6(baseUrl),
4961
+ mintList.map((mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint)
4962
+ ])
4963
+ );
4964
+ let info = Object.fromEntries(
4965
+ Object.entries(rawInfo).map(([baseUrl, entry]) => [
4966
+ normalizeBaseUrl6(baseUrl),
4967
+ entry
4968
+ ])
4969
+ );
4970
+ let _lastUsedModel = lastUsedModel;
4971
+ let _disabledProviders = rawDisabled.map(normalizeBaseUrl6);
4972
+ let _baseUrlsList = rawBaseUrls.map(normalizeBaseUrl6);
4973
+ let _lastBaseUrlsUpdate = lastBaseUrlsUpdate;
4974
+ let _routstr21Models = rawRoutstr21Models;
4975
+ let _lastRoutstr21ModelsUpdate = lastRoutstr21ModelsUpdate;
4976
+ const persistProviderIndex = () => {
4977
+ void driver.setItem(PROVIDER_INDEX_KEY, [...providerIndex]);
4978
+ };
4979
+ return {
4980
+ // -- Models (sharded kv) --
4981
+ getCachedModels: () => {
4982
+ const result = {};
4983
+ for (const [baseUrl, models] of modelsByBaseUrl.entries()) {
4984
+ result[baseUrl] = models;
4985
+ }
4986
+ return result;
4987
+ },
4988
+ setCachedModels: (models) => {
4989
+ const nextKeys = new Set(
4990
+ Object.keys(models).map((baseUrl) => normalizeBaseUrl6(baseUrl))
4991
+ );
4992
+ for (const baseUrl of [...modelsByBaseUrl.keys()]) {
4993
+ if (!nextKeys.has(normalizeBaseUrl6(baseUrl))) {
4994
+ providerIndex.delete(baseUrl);
4995
+ modelsByBaseUrl.delete(baseUrl);
4996
+ timestampsByBaseUrl.delete(baseUrl);
4997
+ void driver.removeItem(modelKey(baseUrl));
4998
+ void driver.removeItem(modelTsKey(baseUrl));
4999
+ }
5000
+ }
5001
+ for (const [baseUrl, modelList] of Object.entries(models)) {
5002
+ const normalized = normalizeBaseUrl6(baseUrl);
5003
+ providerIndex.add(normalized);
5004
+ modelsByBaseUrl.set(normalized, modelList);
5005
+ const ts = timestampsByBaseUrl.get(normalized) ?? Date.now();
5006
+ timestampsByBaseUrl.set(normalized, ts);
5007
+ void driver.setItem(modelKey(normalized), modelList);
5008
+ void driver.setItem(modelTsKey(normalized), ts);
5009
+ }
5010
+ persistProviderIndex();
5011
+ },
5012
+ getProviderLastUpdate: (baseUrl) => {
5013
+ return timestampsByBaseUrl.get(normalizeBaseUrl6(baseUrl)) ?? null;
5014
+ },
5015
+ setProviderLastUpdate: (baseUrl, timestamp) => {
5016
+ const normalized = normalizeBaseUrl6(baseUrl);
5017
+ providerIndex.add(normalized);
5018
+ timestampsByBaseUrl.set(normalized, timestamp);
5019
+ void driver.setItem(modelTsKey(normalized), timestamp);
5020
+ persistProviderIndex();
5021
+ },
5022
+ // -- Mints (kv) --
5023
+ getCachedMints: () => mints,
5024
+ setCachedMints: (value) => {
5025
+ const normalized = {};
5026
+ for (const [baseUrl, mintList] of Object.entries(value)) {
5027
+ normalized[normalizeBaseUrl6(baseUrl)] = mintList.map(
5028
+ (mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint
5029
+ );
5030
+ }
5031
+ mints = normalized;
5032
+ void driver.setItem(SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS, normalized);
5033
+ },
5034
+ // -- Provider info (kv) --
5035
+ getCachedProviderInfo: () => info,
5036
+ setCachedProviderInfo: (value) => {
5037
+ const normalized = {};
5038
+ for (const [baseUrl, entry] of Object.entries(value)) {
5039
+ normalized[normalizeBaseUrl6(baseUrl)] = entry;
5040
+ }
5041
+ info = normalized;
5042
+ void driver.setItem(SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS, normalized);
5043
+ },
5044
+ // -- Last used model (kv) --
5045
+ getLastUsedModel: () => _lastUsedModel,
5046
+ setLastUsedModel: (modelId) => {
5047
+ _lastUsedModel = modelId;
5048
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, modelId);
5049
+ },
5050
+ // -- Disabled providers (kv) --
5051
+ getDisabledProviders: () => _disabledProviders,
5052
+ setDisabledProviders: (urls) => {
5053
+ const normalized = urls.map(normalizeBaseUrl6);
5054
+ _disabledProviders = normalized;
5055
+ void driver.setItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, normalized);
5056
+ },
5057
+ // -- Base URLs (kv) --
5058
+ getBaseUrlsList: () => _baseUrlsList,
5059
+ getBaseUrlsLastUpdate: () => _lastBaseUrlsUpdate,
5060
+ setBaseUrlsList: (urls) => {
5061
+ const normalized = urls.map(normalizeBaseUrl6);
5062
+ _baseUrlsList = normalized;
5063
+ void driver.setItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, normalized);
5064
+ },
5065
+ setBaseUrlsLastUpdate: (timestamp) => {
5066
+ _lastBaseUrlsUpdate = timestamp;
5067
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, timestamp);
5068
+ },
5069
+ // -- Routstr21 models (kv) --
5070
+ getRoutstr21Models: () => _routstr21Models,
5071
+ setRoutstr21Models: (models) => {
5072
+ _routstr21Models = models;
5073
+ void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, models);
5074
+ },
5075
+ getRoutstr21ModelsLastUpdate: () => _lastRoutstr21ModelsUpdate,
5076
+ setRoutstr21ModelsLastUpdate: (timestamp) => {
5077
+ _lastRoutstr21ModelsUpdate = timestamp;
5078
+ void driver.setItem(
5079
+ SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
5080
+ timestamp
5081
+ );
5082
+ }
5083
+ };
5084
+ };
5085
+ var createProviderRegistryFromDiscoveryAdapter = (adapter, logger) => {
5086
+ const log = (logger ?? consoleLogger).child("ProviderRegistry");
5087
+ return {
5088
+ getModelsForProvider: (baseUrl) => {
5089
+ const normalized = normalizeBaseUrl6(baseUrl);
5090
+ return adapter.getCachedModels()[normalized] || [];
5091
+ },
5092
+ getDisabledProviders: () => adapter.getDisabledProviders(),
5093
+ getProviderMints: (baseUrl) => {
5094
+ const normalized = normalizeBaseUrl6(baseUrl);
5095
+ return adapter.getCachedMints()[normalized] || [];
5096
+ },
5097
+ getProviderInfo: async (baseUrl) => {
5098
+ const normalized = normalizeBaseUrl6(baseUrl);
5099
+ const cached = adapter.getCachedProviderInfo()[normalized];
5100
+ if (cached) return cached;
5101
+ try {
5102
+ const response = await fetch(`${normalized}v1/info`);
5103
+ if (!response.ok) {
5104
+ throw new Error(`Failed ${response.status}`);
5105
+ }
5106
+ const info = await response.json();
5107
+ adapter.setCachedProviderInfo({
5108
+ ...adapter.getCachedProviderInfo(),
5109
+ [normalized]: info
5110
+ });
5111
+ return info;
5112
+ } catch (error) {
5113
+ log.warn(`Failed to fetch provider info from ${normalized}:`, error);
5114
+ return null;
5115
+ }
5116
+ },
5117
+ getAllProvidersModels: () => adapter.getCachedModels()
5118
+ };
5119
+ };
5120
+
4687
5121
  // storage/index.ts
4688
5122
  var isBrowser3 = () => {
4689
5123
  try {
@@ -4753,9 +5187,15 @@ var getDefaultUsageTrackingDriver = () => {
4753
5187
  var setDefaultUsageTrackingDriver = (driver) => {
4754
5188
  defaultUsageTrackingDriver = driver;
4755
5189
  };
4756
- var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
5190
+ var defaultDiscoveryAdapter = null;
5191
+ var getDefaultDiscoveryAdapter = async () => {
5192
+ if (defaultDiscoveryAdapter) return defaultDiscoveryAdapter;
5193
+ const driver = getDefaultSdkDriver();
5194
+ defaultDiscoveryAdapter = await createShardedDiscoveryAdapter({ driver });
5195
+ return defaultDiscoveryAdapter;
5196
+ };
4757
5197
  var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
4758
- var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
5198
+ var getDefaultProviderRegistry = async () => createProviderRegistryFromDiscoveryAdapter(await getDefaultDiscoveryAdapter());
4759
5199
  function mergeUsage(previous, next) {
4760
5200
  if (!previous) return next;
4761
5201
  return {
@@ -5065,6 +5505,8 @@ var RoutstrClient = class {
5065
5505
  baseUrl: prepared.baseUrlUsed,
5066
5506
  mintUrl: params.mintUrl,
5067
5507
  initialTokenBalance: prepared.tokenBalanceInSats,
5508
+ initialTokenBalanceUnknown: prepared.tokenBalanceUnknown,
5509
+ fallbackSatsSpent: usage?.satsCost,
5068
5510
  response: prepared.response,
5069
5511
  modelId: prepared.modelId,
5070
5512
  usage,
@@ -5129,7 +5571,7 @@ var RoutstrClient = class {
5129
5571
  );
5130
5572
  }
5131
5573
  }
5132
- const { token, tokenBalance, tokenBalanceUnit } = await this._spendToken({
5574
+ const { token, tokenBalance, tokenBalanceUnit, tokenBalanceUnknown } = await this._spendToken({
5133
5575
  mintUrl,
5134
5576
  amount: requiredSats,
5135
5577
  baseUrl
@@ -5155,9 +5597,20 @@ var RoutstrClient = class {
5155
5597
  baseHeaders,
5156
5598
  selectedModel
5157
5599
  });
5158
- const tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
5600
+ let tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
5601
+ let initialTokenBalanceUnknown = tokenBalanceUnknown;
5159
5602
  const baseUrlUsed = response.baseUrl || baseUrl;
5160
5603
  const tokenUsed = response.token || token;
5604
+ if (baseUrlUsed !== baseUrl || tokenUsed !== token) {
5605
+ if (typeof response.initialTokenBalanceInSats === "number") {
5606
+ tokenBalanceInSats = response.initialTokenBalanceInSats;
5607
+ initialTokenBalanceUnknown = Boolean(
5608
+ response.initialTokenBalanceUnknown
5609
+ );
5610
+ } else {
5611
+ initialTokenBalanceUnknown = true;
5612
+ }
5613
+ }
5161
5614
  const contentType = response.headers.get("content-type") || "";
5162
5615
  let processedResponse = response;
5163
5616
  let capturedUsage;
@@ -5190,6 +5643,7 @@ var RoutstrClient = class {
5190
5643
  tokenUsed,
5191
5644
  baseUrlUsed,
5192
5645
  tokenBalanceInSats,
5646
+ tokenBalanceUnknown: initialTokenBalanceUnknown,
5193
5647
  modelId,
5194
5648
  capturedUsage,
5195
5649
  capturedResponseId,
@@ -5239,7 +5693,8 @@ var RoutstrClient = class {
5239
5693
  let token = spendResult.token;
5240
5694
  let tokenBalance = spendResult.tokenBalance;
5241
5695
  let tokenBalanceUnit = spendResult.tokenBalanceUnit;
5242
- const tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
5696
+ let tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
5697
+ let initialTokenBalanceUnknown = spendResult.tokenBalanceUnknown;
5243
5698
  callbacks.onTokenCreated?.(this._getPendingCashuTokenAmount());
5244
5699
  const baseHeaders = this._buildBaseHeaders(headers);
5245
5700
  const requestHeaders = this._withAuthHeader(baseHeaders, token);
@@ -5282,6 +5737,18 @@ var RoutstrClient = class {
5282
5737
  }
5283
5738
  if (response.status === 200) {
5284
5739
  const baseUrlUsed = response.baseUrl || baseUrl;
5740
+ const responseToken = response.token || token;
5741
+ if (baseUrlUsed !== baseUrl || responseToken !== token) {
5742
+ token = responseToken;
5743
+ if (typeof response.initialTokenBalanceInSats === "number") {
5744
+ tokenBalanceInSats = response.initialTokenBalanceInSats;
5745
+ initialTokenBalanceUnknown = Boolean(
5746
+ response.initialTokenBalanceUnknown
5747
+ );
5748
+ } else {
5749
+ initialTokenBalanceUnknown = true;
5750
+ }
5751
+ }
5285
5752
  const streamingResult = await this.streamProcessor.process(
5286
5753
  response,
5287
5754
  {
@@ -5312,6 +5779,7 @@ var RoutstrClient = class {
5312
5779
  baseUrl: baseUrlUsed,
5313
5780
  mintUrl,
5314
5781
  initialTokenBalance: tokenBalanceInSats,
5782
+ initialTokenBalanceUnknown,
5315
5783
  fallbackSatsSpent: isApikeysEstimate ? this._getEstimatedCosts(selectedModel, streamingResult) : void 0,
5316
5784
  response,
5317
5785
  modelId: selectedModel.id,
@@ -5468,14 +5936,24 @@ var RoutstrClient = class {
5468
5936
  params.token,
5469
5937
  baseUrl
5470
5938
  );
5471
- const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
5472
- const reservedBalance = currentBalanceInfo.unit === "msat" ? (currentBalanceInfo.reserved ?? 0) / 1e3 : currentBalanceInfo.reserved ?? 0;
5473
- const shortfall = Math.max(0, params.requiredSats - currentBalance + reservedBalance);
5474
- topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
5475
- this._log(
5476
- "DEBUG",
5477
- `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance}. Reserved Balance: ${reservedBalance}. Available Balance: ${currentBalance - reservedBalance}`
5478
- );
5939
+ if (currentBalanceInfo.balanceUnknown) {
5940
+ this._log(
5941
+ "DEBUG",
5942
+ `[RoutstrClient] _handleErrorResponse: Current balance unknown for ${baseUrl}; using default topup amount=${topupAmount}`
5943
+ );
5944
+ } else {
5945
+ const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
5946
+ const reservedBalance = currentBalanceInfo.unit === "msat" ? (currentBalanceInfo.reserved ?? 0) / 1e3 : currentBalanceInfo.reserved ?? 0;
5947
+ const shortfall = Math.max(
5948
+ 0,
5949
+ params.requiredSats - currentBalance + reservedBalance
5950
+ );
5951
+ topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
5952
+ this._log(
5953
+ "DEBUG",
5954
+ `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance}. Reserved Balance: ${reservedBalance}. Available Balance: ${currentBalance - reservedBalance}`
5955
+ );
5956
+ }
5479
5957
  } catch (e) {
5480
5958
  this._log(
5481
5959
  "WARN",
@@ -5561,7 +6039,7 @@ var RoutstrClient = class {
5561
6039
  this.storageAdapter.removeApiKey(baseUrl);
5562
6040
  tryNextProvider = true;
5563
6041
  } else {
5564
- const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
6042
+ const latestTokenBalance = latestBalanceInfo.balanceUnknown ? void 0 : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
5565
6043
  if (latestBalanceInfo.apiKey) {
5566
6044
  const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
5567
6045
  if (storedApiKeyEntry?.key !== latestBalanceInfo.apiKey) {
@@ -5572,7 +6050,7 @@ var RoutstrClient = class {
5572
6050
  }
5573
6051
  retryToken = latestBalanceInfo.apiKey;
5574
6052
  }
5575
- if (latestTokenBalance >= 0) {
6053
+ if (latestTokenBalance !== void 0 && latestTokenBalance >= 0) {
5576
6054
  this.storageAdapter.updateApiKeyBalance(
5577
6055
  baseUrl,
5578
6056
  latestTokenBalance
@@ -5647,7 +6125,7 @@ var RoutstrClient = class {
5647
6125
  "DEBUG",
5648
6126
  `[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
5649
6127
  );
5650
- if (!refundResult.success && latestBalanceInfo.amount > 0) {
6128
+ if (!refundResult.success && latestBalanceInfo.amount > 0 && !latestBalanceInfo.balanceUnknown) {
5651
6129
  throw new ProviderError(
5652
6130
  baseUrl,
5653
6131
  status,
@@ -5698,7 +6176,7 @@ var RoutstrClient = class {
5698
6176
  amount: newRequiredSats,
5699
6177
  baseUrl: nextProvider
5700
6178
  });
5701
- return this._makeRequest({
6179
+ const retryResponse = await this._makeRequest({
5702
6180
  ...params,
5703
6181
  path,
5704
6182
  method,
@@ -5710,6 +6188,9 @@ var RoutstrClient = class {
5710
6188
  headers: this._withAuthHeader(params.baseHeaders, spendResult.token),
5711
6189
  retryCount: 0
5712
6190
  });
6191
+ retryResponse.initialTokenBalanceInSats = spendResult.tokenBalanceUnit === "msat" ? spendResult.tokenBalance / 1e3 : spendResult.tokenBalance;
6192
+ retryResponse.initialTokenBalanceUnknown = spendResult.tokenBalanceUnknown;
6193
+ return retryResponse;
5713
6194
  }
5714
6195
  throw new FailoverError(
5715
6196
  baseUrl,
@@ -5725,6 +6206,7 @@ var RoutstrClient = class {
5725
6206
  baseUrl,
5726
6207
  mintUrl,
5727
6208
  initialTokenBalance,
6209
+ initialTokenBalanceUnknown,
5728
6210
  fallbackSatsSpent,
5729
6211
  response,
5730
6212
  modelId,
@@ -5761,17 +6243,19 @@ var RoutstrClient = class {
5761
6243
  latestBalanceInfo.apiKey,
5762
6244
  baseUrl
5763
6245
  );
5764
- const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
6246
+ const latestTokenBalance = latestBalanceInfo.balanceUnknown ? void 0 : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
5765
6247
  const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
5766
6248
  if (storedApiKeyEntry?.key.startsWith("cashu") && latestBalanceInfo.apiKey) {
5767
6249
  this.storageAdapter.removeApiKey(baseUrl);
5768
6250
  this.storageAdapter.setApiKey(baseUrl, latestBalanceInfo.apiKey);
5769
6251
  }
5770
- this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
5771
- satsSpent = initialTokenBalance - latestTokenBalance;
6252
+ if (latestTokenBalance !== void 0) {
6253
+ this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
6254
+ }
6255
+ satsSpent = latestTokenBalance !== void 0 && !initialTokenBalanceUnknown ? Math.max(0, initialTokenBalance - latestTokenBalance) : fallbackSatsSpent ?? usage?.satsCost ?? 0;
5772
6256
  } catch (e) {
5773
6257
  this._log("WARN", "Could not get updated API key balance:", e);
5774
- satsSpent = fallbackSatsSpent ?? initialTokenBalance;
6258
+ satsSpent = fallbackSatsSpent ?? usage?.satsCost ?? 0;
5775
6259
  }
5776
6260
  }
5777
6261
  await this._trackResponseUsage({
@@ -6021,6 +6505,7 @@ var RoutstrClient = class {
6021
6505
  }
6022
6506
  let tokenBalance = 0;
6023
6507
  let tokenBalanceUnit = "sat";
6508
+ let tokenBalanceUnknown = false;
6024
6509
  const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
6025
6510
  const distributionForBaseUrl = apiKeyDistribution.find(
6026
6511
  (d) => d.baseUrl === baseUrl
@@ -6036,6 +6521,7 @@ var RoutstrClient = class {
6036
6521
  );
6037
6522
  tokenBalance = balanceInfo.amount;
6038
6523
  tokenBalanceUnit = balanceInfo.unit;
6524
+ tokenBalanceUnknown = Boolean(balanceInfo.balanceUnknown);
6039
6525
  } catch (e) {
6040
6526
  this._log("WARN", "Could not get initial API key balance:", e);
6041
6527
  }
@@ -6047,7 +6533,8 @@ var RoutstrClient = class {
6047
6533
  return {
6048
6534
  token: parentApiKey?.key ?? "",
6049
6535
  tokenBalance,
6050
- tokenBalanceUnit
6536
+ tokenBalanceUnit,
6537
+ tokenBalanceUnknown
6051
6538
  };
6052
6539
  }
6053
6540
  this._log(
@@ -6076,7 +6563,8 @@ var RoutstrClient = class {
6076
6563
  return {
6077
6564
  token: spendResult.token,
6078
6565
  tokenBalance: spendResult.balance,
6079
- tokenBalanceUnit: spendResult.unit ?? "sat"
6566
+ tokenBalanceUnit: spendResult.unit ?? "sat",
6567
+ tokenBalanceUnknown: false
6080
6568
  };
6081
6569
  }
6082
6570
  /**
@@ -6253,6 +6741,6 @@ function extractStream(requestBody) {
6253
6741
  return typeof stream === "boolean" ? stream : void 0;
6254
6742
  }
6255
6743
 
6256
- export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, consoleLogger, createBunSqliteDriver, createBunSqliteUsageTrackingDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createSqliteDriver, createSqliteUsageTrackingDriver, createStorageAdapterFromStore, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, inspectSSEWebStream, isOnionUrl, isTorContext, localStorageDriver, noopLogger, normalizeProviderUrl, routeRequests, setDefaultUsageTrackingDriver };
6744
+ export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, consoleLogger, createBunSqliteDriver, createBunSqliteUsageTrackingDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromDiscoveryAdapter, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createShardedDiscoveryAdapter, createSqliteDriver, createSqliteUsageTrackingDriver, createStorageAdapterFromStore, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, inspectSSEWebStream, isOnionUrl, isTorContext, localStorageDriver, noopLogger, normalizeProviderUrl, routeRequests, setDefaultUsageTrackingDriver };
6257
6745
  //# sourceMappingURL=index.mjs.map
6258
6746
  //# sourceMappingURL=index.mjs.map