@routstr/sdk 0.2.1 → 0.2.3
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/client/index.d.mts +47 -4
- package/dist/client/index.d.ts +47 -4
- package/dist/client/index.js +133 -16
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +133 -16
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +133 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +133 -16
- package/dist/index.mjs.map +1 -1
- package/dist/wallet/index.js +32 -4
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +32 -4
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -726,7 +726,7 @@ var auditLogger = AuditLogger.getInstance();
|
|
|
726
726
|
|
|
727
727
|
// wallet/tokenUtils.ts
|
|
728
728
|
function isNetworkErrorMessage(message) {
|
|
729
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
729
|
+
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed") || message.includes("ERR_TLS_CERT_ALTNAME_INVALID") || message.includes("ERR_TLS_CERT_NOT_YET_VALID") || message.includes("ERR_TLS_CERT_EXPIRED") || message.includes("UNABLE_TO_VERIFY_LEAF_SIGNATURE") || message.includes("SELF_SIGNED_CERT_IN_CHAIN");
|
|
730
730
|
}
|
|
731
731
|
function getBalanceInSats(balance, unit) {
|
|
732
732
|
return unit === "msat" ? balance / 1e3 : balance;
|
|
@@ -1016,7 +1016,26 @@ var CashuSpender = class {
|
|
|
1016
1016
|
}
|
|
1017
1017
|
}
|
|
1018
1018
|
if (token && baseUrl) {
|
|
1019
|
-
|
|
1019
|
+
try {
|
|
1020
|
+
this.storageAdapter.setToken(baseUrl, token);
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
if (error instanceof Error && error.message.includes("Token already exists")) {
|
|
1023
|
+
this._log(
|
|
1024
|
+
"DEBUG",
|
|
1025
|
+
`[CashuSpender] _spendInternal: Token already exists for ${baseUrl}, receiving newly created token and using existing`
|
|
1026
|
+
);
|
|
1027
|
+
const receiveResult = await this.receiveToken(token);
|
|
1028
|
+
if (receiveResult.success) {
|
|
1029
|
+
this._log(
|
|
1030
|
+
"DEBUG",
|
|
1031
|
+
`[CashuSpender] _spendInternal: Token restored successfully, amount=${receiveResult.amount}`
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
token = this.storageAdapter.getToken(baseUrl);
|
|
1035
|
+
} else {
|
|
1036
|
+
throw error;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1020
1039
|
}
|
|
1021
1040
|
this._logTransaction("spend", {
|
|
1022
1041
|
amount: spentAmount,
|
|
@@ -1494,7 +1513,9 @@ var BalanceManager = class {
|
|
|
1494
1513
|
p2pkPubkey
|
|
1495
1514
|
} = options;
|
|
1496
1515
|
const adjustedAmount = Math.ceil(amount);
|
|
1516
|
+
console.log(`[BalanceManager.createProviderToken] Starting: baseUrl=${baseUrl}, mintUrl=${mintUrl}, amount=${amount}, adjustedAmount=${adjustedAmount}, retryCount=${retryCount}`);
|
|
1497
1517
|
if (!adjustedAmount || isNaN(adjustedAmount)) {
|
|
1518
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: Invalid amount - amount=${amount}, adjustedAmount=${adjustedAmount}`);
|
|
1498
1519
|
return { success: false, error: "Invalid top up amount" };
|
|
1499
1520
|
}
|
|
1500
1521
|
const balanceState = await this.getBalanceState();
|
|
@@ -1525,6 +1546,7 @@ var BalanceManager = class {
|
|
|
1525
1546
|
{ url: "", balance: 0 }
|
|
1526
1547
|
).url
|
|
1527
1548
|
);
|
|
1549
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: Insufficient balance - required=${adjustedAmount}, available=${totalMintBalance + targetProviderBalance}, totalMintBalance=${totalMintBalance}, targetProviderBalance=${targetProviderBalance}, refundableProviderBalance=${refundableProviderBalance}`);
|
|
1528
1550
|
return { success: false, error: error.message };
|
|
1529
1551
|
}
|
|
1530
1552
|
if (targetProviderBalance >= adjustedAmount) {
|
|
@@ -1566,6 +1588,7 @@ var BalanceManager = class {
|
|
|
1566
1588
|
maxMintUrl = mintUrl2;
|
|
1567
1589
|
}
|
|
1568
1590
|
}
|
|
1591
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: No candidate mints found - requiredAmount=${requiredAmount}, totalMintBalance=${totalMintBalance}, maxBalance=${maxBalance}, maxMintUrl=${maxMintUrl}, providerMints=${JSON.stringify(providerMints)}`);
|
|
1569
1592
|
const error = new InsufficientBalanceError(
|
|
1570
1593
|
adjustedAmount,
|
|
1571
1594
|
totalMintBalance,
|
|
@@ -1577,11 +1600,13 @@ var BalanceManager = class {
|
|
|
1577
1600
|
let lastError;
|
|
1578
1601
|
for (const candidateMint of candidates) {
|
|
1579
1602
|
try {
|
|
1603
|
+
console.log(`[BalanceManager.createProviderToken] Attempting mint: ${candidateMint}, amount: ${requiredAmount}`);
|
|
1580
1604
|
const token = await this.walletAdapter.sendToken(
|
|
1581
1605
|
candidateMint,
|
|
1582
1606
|
requiredAmount,
|
|
1583
1607
|
p2pkPubkey
|
|
1584
1608
|
);
|
|
1609
|
+
console.log(`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`);
|
|
1585
1610
|
return {
|
|
1586
1611
|
success: true,
|
|
1587
1612
|
token,
|
|
@@ -1589,9 +1614,12 @@ var BalanceManager = class {
|
|
|
1589
1614
|
amountSpent: requiredAmount
|
|
1590
1615
|
};
|
|
1591
1616
|
} catch (error) {
|
|
1617
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1618
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: Mint ${candidateMint} failed with error: ${errorMsg}`);
|
|
1592
1619
|
if (error instanceof Error) {
|
|
1593
|
-
lastError =
|
|
1620
|
+
lastError = errorMsg;
|
|
1594
1621
|
if (isNetworkErrorMessage(error.message)) {
|
|
1622
|
+
console.warn(`[BalanceManager.createProviderToken] Network error from ${candidateMint}, trying next mint...`);
|
|
1595
1623
|
continue;
|
|
1596
1624
|
}
|
|
1597
1625
|
}
|
|
@@ -1601,6 +1629,7 @@ var BalanceManager = class {
|
|
|
1601
1629
|
};
|
|
1602
1630
|
}
|
|
1603
1631
|
}
|
|
1632
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: All candidate mints exhausted - lastError=${lastError}, candidates=${JSON.stringify(candidates)}`);
|
|
1604
1633
|
return {
|
|
1605
1634
|
success: false,
|
|
1606
1635
|
error: lastError || "All candidate mints failed while creating top up token"
|
|
@@ -1886,7 +1915,6 @@ var BalanceManager = class {
|
|
|
1886
1915
|
});
|
|
1887
1916
|
if (response.ok) {
|
|
1888
1917
|
const data = await response.json();
|
|
1889
|
-
console.log("TOKENA FASJDFAS", data);
|
|
1890
1918
|
return {
|
|
1891
1919
|
amount: data.balance,
|
|
1892
1920
|
reserved: data.reserved ?? 0,
|
|
@@ -2315,22 +2343,101 @@ function calculateImageTokens(width, height, detail = "auto") {
|
|
|
2315
2343
|
function isInsecureHttpUrl(url) {
|
|
2316
2344
|
return url.startsWith("http://");
|
|
2317
2345
|
}
|
|
2318
|
-
var ProviderManager = class {
|
|
2346
|
+
var ProviderManager = class _ProviderManager {
|
|
2319
2347
|
constructor(providerRegistry) {
|
|
2320
2348
|
this.providerRegistry = providerRegistry;
|
|
2321
2349
|
}
|
|
2322
2350
|
failedProviders = /* @__PURE__ */ new Set();
|
|
2351
|
+
/** Track when each provider last failed (provider URL -> timestamp) */
|
|
2352
|
+
lastFailed = /* @__PURE__ */ new Map();
|
|
2353
|
+
/** Providers on cooldown: [provider_url, cooldown_started_timestamp][] */
|
|
2354
|
+
providersOnCoolDown = [];
|
|
2355
|
+
/** Cooldown duration in milliseconds (5 minutes) */
|
|
2356
|
+
static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
|
|
2357
|
+
/**
|
|
2358
|
+
* Clean up expired cooldown entries
|
|
2359
|
+
*/
|
|
2360
|
+
cleanupExpiredCooldowns() {
|
|
2361
|
+
const now = Date.now();
|
|
2362
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
2363
|
+
([, timestamp]) => now - timestamp < _ProviderManager.COOLDOWN_DURATION_MS
|
|
2364
|
+
);
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* Get the cooldown duration in milliseconds
|
|
2368
|
+
*/
|
|
2369
|
+
getCooldownDurationMs() {
|
|
2370
|
+
return _ProviderManager.COOLDOWN_DURATION_MS;
|
|
2371
|
+
}
|
|
2372
|
+
/**
|
|
2373
|
+
* Check if a provider is currently on cooldown
|
|
2374
|
+
*/
|
|
2375
|
+
isOnCooldown(baseUrl) {
|
|
2376
|
+
this.cleanupExpiredCooldowns();
|
|
2377
|
+
return this.providersOnCoolDown.some(([url]) => url === baseUrl);
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2380
|
+
* Get all providers currently on cooldown
|
|
2381
|
+
*/
|
|
2382
|
+
getProvidersOnCooldown() {
|
|
2383
|
+
this.cleanupExpiredCooldowns();
|
|
2384
|
+
return [...this.providersOnCoolDown];
|
|
2385
|
+
}
|
|
2323
2386
|
/**
|
|
2324
2387
|
* Reset the failed providers list
|
|
2325
2388
|
*/
|
|
2326
2389
|
resetFailedProviders() {
|
|
2327
2390
|
this.failedProviders.clear();
|
|
2328
2391
|
}
|
|
2392
|
+
/**
|
|
2393
|
+
* Get the last failed timestamp for a provider
|
|
2394
|
+
*/
|
|
2395
|
+
getLastFailed(baseUrl) {
|
|
2396
|
+
return this.lastFailed.get(baseUrl);
|
|
2397
|
+
}
|
|
2398
|
+
/**
|
|
2399
|
+
* Get all providers with their last failed timestamps
|
|
2400
|
+
*/
|
|
2401
|
+
getAllLastFailed() {
|
|
2402
|
+
return new Map(this.lastFailed);
|
|
2403
|
+
}
|
|
2329
2404
|
/**
|
|
2330
2405
|
* Mark a provider as failed
|
|
2406
|
+
* If a provider fails twice within 5 minutes, it's added to cooldown
|
|
2331
2407
|
*/
|
|
2332
2408
|
markFailed(baseUrl) {
|
|
2409
|
+
const now = Date.now();
|
|
2410
|
+
const lastFailure = this.lastFailed.get(baseUrl);
|
|
2411
|
+
this.lastFailed.set(baseUrl, now);
|
|
2333
2412
|
this.failedProviders.add(baseUrl);
|
|
2413
|
+
if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
|
|
2414
|
+
if (!this.isOnCooldown(baseUrl)) {
|
|
2415
|
+
this.providersOnCoolDown.push([baseUrl, now]);
|
|
2416
|
+
console.log(
|
|
2417
|
+
`Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Remove a provider from cooldown (e.g., after successful request)
|
|
2424
|
+
*/
|
|
2425
|
+
removeFromCooldown(baseUrl) {
|
|
2426
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
2427
|
+
([url]) => url !== baseUrl
|
|
2428
|
+
);
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* Clear all cooldown tracking
|
|
2432
|
+
*/
|
|
2433
|
+
clearCooldowns() {
|
|
2434
|
+
this.providersOnCoolDown = [];
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Clear all failure tracking (lastFailed timestamps)
|
|
2438
|
+
*/
|
|
2439
|
+
clearFailureHistory() {
|
|
2440
|
+
this.lastFailed.clear();
|
|
2334
2441
|
}
|
|
2335
2442
|
/**
|
|
2336
2443
|
* Check if a provider has failed
|
|
@@ -2338,6 +2445,12 @@ var ProviderManager = class {
|
|
|
2338
2445
|
hasFailed(baseUrl) {
|
|
2339
2446
|
return this.failedProviders.has(baseUrl);
|
|
2340
2447
|
}
|
|
2448
|
+
/**
|
|
2449
|
+
* Get a copy of the failed providers set
|
|
2450
|
+
*/
|
|
2451
|
+
getFailedProviders() {
|
|
2452
|
+
return new Set(this.failedProviders);
|
|
2453
|
+
}
|
|
2341
2454
|
/**
|
|
2342
2455
|
* Find the next best provider for a model
|
|
2343
2456
|
* @param modelId The model ID to find a provider for
|
|
@@ -2353,7 +2466,7 @@ var ProviderManager = class {
|
|
|
2353
2466
|
const allProviders = this.providerRegistry.getAllProvidersModels();
|
|
2354
2467
|
const candidates = [];
|
|
2355
2468
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
2356
|
-
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl)) {
|
|
2469
|
+
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl) || this.isOnCooldown(baseUrl)) {
|
|
2357
2470
|
continue;
|
|
2358
2471
|
}
|
|
2359
2472
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
|
|
@@ -2400,6 +2513,7 @@ var ProviderManager = class {
|
|
|
2400
2513
|
const torMode = isTorContext();
|
|
2401
2514
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
2402
2515
|
if (disabledProviders.has(baseUrl)) continue;
|
|
2516
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
2403
2517
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl)))
|
|
2404
2518
|
continue;
|
|
2405
2519
|
const model = models.find((m) => m.id === modelId);
|
|
@@ -2423,6 +2537,7 @@ var ProviderManager = class {
|
|
|
2423
2537
|
const results = [];
|
|
2424
2538
|
for (const [baseUrl, models] of Object.entries(allModels)) {
|
|
2425
2539
|
if (!includeDisabled && disabledProviders.has(baseUrl)) continue;
|
|
2540
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
2426
2541
|
if (torMode && !baseUrl.includes(".onion")) continue;
|
|
2427
2542
|
if (!torMode && (baseUrl.includes(".onion") || isInsecureHttpUrl(baseUrl)))
|
|
2428
2543
|
continue;
|
|
@@ -2836,7 +2951,6 @@ var RoutstrClient = class {
|
|
|
2836
2951
|
body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
|
|
2837
2952
|
});
|
|
2838
2953
|
if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
|
|
2839
|
-
this._log("DEBUG", "response,", response);
|
|
2840
2954
|
response.baseUrl = baseUrl;
|
|
2841
2955
|
response.token = token;
|
|
2842
2956
|
if (!response.ok) {
|
|
@@ -2859,7 +2973,7 @@ var RoutstrClient = class {
|
|
|
2859
2973
|
}
|
|
2860
2974
|
return response;
|
|
2861
2975
|
} catch (error) {
|
|
2862
|
-
if (
|
|
2976
|
+
if (isNetworkErrorMessage(error?.message || "")) {
|
|
2863
2977
|
return await this._handleErrorResponse(
|
|
2864
2978
|
params,
|
|
2865
2979
|
token,
|
|
@@ -2994,7 +3108,13 @@ var RoutstrClient = class {
|
|
|
2994
3108
|
"DEBUG",
|
|
2995
3109
|
`[RoutstrClient] _handleErrorResponse: Insufficient balance, need=${required}, have=${available}`
|
|
2996
3110
|
);
|
|
2997
|
-
throw new InsufficientBalanceError(
|
|
3111
|
+
throw new InsufficientBalanceError(
|
|
3112
|
+
required,
|
|
3113
|
+
available,
|
|
3114
|
+
0,
|
|
3115
|
+
"",
|
|
3116
|
+
message
|
|
3117
|
+
);
|
|
2998
3118
|
} else {
|
|
2999
3119
|
this._log(
|
|
3000
3120
|
"DEBUG",
|
|
@@ -3207,7 +3327,10 @@ var RoutstrClient = class {
|
|
|
3207
3327
|
retryCount: 0
|
|
3208
3328
|
});
|
|
3209
3329
|
}
|
|
3210
|
-
throw new FailoverError(
|
|
3330
|
+
throw new FailoverError(
|
|
3331
|
+
baseUrl,
|
|
3332
|
+
Array.from(this.providerManager.getFailedProviders())
|
|
3333
|
+
);
|
|
3211
3334
|
}
|
|
3212
3335
|
/**
|
|
3213
3336
|
* Handle post-response balance update for all modes
|
|
@@ -3357,12 +3480,6 @@ var RoutstrClient = class {
|
|
|
3357
3480
|
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
3358
3481
|
return distribution.reduce((total, item) => total + item.amount, 0);
|
|
3359
3482
|
}
|
|
3360
|
-
/**
|
|
3361
|
-
* Check if error message indicates a network error
|
|
3362
|
-
*/
|
|
3363
|
-
_isNetworkError(message) {
|
|
3364
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
3365
|
-
}
|
|
3366
3483
|
/**
|
|
3367
3484
|
* Handle errors and notify callbacks
|
|
3368
3485
|
*/
|