@unicitylabs/sphere-sdk 0.3.0 → 0.3.2
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/README.md +166 -8
- package/dist/core/index.cjs +544 -19
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +240 -13
- package/dist/core/index.d.ts +240 -13
- package/dist/core/index.js +543 -19
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +543 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +240 -13
- package/dist/index.d.ts +240 -13
- package/dist/index.js +542 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -508,6 +508,7 @@ __export(index_exports, {
|
|
|
508
508
|
base58Encode: () => base58Encode2,
|
|
509
509
|
buildTxfStorageData: () => buildTxfStorageData,
|
|
510
510
|
bytesToHex: () => bytesToHex2,
|
|
511
|
+
checkNetworkHealth: () => checkNetworkHealth,
|
|
511
512
|
countCommittedTransactions: () => countCommittedTransactions,
|
|
512
513
|
createAddress: () => createAddress,
|
|
513
514
|
createCommunicationsModule: () => createCommunicationsModule,
|
|
@@ -1758,6 +1759,7 @@ async function sendAlpha(wallet, toAddress, amountAlpha, fromAddress) {
|
|
|
1758
1759
|
// modules/payments/L1PaymentsModule.ts
|
|
1759
1760
|
var L1PaymentsModule = class {
|
|
1760
1761
|
_initialized = false;
|
|
1762
|
+
_disabled = false;
|
|
1761
1763
|
_config;
|
|
1762
1764
|
_identity;
|
|
1763
1765
|
_addresses = [];
|
|
@@ -1805,6 +1807,9 @@ var L1PaymentsModule = class {
|
|
|
1805
1807
|
* (e.g. by the address scanner), this is a no-op.
|
|
1806
1808
|
*/
|
|
1807
1809
|
async ensureConnected() {
|
|
1810
|
+
if (this._disabled) {
|
|
1811
|
+
throw new Error("L1 provider is disabled");
|
|
1812
|
+
}
|
|
1808
1813
|
if (!isWebSocketConnected() && this._config.electrumUrl) {
|
|
1809
1814
|
await connect(this._config.electrumUrl);
|
|
1810
1815
|
}
|
|
@@ -1818,6 +1823,24 @@ var L1PaymentsModule = class {
|
|
|
1818
1823
|
this._addresses = [];
|
|
1819
1824
|
this._wallet = void 0;
|
|
1820
1825
|
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Disable this module — disconnect WebSocket and block operations until re-enabled.
|
|
1828
|
+
*/
|
|
1829
|
+
disable() {
|
|
1830
|
+
this._disabled = true;
|
|
1831
|
+
if (isWebSocketConnected()) {
|
|
1832
|
+
disconnect();
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Re-enable this module. Connection will be established lazily on next operation.
|
|
1837
|
+
*/
|
|
1838
|
+
enable() {
|
|
1839
|
+
this._disabled = false;
|
|
1840
|
+
}
|
|
1841
|
+
get disabled() {
|
|
1842
|
+
return this._disabled;
|
|
1843
|
+
}
|
|
1821
1844
|
/**
|
|
1822
1845
|
* Check if a string looks like an L1 address (alpha1... or alphat1...)
|
|
1823
1846
|
*/
|
|
@@ -5678,7 +5701,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5678
5701
|
*/
|
|
5679
5702
|
async getFiatBalance() {
|
|
5680
5703
|
const assets = await this.getAssets();
|
|
5681
|
-
if (!this.priceProvider) {
|
|
5704
|
+
if (!this.priceProvider || this.isPriceDisabled()) {
|
|
5682
5705
|
return null;
|
|
5683
5706
|
}
|
|
5684
5707
|
let total = 0;
|
|
@@ -5713,7 +5736,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5713
5736
|
*/
|
|
5714
5737
|
async getAssets(coinId) {
|
|
5715
5738
|
const rawAssets = this.aggregateTokens(coinId);
|
|
5716
|
-
if (!this.priceProvider || rawAssets.length === 0) {
|
|
5739
|
+
if (!this.priceProvider || this.isPriceDisabled() || rawAssets.length === 0) {
|
|
5717
5740
|
return rawAssets;
|
|
5718
5741
|
}
|
|
5719
5742
|
try {
|
|
@@ -6807,18 +6830,38 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6807
6830
|
}, _PaymentsModule.SYNC_DEBOUNCE_MS);
|
|
6808
6831
|
}
|
|
6809
6832
|
/**
|
|
6810
|
-
* Get all active token storage providers
|
|
6833
|
+
* Get all active (non-disabled) token storage providers
|
|
6811
6834
|
*/
|
|
6812
6835
|
getTokenStorageProviders() {
|
|
6836
|
+
let providers;
|
|
6813
6837
|
if (this.deps.tokenStorageProviders && this.deps.tokenStorageProviders.size > 0) {
|
|
6814
|
-
|
|
6838
|
+
providers = this.deps.tokenStorageProviders;
|
|
6839
|
+
} else if (this.deps.tokenStorage) {
|
|
6840
|
+
providers = /* @__PURE__ */ new Map();
|
|
6841
|
+
providers.set(this.deps.tokenStorage.id, this.deps.tokenStorage);
|
|
6842
|
+
} else {
|
|
6843
|
+
return /* @__PURE__ */ new Map();
|
|
6815
6844
|
}
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6845
|
+
const disabled = this.deps.disabledProviderIds;
|
|
6846
|
+
if (disabled && disabled.size > 0) {
|
|
6847
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
6848
|
+
for (const [id, provider] of providers) {
|
|
6849
|
+
if (!disabled.has(id)) {
|
|
6850
|
+
filtered.set(id, provider);
|
|
6851
|
+
}
|
|
6852
|
+
}
|
|
6853
|
+
return filtered;
|
|
6820
6854
|
}
|
|
6821
|
-
return
|
|
6855
|
+
return providers;
|
|
6856
|
+
}
|
|
6857
|
+
/**
|
|
6858
|
+
* Check if the price provider is disabled via the disabled providers set.
|
|
6859
|
+
*/
|
|
6860
|
+
isPriceDisabled() {
|
|
6861
|
+
const disabled = this.deps?.disabledProviderIds;
|
|
6862
|
+
if (!disabled || disabled.size === 0) return false;
|
|
6863
|
+
const priceId = this.priceProvider?.id ?? "price";
|
|
6864
|
+
return disabled.has(priceId);
|
|
6822
6865
|
}
|
|
6823
6866
|
/**
|
|
6824
6867
|
* Replace the set of token storage providers at runtime.
|
|
@@ -9321,6 +9364,9 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
|
9321
9364
|
};
|
|
9322
9365
|
}
|
|
9323
9366
|
|
|
9367
|
+
// core/Sphere.ts
|
|
9368
|
+
init_network();
|
|
9369
|
+
|
|
9324
9370
|
// serialization/wallet-text.ts
|
|
9325
9371
|
var import_crypto_js7 = __toESM(require("crypto-js"), 1);
|
|
9326
9372
|
|
|
@@ -10049,6 +10095,10 @@ var Sphere = class _Sphere {
|
|
|
10049
10095
|
_groupChat = null;
|
|
10050
10096
|
// Events
|
|
10051
10097
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
10098
|
+
// Provider management
|
|
10099
|
+
_disabledProviders = /* @__PURE__ */ new Set();
|
|
10100
|
+
_providerEventCleanups = [];
|
|
10101
|
+
_lastProviderConnected = /* @__PURE__ */ new Map();
|
|
10052
10102
|
// ===========================================================================
|
|
10053
10103
|
// Constructor (private)
|
|
10054
10104
|
// ===========================================================================
|
|
@@ -10255,9 +10305,14 @@ var Sphere = class _Sphere {
|
|
|
10255
10305
|
throw new Error("Either mnemonic or masterKey is required");
|
|
10256
10306
|
}
|
|
10257
10307
|
console.log("[Sphere.import] Starting import...");
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
10308
|
+
const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
|
|
10309
|
+
if (needsClear) {
|
|
10310
|
+
console.log("[Sphere.import] Clearing existing wallet data...");
|
|
10311
|
+
await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
|
|
10312
|
+
console.log("[Sphere.import] Clear done");
|
|
10313
|
+
} else {
|
|
10314
|
+
console.log("[Sphere.import] No existing wallet \u2014 skipping clear");
|
|
10315
|
+
}
|
|
10261
10316
|
if (!options.storage.isConnected()) {
|
|
10262
10317
|
console.log("[Sphere.import] Reconnecting storage...");
|
|
10263
10318
|
await options.storage.connect();
|
|
@@ -11484,20 +11539,193 @@ var Sphere = class _Sphere {
|
|
|
11484
11539
|
// ===========================================================================
|
|
11485
11540
|
// Public Methods - Status
|
|
11486
11541
|
// ===========================================================================
|
|
11542
|
+
/**
|
|
11543
|
+
* Get aggregated status of all providers, grouped by role.
|
|
11544
|
+
*
|
|
11545
|
+
* @example
|
|
11546
|
+
* ```ts
|
|
11547
|
+
* const status = sphere.getStatus();
|
|
11548
|
+
* // status.transport[0].connected // true/false
|
|
11549
|
+
* // status.transport[0].metadata?.relays // { total: 3, connected: 2 }
|
|
11550
|
+
* // status.tokenStorage // all registered token storage providers
|
|
11551
|
+
* ```
|
|
11552
|
+
*/
|
|
11487
11553
|
getStatus() {
|
|
11554
|
+
const mkInfo = (provider, role, metadata) => ({
|
|
11555
|
+
id: provider.id,
|
|
11556
|
+
name: provider.name,
|
|
11557
|
+
role,
|
|
11558
|
+
status: provider.getStatus(),
|
|
11559
|
+
connected: provider.isConnected(),
|
|
11560
|
+
enabled: !this._disabledProviders.has(provider.id),
|
|
11561
|
+
...metadata ? { metadata } : {}
|
|
11562
|
+
});
|
|
11563
|
+
let transportMeta;
|
|
11564
|
+
const transport = this._transport;
|
|
11565
|
+
if (typeof transport.getRelays === "function") {
|
|
11566
|
+
const total = transport.getRelays().length;
|
|
11567
|
+
const connected = typeof transport.getConnectedRelays === "function" ? transport.getConnectedRelays().length : 0;
|
|
11568
|
+
transportMeta = { relays: { total, connected } };
|
|
11569
|
+
}
|
|
11570
|
+
const l1Module = this._payments.l1;
|
|
11571
|
+
const l1Providers = [];
|
|
11572
|
+
if (l1Module) {
|
|
11573
|
+
const wsConnected = isWebSocketConnected();
|
|
11574
|
+
l1Providers.push({
|
|
11575
|
+
id: "l1-alpha",
|
|
11576
|
+
name: "ALPHA L1",
|
|
11577
|
+
role: "l1",
|
|
11578
|
+
status: wsConnected ? "connected" : "disconnected",
|
|
11579
|
+
connected: wsConnected,
|
|
11580
|
+
enabled: !this._disabledProviders.has("l1-alpha")
|
|
11581
|
+
});
|
|
11582
|
+
}
|
|
11583
|
+
const priceProviders = [];
|
|
11584
|
+
if (this._priceProvider) {
|
|
11585
|
+
priceProviders.push({
|
|
11586
|
+
id: this._priceProviderId,
|
|
11587
|
+
name: this._priceProvider.platform ?? "Price",
|
|
11588
|
+
role: "price",
|
|
11589
|
+
status: "connected",
|
|
11590
|
+
connected: true,
|
|
11591
|
+
enabled: !this._disabledProviders.has(this._priceProviderId)
|
|
11592
|
+
});
|
|
11593
|
+
}
|
|
11488
11594
|
return {
|
|
11489
|
-
storage:
|
|
11490
|
-
|
|
11491
|
-
|
|
11595
|
+
storage: [mkInfo(this._storage, "storage")],
|
|
11596
|
+
tokenStorage: Array.from(this._tokenStorageProviders.values()).map(
|
|
11597
|
+
(p) => mkInfo(p, "token-storage")
|
|
11598
|
+
),
|
|
11599
|
+
transport: [mkInfo(this._transport, "transport", transportMeta)],
|
|
11600
|
+
oracle: [mkInfo(this._oracle, "oracle")],
|
|
11601
|
+
l1: l1Providers,
|
|
11602
|
+
price: priceProviders
|
|
11492
11603
|
};
|
|
11493
11604
|
}
|
|
11494
11605
|
async reconnect() {
|
|
11495
11606
|
await this._transport.disconnect();
|
|
11496
11607
|
await this._transport.connect();
|
|
11608
|
+
}
|
|
11609
|
+
// ===========================================================================
|
|
11610
|
+
// Public Methods - Provider Management
|
|
11611
|
+
// ===========================================================================
|
|
11612
|
+
/**
|
|
11613
|
+
* Disable a provider at runtime. The provider stays registered but is disconnected
|
|
11614
|
+
* and skipped during operations (e.g., sync).
|
|
11615
|
+
*
|
|
11616
|
+
* Main storage provider cannot be disabled.
|
|
11617
|
+
*
|
|
11618
|
+
* @returns true if successfully disabled, false if provider not found
|
|
11619
|
+
*/
|
|
11620
|
+
async disableProvider(providerId) {
|
|
11621
|
+
if (providerId === this._storage.id) {
|
|
11622
|
+
throw new Error("Cannot disable the main storage provider");
|
|
11623
|
+
}
|
|
11624
|
+
const provider = this.findProviderById(providerId);
|
|
11625
|
+
if (!provider) return false;
|
|
11626
|
+
this._disabledProviders.add(providerId);
|
|
11627
|
+
try {
|
|
11628
|
+
if ("disable" in provider && typeof provider.disable === "function") {
|
|
11629
|
+
provider.disable();
|
|
11630
|
+
} else if ("shutdown" in provider && typeof provider.shutdown === "function") {
|
|
11631
|
+
await provider.shutdown();
|
|
11632
|
+
} else if ("disconnect" in provider && typeof provider.disconnect === "function") {
|
|
11633
|
+
await provider.disconnect();
|
|
11634
|
+
} else if ("clearCache" in provider && typeof provider.clearCache === "function") {
|
|
11635
|
+
provider.clearCache();
|
|
11636
|
+
}
|
|
11637
|
+
} catch {
|
|
11638
|
+
}
|
|
11639
|
+
this.emitEvent("connection:changed", {
|
|
11640
|
+
provider: providerId,
|
|
11641
|
+
connected: false,
|
|
11642
|
+
status: "disconnected",
|
|
11643
|
+
enabled: false
|
|
11644
|
+
});
|
|
11645
|
+
return true;
|
|
11646
|
+
}
|
|
11647
|
+
/**
|
|
11648
|
+
* Re-enable a previously disabled provider. Reconnects and resumes operations.
|
|
11649
|
+
*
|
|
11650
|
+
* @returns true if successfully enabled, false if provider not found
|
|
11651
|
+
*/
|
|
11652
|
+
async enableProvider(providerId) {
|
|
11653
|
+
const provider = this.findProviderById(providerId);
|
|
11654
|
+
if (!provider) return false;
|
|
11655
|
+
this._disabledProviders.delete(providerId);
|
|
11656
|
+
if ("enable" in provider && typeof provider.enable === "function") {
|
|
11657
|
+
provider.enable();
|
|
11658
|
+
this.emitEvent("connection:changed", {
|
|
11659
|
+
provider: providerId,
|
|
11660
|
+
connected: false,
|
|
11661
|
+
status: "disconnected",
|
|
11662
|
+
enabled: true
|
|
11663
|
+
});
|
|
11664
|
+
return true;
|
|
11665
|
+
}
|
|
11666
|
+
const hasLifecycle = "connect" in provider && typeof provider.connect === "function" || "initialize" in provider && typeof provider.initialize === "function";
|
|
11667
|
+
if (hasLifecycle) {
|
|
11668
|
+
try {
|
|
11669
|
+
if ("connect" in provider && typeof provider.connect === "function") {
|
|
11670
|
+
await provider.connect();
|
|
11671
|
+
} else if ("initialize" in provider && typeof provider.initialize === "function") {
|
|
11672
|
+
await provider.initialize();
|
|
11673
|
+
}
|
|
11674
|
+
} catch (err) {
|
|
11675
|
+
this.emitEvent("connection:changed", {
|
|
11676
|
+
provider: providerId,
|
|
11677
|
+
connected: false,
|
|
11678
|
+
status: "error",
|
|
11679
|
+
enabled: true,
|
|
11680
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11681
|
+
});
|
|
11682
|
+
return false;
|
|
11683
|
+
}
|
|
11684
|
+
}
|
|
11497
11685
|
this.emitEvent("connection:changed", {
|
|
11498
|
-
provider:
|
|
11499
|
-
connected: true
|
|
11686
|
+
provider: providerId,
|
|
11687
|
+
connected: true,
|
|
11688
|
+
status: "connected",
|
|
11689
|
+
enabled: true
|
|
11500
11690
|
});
|
|
11691
|
+
return true;
|
|
11692
|
+
}
|
|
11693
|
+
/**
|
|
11694
|
+
* Check if a provider is currently enabled
|
|
11695
|
+
*/
|
|
11696
|
+
isProviderEnabled(providerId) {
|
|
11697
|
+
return !this._disabledProviders.has(providerId);
|
|
11698
|
+
}
|
|
11699
|
+
/**
|
|
11700
|
+
* Get the set of disabled provider IDs (for passing to modules)
|
|
11701
|
+
*/
|
|
11702
|
+
getDisabledProviderIds() {
|
|
11703
|
+
return this._disabledProviders;
|
|
11704
|
+
}
|
|
11705
|
+
/** Get the price provider's ID (implementation detail — not on PriceProvider interface) */
|
|
11706
|
+
get _priceProviderId() {
|
|
11707
|
+
if (!this._priceProvider) return "price";
|
|
11708
|
+
const p = this._priceProvider;
|
|
11709
|
+
return typeof p.id === "string" ? p.id : "price";
|
|
11710
|
+
}
|
|
11711
|
+
/**
|
|
11712
|
+
* Find a provider by ID across all provider collections
|
|
11713
|
+
*/
|
|
11714
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11715
|
+
findProviderById(providerId) {
|
|
11716
|
+
if (this._storage.id === providerId) return this._storage;
|
|
11717
|
+
if (this._transport.id === providerId) return this._transport;
|
|
11718
|
+
if (this._oracle.id === providerId) return this._oracle;
|
|
11719
|
+
if (this._tokenStorageProviders.has(providerId)) {
|
|
11720
|
+
return this._tokenStorageProviders.get(providerId);
|
|
11721
|
+
}
|
|
11722
|
+
if (this._priceProvider && this._priceProviderId === providerId) {
|
|
11723
|
+
return this._priceProvider;
|
|
11724
|
+
}
|
|
11725
|
+
if (providerId === "l1-alpha" && this._payments.l1) {
|
|
11726
|
+
return this._payments.l1;
|
|
11727
|
+
}
|
|
11728
|
+
return null;
|
|
11501
11729
|
}
|
|
11502
11730
|
// ===========================================================================
|
|
11503
11731
|
// Public Methods - Events
|
|
@@ -11840,6 +12068,33 @@ var Sphere = class _Sphere {
|
|
|
11840
12068
|
return;
|
|
11841
12069
|
}
|
|
11842
12070
|
try {
|
|
12071
|
+
if (this._identity?.directAddress && this._transport.resolve) {
|
|
12072
|
+
try {
|
|
12073
|
+
const existing = await this._transport.resolve(this._identity.directAddress);
|
|
12074
|
+
if (existing) {
|
|
12075
|
+
if (existing.nametag && !this._identity.nametag) {
|
|
12076
|
+
this._identity.nametag = existing.nametag;
|
|
12077
|
+
await this._updateCachedProxyAddress();
|
|
12078
|
+
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
12079
|
+
let nametags = this._addressNametags.get(entry.addressId);
|
|
12080
|
+
if (!nametags) {
|
|
12081
|
+
nametags = /* @__PURE__ */ new Map();
|
|
12082
|
+
this._addressNametags.set(entry.addressId, nametags);
|
|
12083
|
+
}
|
|
12084
|
+
if (!nametags.has(0)) {
|
|
12085
|
+
nametags.set(0, existing.nametag);
|
|
12086
|
+
await this.persistAddressNametags();
|
|
12087
|
+
}
|
|
12088
|
+
this.emitEvent("nametag:recovered", { nametag: existing.nametag });
|
|
12089
|
+
}
|
|
12090
|
+
console.log("[Sphere] Existing binding found, skipping re-publish");
|
|
12091
|
+
return;
|
|
12092
|
+
}
|
|
12093
|
+
} catch (e) {
|
|
12094
|
+
console.warn("[Sphere] resolve() failed, skipping publish to avoid overwrite", e);
|
|
12095
|
+
return;
|
|
12096
|
+
}
|
|
12097
|
+
}
|
|
11843
12098
|
const nametag = this._identity?.nametag;
|
|
11844
12099
|
const success = await this._transport.publishIdentityBinding(
|
|
11845
12100
|
this._identity.chainPubkey,
|
|
@@ -11913,17 +12168,26 @@ var Sphere = class _Sphere {
|
|
|
11913
12168
|
// Public Methods - Lifecycle
|
|
11914
12169
|
// ===========================================================================
|
|
11915
12170
|
async destroy() {
|
|
12171
|
+
this.cleanupProviderEventSubscriptions();
|
|
11916
12172
|
this._payments.destroy();
|
|
11917
12173
|
this._communications.destroy();
|
|
11918
12174
|
this._groupChat?.destroy();
|
|
11919
12175
|
await this._transport.disconnect();
|
|
11920
12176
|
await this._storage.disconnect();
|
|
11921
12177
|
await this._oracle.disconnect();
|
|
12178
|
+
for (const provider of this._tokenStorageProviders.values()) {
|
|
12179
|
+
try {
|
|
12180
|
+
await provider.shutdown();
|
|
12181
|
+
} catch {
|
|
12182
|
+
}
|
|
12183
|
+
}
|
|
12184
|
+
this._tokenStorageProviders.clear();
|
|
11922
12185
|
this._initialized = false;
|
|
11923
12186
|
this._identity = null;
|
|
11924
12187
|
this._trackedAddresses.clear();
|
|
11925
12188
|
this._addressIdToIndex.clear();
|
|
11926
12189
|
this._addressNametags.clear();
|
|
12190
|
+
this._disabledProviders.clear();
|
|
11927
12191
|
this.eventHandlers.clear();
|
|
11928
12192
|
if (_Sphere.instance === this) {
|
|
11929
12193
|
_Sphere.instance = null;
|
|
@@ -12116,6 +12380,79 @@ var Sphere = class _Sphere {
|
|
|
12116
12380
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
12117
12381
|
await provider.initialize();
|
|
12118
12382
|
}
|
|
12383
|
+
this.subscribeToProviderEvents();
|
|
12384
|
+
}
|
|
12385
|
+
/**
|
|
12386
|
+
* Subscribe to provider-level events and bridge them to Sphere connection:changed events.
|
|
12387
|
+
* Uses deduplication to avoid emitting duplicate events.
|
|
12388
|
+
*/
|
|
12389
|
+
subscribeToProviderEvents() {
|
|
12390
|
+
this.cleanupProviderEventSubscriptions();
|
|
12391
|
+
const transportAny = this._transport;
|
|
12392
|
+
if (typeof transportAny.onEvent === "function") {
|
|
12393
|
+
const unsub = transportAny.onEvent((event) => {
|
|
12394
|
+
const type = event?.type;
|
|
12395
|
+
if (type === "transport:connected") {
|
|
12396
|
+
this.emitConnectionChanged(this._transport.id, true, "connected");
|
|
12397
|
+
} else if (type === "transport:disconnected") {
|
|
12398
|
+
this.emitConnectionChanged(this._transport.id, false, "disconnected");
|
|
12399
|
+
} else if (type === "transport:reconnecting") {
|
|
12400
|
+
this.emitConnectionChanged(this._transport.id, false, "connecting");
|
|
12401
|
+
} else if (type === "transport:error") {
|
|
12402
|
+
this.emitConnectionChanged(this._transport.id, false, "error", event?.error);
|
|
12403
|
+
}
|
|
12404
|
+
});
|
|
12405
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12406
|
+
}
|
|
12407
|
+
const oracleAny = this._oracle;
|
|
12408
|
+
if (typeof oracleAny.onEvent === "function") {
|
|
12409
|
+
const unsub = oracleAny.onEvent((event) => {
|
|
12410
|
+
const type = event?.type;
|
|
12411
|
+
if (type === "oracle:connected") {
|
|
12412
|
+
this.emitConnectionChanged(this._oracle.id, true, "connected");
|
|
12413
|
+
} else if (type === "oracle:disconnected") {
|
|
12414
|
+
this.emitConnectionChanged(this._oracle.id, false, "disconnected");
|
|
12415
|
+
} else if (type === "oracle:error") {
|
|
12416
|
+
this.emitConnectionChanged(this._oracle.id, false, "error", event?.error);
|
|
12417
|
+
}
|
|
12418
|
+
});
|
|
12419
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12420
|
+
}
|
|
12421
|
+
for (const [providerId, provider] of this._tokenStorageProviders) {
|
|
12422
|
+
if (typeof provider.onEvent === "function") {
|
|
12423
|
+
const unsub = provider.onEvent((event) => {
|
|
12424
|
+
if (event.type === "storage:error" || event.type === "sync:error") {
|
|
12425
|
+
this.emitConnectionChanged(providerId, provider.isConnected(), provider.getStatus(), event.error);
|
|
12426
|
+
}
|
|
12427
|
+
});
|
|
12428
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12429
|
+
}
|
|
12430
|
+
}
|
|
12431
|
+
}
|
|
12432
|
+
/**
|
|
12433
|
+
* Emit connection:changed with deduplication — only emits if status actually changed.
|
|
12434
|
+
*/
|
|
12435
|
+
emitConnectionChanged(providerId, connected, status, error) {
|
|
12436
|
+
const lastConnected = this._lastProviderConnected.get(providerId);
|
|
12437
|
+
if (lastConnected === connected) return;
|
|
12438
|
+
this._lastProviderConnected.set(providerId, connected);
|
|
12439
|
+
this.emitEvent("connection:changed", {
|
|
12440
|
+
provider: providerId,
|
|
12441
|
+
connected,
|
|
12442
|
+
status,
|
|
12443
|
+
enabled: !this._disabledProviders.has(providerId),
|
|
12444
|
+
...error ? { error } : {}
|
|
12445
|
+
});
|
|
12446
|
+
}
|
|
12447
|
+
cleanupProviderEventSubscriptions() {
|
|
12448
|
+
for (const cleanup of this._providerEventCleanups) {
|
|
12449
|
+
try {
|
|
12450
|
+
cleanup();
|
|
12451
|
+
} catch {
|
|
12452
|
+
}
|
|
12453
|
+
}
|
|
12454
|
+
this._providerEventCleanups = [];
|
|
12455
|
+
this._lastProviderConnected.clear();
|
|
12119
12456
|
}
|
|
12120
12457
|
async initializeModules() {
|
|
12121
12458
|
const emitEvent = this.emitEvent.bind(this);
|
|
@@ -12128,7 +12465,8 @@ var Sphere = class _Sphere {
|
|
|
12128
12465
|
emitEvent,
|
|
12129
12466
|
// Pass chain code for L1 HD derivation
|
|
12130
12467
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
12131
|
-
price: this._priceProvider ?? void 0
|
|
12468
|
+
price: this._priceProvider ?? void 0,
|
|
12469
|
+
disabledProviderIds: this._disabledProviders
|
|
12132
12470
|
});
|
|
12133
12471
|
this._communications.initialize({
|
|
12134
12472
|
identity: this._identity,
|
|
@@ -12219,6 +12557,192 @@ function formatAmount(amount, options = {}) {
|
|
|
12219
12557
|
// core/index.ts
|
|
12220
12558
|
init_bech32();
|
|
12221
12559
|
|
|
12560
|
+
// core/network-health.ts
|
|
12561
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
12562
|
+
async function checkNetworkHealth(network = "testnet", options) {
|
|
12563
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
12564
|
+
const servicesToCheck = options?.services ?? ["relay", "oracle", "l1"];
|
|
12565
|
+
const networkConfig = NETWORKS[network];
|
|
12566
|
+
const customUrls = options?.urls;
|
|
12567
|
+
const startTime = Date.now();
|
|
12568
|
+
const allChecks = [];
|
|
12569
|
+
if (servicesToCheck.includes("relay")) {
|
|
12570
|
+
const relayUrl = customUrls?.relay ?? networkConfig.nostrRelays[0];
|
|
12571
|
+
allChecks.push(checkWebSocket(relayUrl, timeoutMs).then((r) => ["relay", r]));
|
|
12572
|
+
}
|
|
12573
|
+
if (servicesToCheck.includes("oracle")) {
|
|
12574
|
+
const oracleUrl = customUrls?.oracle ?? networkConfig.aggregatorUrl;
|
|
12575
|
+
allChecks.push(checkOracle(oracleUrl, timeoutMs).then((r) => ["oracle", r]));
|
|
12576
|
+
}
|
|
12577
|
+
if (servicesToCheck.includes("l1")) {
|
|
12578
|
+
const l1Url = customUrls?.l1 ?? networkConfig.electrumUrl;
|
|
12579
|
+
allChecks.push(checkWebSocket(l1Url, timeoutMs).then((r) => ["l1", r]));
|
|
12580
|
+
}
|
|
12581
|
+
if (options?.checks) {
|
|
12582
|
+
for (const [name, checkFn] of Object.entries(options.checks)) {
|
|
12583
|
+
allChecks.push(
|
|
12584
|
+
runCustomCheck(name, checkFn, timeoutMs).then((r) => [name, r])
|
|
12585
|
+
);
|
|
12586
|
+
}
|
|
12587
|
+
}
|
|
12588
|
+
const results = await Promise.allSettled(allChecks);
|
|
12589
|
+
const services = {};
|
|
12590
|
+
let allHealthy = true;
|
|
12591
|
+
for (const result of results) {
|
|
12592
|
+
if (result.status === "fulfilled") {
|
|
12593
|
+
const [name, healthResult] = result.value;
|
|
12594
|
+
services[name] = healthResult;
|
|
12595
|
+
if (!healthResult.healthy) allHealthy = false;
|
|
12596
|
+
} else {
|
|
12597
|
+
allHealthy = false;
|
|
12598
|
+
}
|
|
12599
|
+
}
|
|
12600
|
+
return {
|
|
12601
|
+
healthy: allHealthy,
|
|
12602
|
+
services,
|
|
12603
|
+
totalTimeMs: Date.now() - startTime
|
|
12604
|
+
};
|
|
12605
|
+
}
|
|
12606
|
+
async function checkWebSocket(url, timeoutMs) {
|
|
12607
|
+
const startTime = Date.now();
|
|
12608
|
+
const WS = typeof globalThis !== "undefined" && globalThis.WebSocket;
|
|
12609
|
+
if (!WS) {
|
|
12610
|
+
return {
|
|
12611
|
+
healthy: false,
|
|
12612
|
+
url,
|
|
12613
|
+
responseTimeMs: null,
|
|
12614
|
+
error: "WebSocket not available in this environment"
|
|
12615
|
+
};
|
|
12616
|
+
}
|
|
12617
|
+
return new Promise((resolve) => {
|
|
12618
|
+
let resolved = false;
|
|
12619
|
+
const timer = setTimeout(() => {
|
|
12620
|
+
if (!resolved) {
|
|
12621
|
+
resolved = true;
|
|
12622
|
+
try {
|
|
12623
|
+
ws2.close();
|
|
12624
|
+
} catch {
|
|
12625
|
+
}
|
|
12626
|
+
resolve({
|
|
12627
|
+
healthy: false,
|
|
12628
|
+
url,
|
|
12629
|
+
responseTimeMs: null,
|
|
12630
|
+
error: `Connection timeout after ${timeoutMs}ms`
|
|
12631
|
+
});
|
|
12632
|
+
}
|
|
12633
|
+
}, timeoutMs);
|
|
12634
|
+
let ws2;
|
|
12635
|
+
try {
|
|
12636
|
+
ws2 = new WS(url);
|
|
12637
|
+
} catch (err) {
|
|
12638
|
+
clearTimeout(timer);
|
|
12639
|
+
return resolve({
|
|
12640
|
+
healthy: false,
|
|
12641
|
+
url,
|
|
12642
|
+
responseTimeMs: null,
|
|
12643
|
+
error: `Failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`
|
|
12644
|
+
});
|
|
12645
|
+
}
|
|
12646
|
+
ws2.onopen = () => {
|
|
12647
|
+
if (!resolved) {
|
|
12648
|
+
resolved = true;
|
|
12649
|
+
clearTimeout(timer);
|
|
12650
|
+
const responseTimeMs = Date.now() - startTime;
|
|
12651
|
+
try {
|
|
12652
|
+
ws2.close();
|
|
12653
|
+
} catch {
|
|
12654
|
+
}
|
|
12655
|
+
resolve({ healthy: true, url, responseTimeMs });
|
|
12656
|
+
}
|
|
12657
|
+
};
|
|
12658
|
+
ws2.onerror = (event) => {
|
|
12659
|
+
if (!resolved) {
|
|
12660
|
+
resolved = true;
|
|
12661
|
+
clearTimeout(timer);
|
|
12662
|
+
try {
|
|
12663
|
+
ws2.close();
|
|
12664
|
+
} catch {
|
|
12665
|
+
}
|
|
12666
|
+
resolve({
|
|
12667
|
+
healthy: false,
|
|
12668
|
+
url,
|
|
12669
|
+
responseTimeMs: null,
|
|
12670
|
+
error: "WebSocket connection error"
|
|
12671
|
+
});
|
|
12672
|
+
}
|
|
12673
|
+
};
|
|
12674
|
+
ws2.onclose = (event) => {
|
|
12675
|
+
if (!resolved) {
|
|
12676
|
+
resolved = true;
|
|
12677
|
+
clearTimeout(timer);
|
|
12678
|
+
resolve({
|
|
12679
|
+
healthy: false,
|
|
12680
|
+
url,
|
|
12681
|
+
responseTimeMs: null,
|
|
12682
|
+
error: `WebSocket closed: ${event.reason || `code ${event.code}`}`
|
|
12683
|
+
});
|
|
12684
|
+
}
|
|
12685
|
+
};
|
|
12686
|
+
});
|
|
12687
|
+
}
|
|
12688
|
+
async function checkOracle(url, timeoutMs) {
|
|
12689
|
+
const startTime = Date.now();
|
|
12690
|
+
try {
|
|
12691
|
+
const controller = new AbortController();
|
|
12692
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
12693
|
+
const response = await fetch(url, {
|
|
12694
|
+
method: "POST",
|
|
12695
|
+
headers: { "Content-Type": "application/json" },
|
|
12696
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "get_round_number", params: {} }),
|
|
12697
|
+
signal: controller.signal
|
|
12698
|
+
});
|
|
12699
|
+
clearTimeout(timer);
|
|
12700
|
+
const responseTimeMs = Date.now() - startTime;
|
|
12701
|
+
if (response.ok) {
|
|
12702
|
+
return { healthy: true, url, responseTimeMs };
|
|
12703
|
+
}
|
|
12704
|
+
return {
|
|
12705
|
+
healthy: false,
|
|
12706
|
+
url,
|
|
12707
|
+
responseTimeMs,
|
|
12708
|
+
error: `HTTP ${response.status} ${response.statusText}`
|
|
12709
|
+
};
|
|
12710
|
+
} catch (err) {
|
|
12711
|
+
return {
|
|
12712
|
+
healthy: false,
|
|
12713
|
+
url,
|
|
12714
|
+
responseTimeMs: null,
|
|
12715
|
+
error: err instanceof Error ? err.name === "AbortError" ? `Connection timeout after ${timeoutMs}ms` : err.message : String(err)
|
|
12716
|
+
};
|
|
12717
|
+
}
|
|
12718
|
+
}
|
|
12719
|
+
async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
12720
|
+
try {
|
|
12721
|
+
const result = await Promise.race([
|
|
12722
|
+
checkFn(timeoutMs),
|
|
12723
|
+
new Promise(
|
|
12724
|
+
(resolve) => setTimeout(
|
|
12725
|
+
() => resolve({
|
|
12726
|
+
healthy: false,
|
|
12727
|
+
url: name,
|
|
12728
|
+
responseTimeMs: null,
|
|
12729
|
+
error: `Custom check '${name}' timeout after ${timeoutMs}ms`
|
|
12730
|
+
}),
|
|
12731
|
+
timeoutMs
|
|
12732
|
+
)
|
|
12733
|
+
)
|
|
12734
|
+
]);
|
|
12735
|
+
return result;
|
|
12736
|
+
} catch (err) {
|
|
12737
|
+
return {
|
|
12738
|
+
healthy: false,
|
|
12739
|
+
url: name,
|
|
12740
|
+
responseTimeMs: null,
|
|
12741
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12742
|
+
};
|
|
12743
|
+
}
|
|
12744
|
+
}
|
|
12745
|
+
|
|
12222
12746
|
// types/payment-session.ts
|
|
12223
12747
|
function createPaymentSession(params) {
|
|
12224
12748
|
const now = Date.now();
|
|
@@ -12763,6 +13287,7 @@ function createPriceProvider(config) {
|
|
|
12763
13287
|
base58Encode,
|
|
12764
13288
|
buildTxfStorageData,
|
|
12765
13289
|
bytesToHex,
|
|
13290
|
+
checkNetworkHealth,
|
|
12766
13291
|
countCommittedTransactions,
|
|
12767
13292
|
createAddress,
|
|
12768
13293
|
createCommunicationsModule,
|