@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/client/index.mjs
CHANGED
|
@@ -79,7 +79,7 @@ var auditLogger = AuditLogger.getInstance();
|
|
|
79
79
|
|
|
80
80
|
// wallet/tokenUtils.ts
|
|
81
81
|
function isNetworkErrorMessage(message) {
|
|
82
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
82
|
+
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");
|
|
83
83
|
}
|
|
84
84
|
function getBalanceInSats(balance, unit) {
|
|
85
85
|
return unit === "msat" ? balance / 1e3 : balance;
|
|
@@ -369,7 +369,26 @@ var CashuSpender = class {
|
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
371
|
if (token && baseUrl) {
|
|
372
|
-
|
|
372
|
+
try {
|
|
373
|
+
this.storageAdapter.setToken(baseUrl, token);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (error instanceof Error && error.message.includes("Token already exists")) {
|
|
376
|
+
this._log(
|
|
377
|
+
"DEBUG",
|
|
378
|
+
`[CashuSpender] _spendInternal: Token already exists for ${baseUrl}, receiving newly created token and using existing`
|
|
379
|
+
);
|
|
380
|
+
const receiveResult = await this.receiveToken(token);
|
|
381
|
+
if (receiveResult.success) {
|
|
382
|
+
this._log(
|
|
383
|
+
"DEBUG",
|
|
384
|
+
`[CashuSpender] _spendInternal: Token restored successfully, amount=${receiveResult.amount}`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
token = this.storageAdapter.getToken(baseUrl);
|
|
388
|
+
} else {
|
|
389
|
+
throw error;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
373
392
|
}
|
|
374
393
|
this._logTransaction("spend", {
|
|
375
394
|
amount: spentAmount,
|
|
@@ -847,7 +866,9 @@ var BalanceManager = class {
|
|
|
847
866
|
p2pkPubkey
|
|
848
867
|
} = options;
|
|
849
868
|
const adjustedAmount = Math.ceil(amount);
|
|
869
|
+
console.log(`[BalanceManager.createProviderToken] Starting: baseUrl=${baseUrl}, mintUrl=${mintUrl}, amount=${amount}, adjustedAmount=${adjustedAmount}, retryCount=${retryCount}`);
|
|
850
870
|
if (!adjustedAmount || isNaN(adjustedAmount)) {
|
|
871
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: Invalid amount - amount=${amount}, adjustedAmount=${adjustedAmount}`);
|
|
851
872
|
return { success: false, error: "Invalid top up amount" };
|
|
852
873
|
}
|
|
853
874
|
const balanceState = await this.getBalanceState();
|
|
@@ -878,6 +899,7 @@ var BalanceManager = class {
|
|
|
878
899
|
{ url: "", balance: 0 }
|
|
879
900
|
).url
|
|
880
901
|
);
|
|
902
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: Insufficient balance - required=${adjustedAmount}, available=${totalMintBalance + targetProviderBalance}, totalMintBalance=${totalMintBalance}, targetProviderBalance=${targetProviderBalance}, refundableProviderBalance=${refundableProviderBalance}`);
|
|
881
903
|
return { success: false, error: error.message };
|
|
882
904
|
}
|
|
883
905
|
if (targetProviderBalance >= adjustedAmount) {
|
|
@@ -919,6 +941,7 @@ var BalanceManager = class {
|
|
|
919
941
|
maxMintUrl = mintUrl2;
|
|
920
942
|
}
|
|
921
943
|
}
|
|
944
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: No candidate mints found - requiredAmount=${requiredAmount}, totalMintBalance=${totalMintBalance}, maxBalance=${maxBalance}, maxMintUrl=${maxMintUrl}, providerMints=${JSON.stringify(providerMints)}`);
|
|
922
945
|
const error = new InsufficientBalanceError(
|
|
923
946
|
adjustedAmount,
|
|
924
947
|
totalMintBalance,
|
|
@@ -930,11 +953,13 @@ var BalanceManager = class {
|
|
|
930
953
|
let lastError;
|
|
931
954
|
for (const candidateMint of candidates) {
|
|
932
955
|
try {
|
|
956
|
+
console.log(`[BalanceManager.createProviderToken] Attempting mint: ${candidateMint}, amount: ${requiredAmount}`);
|
|
933
957
|
const token = await this.walletAdapter.sendToken(
|
|
934
958
|
candidateMint,
|
|
935
959
|
requiredAmount,
|
|
936
960
|
p2pkPubkey
|
|
937
961
|
);
|
|
962
|
+
console.log(`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`);
|
|
938
963
|
return {
|
|
939
964
|
success: true,
|
|
940
965
|
token,
|
|
@@ -942,9 +967,12 @@ var BalanceManager = class {
|
|
|
942
967
|
amountSpent: requiredAmount
|
|
943
968
|
};
|
|
944
969
|
} catch (error) {
|
|
970
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
971
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: Mint ${candidateMint} failed with error: ${errorMsg}`);
|
|
945
972
|
if (error instanceof Error) {
|
|
946
|
-
lastError =
|
|
973
|
+
lastError = errorMsg;
|
|
947
974
|
if (isNetworkErrorMessage(error.message)) {
|
|
975
|
+
console.warn(`[BalanceManager.createProviderToken] Network error from ${candidateMint}, trying next mint...`);
|
|
948
976
|
continue;
|
|
949
977
|
}
|
|
950
978
|
}
|
|
@@ -954,6 +982,7 @@ var BalanceManager = class {
|
|
|
954
982
|
};
|
|
955
983
|
}
|
|
956
984
|
}
|
|
985
|
+
console.error(`[BalanceManager.createProviderToken] FAILURE: All candidate mints exhausted - lastError=${lastError}, candidates=${JSON.stringify(candidates)}`);
|
|
957
986
|
return {
|
|
958
987
|
success: false,
|
|
959
988
|
error: lastError || "All candidate mints failed while creating top up token"
|
|
@@ -1239,7 +1268,6 @@ var BalanceManager = class {
|
|
|
1239
1268
|
});
|
|
1240
1269
|
if (response.ok) {
|
|
1241
1270
|
const data = await response.json();
|
|
1242
|
-
console.log("TOKENA FASJDFAS", data);
|
|
1243
1271
|
return {
|
|
1244
1272
|
amount: data.balance,
|
|
1245
1273
|
reserved: data.reserved ?? 0,
|
|
@@ -1612,22 +1640,101 @@ function calculateImageTokens(width, height, detail = "auto") {
|
|
|
1612
1640
|
function isInsecureHttpUrl(url) {
|
|
1613
1641
|
return url.startsWith("http://");
|
|
1614
1642
|
}
|
|
1615
|
-
var ProviderManager = class {
|
|
1643
|
+
var ProviderManager = class _ProviderManager {
|
|
1616
1644
|
constructor(providerRegistry) {
|
|
1617
1645
|
this.providerRegistry = providerRegistry;
|
|
1618
1646
|
}
|
|
1619
1647
|
failedProviders = /* @__PURE__ */ new Set();
|
|
1648
|
+
/** Track when each provider last failed (provider URL -> timestamp) */
|
|
1649
|
+
lastFailed = /* @__PURE__ */ new Map();
|
|
1650
|
+
/** Providers on cooldown: [provider_url, cooldown_started_timestamp][] */
|
|
1651
|
+
providersOnCoolDown = [];
|
|
1652
|
+
/** Cooldown duration in milliseconds (5 minutes) */
|
|
1653
|
+
static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
|
|
1654
|
+
/**
|
|
1655
|
+
* Clean up expired cooldown entries
|
|
1656
|
+
*/
|
|
1657
|
+
cleanupExpiredCooldowns() {
|
|
1658
|
+
const now = Date.now();
|
|
1659
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1660
|
+
([, timestamp]) => now - timestamp < _ProviderManager.COOLDOWN_DURATION_MS
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Get the cooldown duration in milliseconds
|
|
1665
|
+
*/
|
|
1666
|
+
getCooldownDurationMs() {
|
|
1667
|
+
return _ProviderManager.COOLDOWN_DURATION_MS;
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Check if a provider is currently on cooldown
|
|
1671
|
+
*/
|
|
1672
|
+
isOnCooldown(baseUrl) {
|
|
1673
|
+
this.cleanupExpiredCooldowns();
|
|
1674
|
+
return this.providersOnCoolDown.some(([url]) => url === baseUrl);
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Get all providers currently on cooldown
|
|
1678
|
+
*/
|
|
1679
|
+
getProvidersOnCooldown() {
|
|
1680
|
+
this.cleanupExpiredCooldowns();
|
|
1681
|
+
return [...this.providersOnCoolDown];
|
|
1682
|
+
}
|
|
1620
1683
|
/**
|
|
1621
1684
|
* Reset the failed providers list
|
|
1622
1685
|
*/
|
|
1623
1686
|
resetFailedProviders() {
|
|
1624
1687
|
this.failedProviders.clear();
|
|
1625
1688
|
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Get the last failed timestamp for a provider
|
|
1691
|
+
*/
|
|
1692
|
+
getLastFailed(baseUrl) {
|
|
1693
|
+
return this.lastFailed.get(baseUrl);
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Get all providers with their last failed timestamps
|
|
1697
|
+
*/
|
|
1698
|
+
getAllLastFailed() {
|
|
1699
|
+
return new Map(this.lastFailed);
|
|
1700
|
+
}
|
|
1626
1701
|
/**
|
|
1627
1702
|
* Mark a provider as failed
|
|
1703
|
+
* If a provider fails twice within 5 minutes, it's added to cooldown
|
|
1628
1704
|
*/
|
|
1629
1705
|
markFailed(baseUrl) {
|
|
1706
|
+
const now = Date.now();
|
|
1707
|
+
const lastFailure = this.lastFailed.get(baseUrl);
|
|
1708
|
+
this.lastFailed.set(baseUrl, now);
|
|
1630
1709
|
this.failedProviders.add(baseUrl);
|
|
1710
|
+
if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
|
|
1711
|
+
if (!this.isOnCooldown(baseUrl)) {
|
|
1712
|
+
this.providersOnCoolDown.push([baseUrl, now]);
|
|
1713
|
+
console.log(
|
|
1714
|
+
`Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Remove a provider from cooldown (e.g., after successful request)
|
|
1721
|
+
*/
|
|
1722
|
+
removeFromCooldown(baseUrl) {
|
|
1723
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1724
|
+
([url]) => url !== baseUrl
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Clear all cooldown tracking
|
|
1729
|
+
*/
|
|
1730
|
+
clearCooldowns() {
|
|
1731
|
+
this.providersOnCoolDown = [];
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Clear all failure tracking (lastFailed timestamps)
|
|
1735
|
+
*/
|
|
1736
|
+
clearFailureHistory() {
|
|
1737
|
+
this.lastFailed.clear();
|
|
1631
1738
|
}
|
|
1632
1739
|
/**
|
|
1633
1740
|
* Check if a provider has failed
|
|
@@ -1635,6 +1742,12 @@ var ProviderManager = class {
|
|
|
1635
1742
|
hasFailed(baseUrl) {
|
|
1636
1743
|
return this.failedProviders.has(baseUrl);
|
|
1637
1744
|
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Get a copy of the failed providers set
|
|
1747
|
+
*/
|
|
1748
|
+
getFailedProviders() {
|
|
1749
|
+
return new Set(this.failedProviders);
|
|
1750
|
+
}
|
|
1638
1751
|
/**
|
|
1639
1752
|
* Find the next best provider for a model
|
|
1640
1753
|
* @param modelId The model ID to find a provider for
|
|
@@ -1650,7 +1763,7 @@ var ProviderManager = class {
|
|
|
1650
1763
|
const allProviders = this.providerRegistry.getAllProvidersModels();
|
|
1651
1764
|
const candidates = [];
|
|
1652
1765
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
1653
|
-
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl)) {
|
|
1766
|
+
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl) || this.isOnCooldown(baseUrl)) {
|
|
1654
1767
|
continue;
|
|
1655
1768
|
}
|
|
1656
1769
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
|
|
@@ -1697,6 +1810,7 @@ var ProviderManager = class {
|
|
|
1697
1810
|
const torMode = isTorContext();
|
|
1698
1811
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
1699
1812
|
if (disabledProviders.has(baseUrl)) continue;
|
|
1813
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
1700
1814
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl)))
|
|
1701
1815
|
continue;
|
|
1702
1816
|
const model = models.find((m) => m.id === modelId);
|
|
@@ -1720,6 +1834,7 @@ var ProviderManager = class {
|
|
|
1720
1834
|
const results = [];
|
|
1721
1835
|
for (const [baseUrl, models] of Object.entries(allModels)) {
|
|
1722
1836
|
if (!includeDisabled && disabledProviders.has(baseUrl)) continue;
|
|
1837
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
1723
1838
|
if (torMode && !baseUrl.includes(".onion")) continue;
|
|
1724
1839
|
if (!torMode && (baseUrl.includes(".onion") || isInsecureHttpUrl(baseUrl)))
|
|
1725
1840
|
continue;
|
|
@@ -2133,7 +2248,6 @@ var RoutstrClient = class {
|
|
|
2133
2248
|
body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
|
|
2134
2249
|
});
|
|
2135
2250
|
if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
|
|
2136
|
-
this._log("DEBUG", "response,", response);
|
|
2137
2251
|
response.baseUrl = baseUrl;
|
|
2138
2252
|
response.token = token;
|
|
2139
2253
|
if (!response.ok) {
|
|
@@ -2156,7 +2270,7 @@ var RoutstrClient = class {
|
|
|
2156
2270
|
}
|
|
2157
2271
|
return response;
|
|
2158
2272
|
} catch (error) {
|
|
2159
|
-
if (
|
|
2273
|
+
if (isNetworkErrorMessage(error?.message || "")) {
|
|
2160
2274
|
return await this._handleErrorResponse(
|
|
2161
2275
|
params,
|
|
2162
2276
|
token,
|
|
@@ -2291,7 +2405,13 @@ var RoutstrClient = class {
|
|
|
2291
2405
|
"DEBUG",
|
|
2292
2406
|
`[RoutstrClient] _handleErrorResponse: Insufficient balance, need=${required}, have=${available}`
|
|
2293
2407
|
);
|
|
2294
|
-
throw new InsufficientBalanceError(
|
|
2408
|
+
throw new InsufficientBalanceError(
|
|
2409
|
+
required,
|
|
2410
|
+
available,
|
|
2411
|
+
0,
|
|
2412
|
+
"",
|
|
2413
|
+
message
|
|
2414
|
+
);
|
|
2295
2415
|
} else {
|
|
2296
2416
|
this._log(
|
|
2297
2417
|
"DEBUG",
|
|
@@ -2504,7 +2624,10 @@ var RoutstrClient = class {
|
|
|
2504
2624
|
retryCount: 0
|
|
2505
2625
|
});
|
|
2506
2626
|
}
|
|
2507
|
-
throw new FailoverError(
|
|
2627
|
+
throw new FailoverError(
|
|
2628
|
+
baseUrl,
|
|
2629
|
+
Array.from(this.providerManager.getFailedProviders())
|
|
2630
|
+
);
|
|
2508
2631
|
}
|
|
2509
2632
|
/**
|
|
2510
2633
|
* Handle post-response balance update for all modes
|
|
@@ -2654,12 +2777,6 @@ var RoutstrClient = class {
|
|
|
2654
2777
|
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
2655
2778
|
return distribution.reduce((total, item) => total + item.amount, 0);
|
|
2656
2779
|
}
|
|
2657
|
-
/**
|
|
2658
|
-
* Check if error message indicates a network error
|
|
2659
|
-
*/
|
|
2660
|
-
_isNetworkError(message) {
|
|
2661
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
2662
|
-
}
|
|
2663
2780
|
/**
|
|
2664
2781
|
* Handle errors and notify callbacks
|
|
2665
2782
|
*/
|