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