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