@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.
@@ -478,6 +478,7 @@ __export(core_exports, {
478
478
  base58Decode: () => base58Decode,
479
479
  base58Encode: () => base58Encode,
480
480
  bytesToHex: () => bytesToHex2,
481
+ checkNetworkHealth: () => checkNetworkHealth,
481
482
  computeHash160: () => computeHash160,
482
483
  convertBits: () => convertBits,
483
484
  createAddress: () => createAddress,
@@ -1547,6 +1548,7 @@ async function sendAlpha(wallet, toAddress, amountAlpha, fromAddress) {
1547
1548
  // modules/payments/L1PaymentsModule.ts
1548
1549
  var L1PaymentsModule = class {
1549
1550
  _initialized = false;
1551
+ _disabled = false;
1550
1552
  _config;
1551
1553
  _identity;
1552
1554
  _addresses = [];
@@ -1594,6 +1596,9 @@ var L1PaymentsModule = class {
1594
1596
  * (e.g. by the address scanner), this is a no-op.
1595
1597
  */
1596
1598
  async ensureConnected() {
1599
+ if (this._disabled) {
1600
+ throw new Error("L1 provider is disabled");
1601
+ }
1597
1602
  if (!isWebSocketConnected() && this._config.electrumUrl) {
1598
1603
  await connect(this._config.electrumUrl);
1599
1604
  }
@@ -1607,6 +1612,24 @@ var L1PaymentsModule = class {
1607
1612
  this._addresses = [];
1608
1613
  this._wallet = void 0;
1609
1614
  }
1615
+ /**
1616
+ * Disable this module — disconnect WebSocket and block operations until re-enabled.
1617
+ */
1618
+ disable() {
1619
+ this._disabled = true;
1620
+ if (isWebSocketConnected()) {
1621
+ disconnect();
1622
+ }
1623
+ }
1624
+ /**
1625
+ * Re-enable this module. Connection will be established lazily on next operation.
1626
+ */
1627
+ enable() {
1628
+ this._disabled = false;
1629
+ }
1630
+ get disabled() {
1631
+ return this._disabled;
1632
+ }
1610
1633
  /**
1611
1634
  * Check if a string looks like an L1 address (alpha1... or alphat1...)
1612
1635
  */
@@ -5326,7 +5349,7 @@ var PaymentsModule = class _PaymentsModule {
5326
5349
  */
5327
5350
  async getFiatBalance() {
5328
5351
  const assets = await this.getAssets();
5329
- if (!this.priceProvider) {
5352
+ if (!this.priceProvider || this.isPriceDisabled()) {
5330
5353
  return null;
5331
5354
  }
5332
5355
  let total = 0;
@@ -5361,7 +5384,7 @@ var PaymentsModule = class _PaymentsModule {
5361
5384
  */
5362
5385
  async getAssets(coinId) {
5363
5386
  const rawAssets = this.aggregateTokens(coinId);
5364
- if (!this.priceProvider || rawAssets.length === 0) {
5387
+ if (!this.priceProvider || this.isPriceDisabled() || rawAssets.length === 0) {
5365
5388
  return rawAssets;
5366
5389
  }
5367
5390
  try {
@@ -6455,18 +6478,38 @@ var PaymentsModule = class _PaymentsModule {
6455
6478
  }, _PaymentsModule.SYNC_DEBOUNCE_MS);
6456
6479
  }
6457
6480
  /**
6458
- * Get all active token storage providers
6481
+ * Get all active (non-disabled) token storage providers
6459
6482
  */
6460
6483
  getTokenStorageProviders() {
6484
+ let providers;
6461
6485
  if (this.deps.tokenStorageProviders && this.deps.tokenStorageProviders.size > 0) {
6462
- return this.deps.tokenStorageProviders;
6463
- }
6464
- if (this.deps.tokenStorage) {
6465
- const map = /* @__PURE__ */ new Map();
6466
- map.set(this.deps.tokenStorage.id, this.deps.tokenStorage);
6467
- return map;
6486
+ providers = this.deps.tokenStorageProviders;
6487
+ } else if (this.deps.tokenStorage) {
6488
+ providers = /* @__PURE__ */ new Map();
6489
+ providers.set(this.deps.tokenStorage.id, this.deps.tokenStorage);
6490
+ } else {
6491
+ return /* @__PURE__ */ new Map();
6492
+ }
6493
+ const disabled = this.deps.disabledProviderIds;
6494
+ if (disabled && disabled.size > 0) {
6495
+ const filtered = /* @__PURE__ */ new Map();
6496
+ for (const [id, provider] of providers) {
6497
+ if (!disabled.has(id)) {
6498
+ filtered.set(id, provider);
6499
+ }
6500
+ }
6501
+ return filtered;
6468
6502
  }
6469
- return /* @__PURE__ */ new Map();
6503
+ return providers;
6504
+ }
6505
+ /**
6506
+ * Check if the price provider is disabled via the disabled providers set.
6507
+ */
6508
+ isPriceDisabled() {
6509
+ const disabled = this.deps?.disabledProviderIds;
6510
+ if (!disabled || disabled.size === 0) return false;
6511
+ const priceId = this.priceProvider?.id ?? "price";
6512
+ return disabled.has(priceId);
6470
6513
  }
6471
6514
  /**
6472
6515
  * Replace the set of token storage providers at runtime.
@@ -9054,6 +9097,9 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
9054
9097
  };
9055
9098
  }
9056
9099
 
9100
+ // core/Sphere.ts
9101
+ init_network();
9102
+
9057
9103
  // serialization/wallet-text.ts
9058
9104
  var import_crypto_js7 = __toESM(require("crypto-js"), 1);
9059
9105
 
@@ -9782,6 +9828,10 @@ var Sphere = class _Sphere {
9782
9828
  _groupChat = null;
9783
9829
  // Events
9784
9830
  eventHandlers = /* @__PURE__ */ new Map();
9831
+ // Provider management
9832
+ _disabledProviders = /* @__PURE__ */ new Set();
9833
+ _providerEventCleanups = [];
9834
+ _lastProviderConnected = /* @__PURE__ */ new Map();
9785
9835
  // ===========================================================================
9786
9836
  // Constructor (private)
9787
9837
  // ===========================================================================
@@ -9988,9 +10038,14 @@ var Sphere = class _Sphere {
9988
10038
  throw new Error("Either mnemonic or masterKey is required");
9989
10039
  }
9990
10040
  console.log("[Sphere.import] Starting import...");
9991
- console.log("[Sphere.import] Clearing existing wallet data...");
9992
- await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
9993
- console.log("[Sphere.import] Clear done");
10041
+ const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
10042
+ if (needsClear) {
10043
+ console.log("[Sphere.import] Clearing existing wallet data...");
10044
+ await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
10045
+ console.log("[Sphere.import] Clear done");
10046
+ } else {
10047
+ console.log("[Sphere.import] No existing wallet \u2014 skipping clear");
10048
+ }
9994
10049
  if (!options.storage.isConnected()) {
9995
10050
  console.log("[Sphere.import] Reconnecting storage...");
9996
10051
  await options.storage.connect();
@@ -11217,20 +11272,193 @@ var Sphere = class _Sphere {
11217
11272
  // ===========================================================================
11218
11273
  // Public Methods - Status
11219
11274
  // ===========================================================================
11275
+ /**
11276
+ * Get aggregated status of all providers, grouped by role.
11277
+ *
11278
+ * @example
11279
+ * ```ts
11280
+ * const status = sphere.getStatus();
11281
+ * // status.transport[0].connected // true/false
11282
+ * // status.transport[0].metadata?.relays // { total: 3, connected: 2 }
11283
+ * // status.tokenStorage // all registered token storage providers
11284
+ * ```
11285
+ */
11220
11286
  getStatus() {
11287
+ const mkInfo = (provider, role, metadata) => ({
11288
+ id: provider.id,
11289
+ name: provider.name,
11290
+ role,
11291
+ status: provider.getStatus(),
11292
+ connected: provider.isConnected(),
11293
+ enabled: !this._disabledProviders.has(provider.id),
11294
+ ...metadata ? { metadata } : {}
11295
+ });
11296
+ let transportMeta;
11297
+ const transport = this._transport;
11298
+ if (typeof transport.getRelays === "function") {
11299
+ const total = transport.getRelays().length;
11300
+ const connected = typeof transport.getConnectedRelays === "function" ? transport.getConnectedRelays().length : 0;
11301
+ transportMeta = { relays: { total, connected } };
11302
+ }
11303
+ const l1Module = this._payments.l1;
11304
+ const l1Providers = [];
11305
+ if (l1Module) {
11306
+ const wsConnected = isWebSocketConnected();
11307
+ l1Providers.push({
11308
+ id: "l1-alpha",
11309
+ name: "ALPHA L1",
11310
+ role: "l1",
11311
+ status: wsConnected ? "connected" : "disconnected",
11312
+ connected: wsConnected,
11313
+ enabled: !this._disabledProviders.has("l1-alpha")
11314
+ });
11315
+ }
11316
+ const priceProviders = [];
11317
+ if (this._priceProvider) {
11318
+ priceProviders.push({
11319
+ id: this._priceProviderId,
11320
+ name: this._priceProvider.platform ?? "Price",
11321
+ role: "price",
11322
+ status: "connected",
11323
+ connected: true,
11324
+ enabled: !this._disabledProviders.has(this._priceProviderId)
11325
+ });
11326
+ }
11221
11327
  return {
11222
- storage: { connected: this._storage.isConnected() },
11223
- transport: { connected: this._transport.isConnected() },
11224
- oracle: { connected: this._oracle.isConnected() }
11328
+ storage: [mkInfo(this._storage, "storage")],
11329
+ tokenStorage: Array.from(this._tokenStorageProviders.values()).map(
11330
+ (p) => mkInfo(p, "token-storage")
11331
+ ),
11332
+ transport: [mkInfo(this._transport, "transport", transportMeta)],
11333
+ oracle: [mkInfo(this._oracle, "oracle")],
11334
+ l1: l1Providers,
11335
+ price: priceProviders
11225
11336
  };
11226
11337
  }
11227
11338
  async reconnect() {
11228
11339
  await this._transport.disconnect();
11229
11340
  await this._transport.connect();
11341
+ }
11342
+ // ===========================================================================
11343
+ // Public Methods - Provider Management
11344
+ // ===========================================================================
11345
+ /**
11346
+ * Disable a provider at runtime. The provider stays registered but is disconnected
11347
+ * and skipped during operations (e.g., sync).
11348
+ *
11349
+ * Main storage provider cannot be disabled.
11350
+ *
11351
+ * @returns true if successfully disabled, false if provider not found
11352
+ */
11353
+ async disableProvider(providerId) {
11354
+ if (providerId === this._storage.id) {
11355
+ throw new Error("Cannot disable the main storage provider");
11356
+ }
11357
+ const provider = this.findProviderById(providerId);
11358
+ if (!provider) return false;
11359
+ this._disabledProviders.add(providerId);
11360
+ try {
11361
+ if ("disable" in provider && typeof provider.disable === "function") {
11362
+ provider.disable();
11363
+ } else if ("shutdown" in provider && typeof provider.shutdown === "function") {
11364
+ await provider.shutdown();
11365
+ } else if ("disconnect" in provider && typeof provider.disconnect === "function") {
11366
+ await provider.disconnect();
11367
+ } else if ("clearCache" in provider && typeof provider.clearCache === "function") {
11368
+ provider.clearCache();
11369
+ }
11370
+ } catch {
11371
+ }
11372
+ this.emitEvent("connection:changed", {
11373
+ provider: providerId,
11374
+ connected: false,
11375
+ status: "disconnected",
11376
+ enabled: false
11377
+ });
11378
+ return true;
11379
+ }
11380
+ /**
11381
+ * Re-enable a previously disabled provider. Reconnects and resumes operations.
11382
+ *
11383
+ * @returns true if successfully enabled, false if provider not found
11384
+ */
11385
+ async enableProvider(providerId) {
11386
+ const provider = this.findProviderById(providerId);
11387
+ if (!provider) return false;
11388
+ this._disabledProviders.delete(providerId);
11389
+ if ("enable" in provider && typeof provider.enable === "function") {
11390
+ provider.enable();
11391
+ this.emitEvent("connection:changed", {
11392
+ provider: providerId,
11393
+ connected: false,
11394
+ status: "disconnected",
11395
+ enabled: true
11396
+ });
11397
+ return true;
11398
+ }
11399
+ const hasLifecycle = "connect" in provider && typeof provider.connect === "function" || "initialize" in provider && typeof provider.initialize === "function";
11400
+ if (hasLifecycle) {
11401
+ try {
11402
+ if ("connect" in provider && typeof provider.connect === "function") {
11403
+ await provider.connect();
11404
+ } else if ("initialize" in provider && typeof provider.initialize === "function") {
11405
+ await provider.initialize();
11406
+ }
11407
+ } catch (err) {
11408
+ this.emitEvent("connection:changed", {
11409
+ provider: providerId,
11410
+ connected: false,
11411
+ status: "error",
11412
+ enabled: true,
11413
+ error: err instanceof Error ? err.message : String(err)
11414
+ });
11415
+ return false;
11416
+ }
11417
+ }
11230
11418
  this.emitEvent("connection:changed", {
11231
- provider: "transport",
11232
- connected: true
11419
+ provider: providerId,
11420
+ connected: true,
11421
+ status: "connected",
11422
+ enabled: true
11233
11423
  });
11424
+ return true;
11425
+ }
11426
+ /**
11427
+ * Check if a provider is currently enabled
11428
+ */
11429
+ isProviderEnabled(providerId) {
11430
+ return !this._disabledProviders.has(providerId);
11431
+ }
11432
+ /**
11433
+ * Get the set of disabled provider IDs (for passing to modules)
11434
+ */
11435
+ getDisabledProviderIds() {
11436
+ return this._disabledProviders;
11437
+ }
11438
+ /** Get the price provider's ID (implementation detail — not on PriceProvider interface) */
11439
+ get _priceProviderId() {
11440
+ if (!this._priceProvider) return "price";
11441
+ const p = this._priceProvider;
11442
+ return typeof p.id === "string" ? p.id : "price";
11443
+ }
11444
+ /**
11445
+ * Find a provider by ID across all provider collections
11446
+ */
11447
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11448
+ findProviderById(providerId) {
11449
+ if (this._storage.id === providerId) return this._storage;
11450
+ if (this._transport.id === providerId) return this._transport;
11451
+ if (this._oracle.id === providerId) return this._oracle;
11452
+ if (this._tokenStorageProviders.has(providerId)) {
11453
+ return this._tokenStorageProviders.get(providerId);
11454
+ }
11455
+ if (this._priceProvider && this._priceProviderId === providerId) {
11456
+ return this._priceProvider;
11457
+ }
11458
+ if (providerId === "l1-alpha" && this._payments.l1) {
11459
+ return this._payments.l1;
11460
+ }
11461
+ return null;
11234
11462
  }
11235
11463
  // ===========================================================================
11236
11464
  // Public Methods - Events
@@ -11573,6 +11801,33 @@ var Sphere = class _Sphere {
11573
11801
  return;
11574
11802
  }
11575
11803
  try {
11804
+ if (this._identity?.directAddress && this._transport.resolve) {
11805
+ try {
11806
+ const existing = await this._transport.resolve(this._identity.directAddress);
11807
+ if (existing) {
11808
+ if (existing.nametag && !this._identity.nametag) {
11809
+ this._identity.nametag = existing.nametag;
11810
+ await this._updateCachedProxyAddress();
11811
+ const entry = await this.ensureAddressTracked(this._currentAddressIndex);
11812
+ let nametags = this._addressNametags.get(entry.addressId);
11813
+ if (!nametags) {
11814
+ nametags = /* @__PURE__ */ new Map();
11815
+ this._addressNametags.set(entry.addressId, nametags);
11816
+ }
11817
+ if (!nametags.has(0)) {
11818
+ nametags.set(0, existing.nametag);
11819
+ await this.persistAddressNametags();
11820
+ }
11821
+ this.emitEvent("nametag:recovered", { nametag: existing.nametag });
11822
+ }
11823
+ console.log("[Sphere] Existing binding found, skipping re-publish");
11824
+ return;
11825
+ }
11826
+ } catch (e) {
11827
+ console.warn("[Sphere] resolve() failed, skipping publish to avoid overwrite", e);
11828
+ return;
11829
+ }
11830
+ }
11576
11831
  const nametag = this._identity?.nametag;
11577
11832
  const success = await this._transport.publishIdentityBinding(
11578
11833
  this._identity.chainPubkey,
@@ -11646,17 +11901,26 @@ var Sphere = class _Sphere {
11646
11901
  // Public Methods - Lifecycle
11647
11902
  // ===========================================================================
11648
11903
  async destroy() {
11904
+ this.cleanupProviderEventSubscriptions();
11649
11905
  this._payments.destroy();
11650
11906
  this._communications.destroy();
11651
11907
  this._groupChat?.destroy();
11652
11908
  await this._transport.disconnect();
11653
11909
  await this._storage.disconnect();
11654
11910
  await this._oracle.disconnect();
11911
+ for (const provider of this._tokenStorageProviders.values()) {
11912
+ try {
11913
+ await provider.shutdown();
11914
+ } catch {
11915
+ }
11916
+ }
11917
+ this._tokenStorageProviders.clear();
11655
11918
  this._initialized = false;
11656
11919
  this._identity = null;
11657
11920
  this._trackedAddresses.clear();
11658
11921
  this._addressIdToIndex.clear();
11659
11922
  this._addressNametags.clear();
11923
+ this._disabledProviders.clear();
11660
11924
  this.eventHandlers.clear();
11661
11925
  if (_Sphere.instance === this) {
11662
11926
  _Sphere.instance = null;
@@ -11849,6 +12113,79 @@ var Sphere = class _Sphere {
11849
12113
  for (const provider of this._tokenStorageProviders.values()) {
11850
12114
  await provider.initialize();
11851
12115
  }
12116
+ this.subscribeToProviderEvents();
12117
+ }
12118
+ /**
12119
+ * Subscribe to provider-level events and bridge them to Sphere connection:changed events.
12120
+ * Uses deduplication to avoid emitting duplicate events.
12121
+ */
12122
+ subscribeToProviderEvents() {
12123
+ this.cleanupProviderEventSubscriptions();
12124
+ const transportAny = this._transport;
12125
+ if (typeof transportAny.onEvent === "function") {
12126
+ const unsub = transportAny.onEvent((event) => {
12127
+ const type = event?.type;
12128
+ if (type === "transport:connected") {
12129
+ this.emitConnectionChanged(this._transport.id, true, "connected");
12130
+ } else if (type === "transport:disconnected") {
12131
+ this.emitConnectionChanged(this._transport.id, false, "disconnected");
12132
+ } else if (type === "transport:reconnecting") {
12133
+ this.emitConnectionChanged(this._transport.id, false, "connecting");
12134
+ } else if (type === "transport:error") {
12135
+ this.emitConnectionChanged(this._transport.id, false, "error", event?.error);
12136
+ }
12137
+ });
12138
+ if (unsub) this._providerEventCleanups.push(unsub);
12139
+ }
12140
+ const oracleAny = this._oracle;
12141
+ if (typeof oracleAny.onEvent === "function") {
12142
+ const unsub = oracleAny.onEvent((event) => {
12143
+ const type = event?.type;
12144
+ if (type === "oracle:connected") {
12145
+ this.emitConnectionChanged(this._oracle.id, true, "connected");
12146
+ } else if (type === "oracle:disconnected") {
12147
+ this.emitConnectionChanged(this._oracle.id, false, "disconnected");
12148
+ } else if (type === "oracle:error") {
12149
+ this.emitConnectionChanged(this._oracle.id, false, "error", event?.error);
12150
+ }
12151
+ });
12152
+ if (unsub) this._providerEventCleanups.push(unsub);
12153
+ }
12154
+ for (const [providerId, provider] of this._tokenStorageProviders) {
12155
+ if (typeof provider.onEvent === "function") {
12156
+ const unsub = provider.onEvent((event) => {
12157
+ if (event.type === "storage:error" || event.type === "sync:error") {
12158
+ this.emitConnectionChanged(providerId, provider.isConnected(), provider.getStatus(), event.error);
12159
+ }
12160
+ });
12161
+ if (unsub) this._providerEventCleanups.push(unsub);
12162
+ }
12163
+ }
12164
+ }
12165
+ /**
12166
+ * Emit connection:changed with deduplication — only emits if status actually changed.
12167
+ */
12168
+ emitConnectionChanged(providerId, connected, status, error) {
12169
+ const lastConnected = this._lastProviderConnected.get(providerId);
12170
+ if (lastConnected === connected) return;
12171
+ this._lastProviderConnected.set(providerId, connected);
12172
+ this.emitEvent("connection:changed", {
12173
+ provider: providerId,
12174
+ connected,
12175
+ status,
12176
+ enabled: !this._disabledProviders.has(providerId),
12177
+ ...error ? { error } : {}
12178
+ });
12179
+ }
12180
+ cleanupProviderEventSubscriptions() {
12181
+ for (const cleanup of this._providerEventCleanups) {
12182
+ try {
12183
+ cleanup();
12184
+ } catch {
12185
+ }
12186
+ }
12187
+ this._providerEventCleanups = [];
12188
+ this._lastProviderConnected.clear();
11852
12189
  }
11853
12190
  async initializeModules() {
11854
12191
  const emitEvent = this.emitEvent.bind(this);
@@ -11861,7 +12198,8 @@ var Sphere = class _Sphere {
11861
12198
  emitEvent,
11862
12199
  // Pass chain code for L1 HD derivation
11863
12200
  chainCode: this._masterKey?.chainCode || void 0,
11864
- price: this._priceProvider ?? void 0
12201
+ price: this._priceProvider ?? void 0,
12202
+ disabledProviderIds: this._disabledProviders
11865
12203
  });
11866
12204
  this._communications.initialize({
11867
12205
  identity: this._identity,
@@ -11956,6 +12294,192 @@ var CurrencyUtils = {
11956
12294
 
11957
12295
  // core/index.ts
11958
12296
  init_bech32();
12297
+
12298
+ // core/network-health.ts
12299
+ var DEFAULT_TIMEOUT_MS = 5e3;
12300
+ async function checkNetworkHealth(network = "testnet", options) {
12301
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
12302
+ const servicesToCheck = options?.services ?? ["relay", "oracle", "l1"];
12303
+ const networkConfig = NETWORKS[network];
12304
+ const customUrls = options?.urls;
12305
+ const startTime = Date.now();
12306
+ const allChecks = [];
12307
+ if (servicesToCheck.includes("relay")) {
12308
+ const relayUrl = customUrls?.relay ?? networkConfig.nostrRelays[0];
12309
+ allChecks.push(checkWebSocket(relayUrl, timeoutMs).then((r) => ["relay", r]));
12310
+ }
12311
+ if (servicesToCheck.includes("oracle")) {
12312
+ const oracleUrl = customUrls?.oracle ?? networkConfig.aggregatorUrl;
12313
+ allChecks.push(checkOracle(oracleUrl, timeoutMs).then((r) => ["oracle", r]));
12314
+ }
12315
+ if (servicesToCheck.includes("l1")) {
12316
+ const l1Url = customUrls?.l1 ?? networkConfig.electrumUrl;
12317
+ allChecks.push(checkWebSocket(l1Url, timeoutMs).then((r) => ["l1", r]));
12318
+ }
12319
+ if (options?.checks) {
12320
+ for (const [name, checkFn] of Object.entries(options.checks)) {
12321
+ allChecks.push(
12322
+ runCustomCheck(name, checkFn, timeoutMs).then((r) => [name, r])
12323
+ );
12324
+ }
12325
+ }
12326
+ const results = await Promise.allSettled(allChecks);
12327
+ const services = {};
12328
+ let allHealthy = true;
12329
+ for (const result of results) {
12330
+ if (result.status === "fulfilled") {
12331
+ const [name, healthResult] = result.value;
12332
+ services[name] = healthResult;
12333
+ if (!healthResult.healthy) allHealthy = false;
12334
+ } else {
12335
+ allHealthy = false;
12336
+ }
12337
+ }
12338
+ return {
12339
+ healthy: allHealthy,
12340
+ services,
12341
+ totalTimeMs: Date.now() - startTime
12342
+ };
12343
+ }
12344
+ async function checkWebSocket(url, timeoutMs) {
12345
+ const startTime = Date.now();
12346
+ const WS = typeof globalThis !== "undefined" && globalThis.WebSocket;
12347
+ if (!WS) {
12348
+ return {
12349
+ healthy: false,
12350
+ url,
12351
+ responseTimeMs: null,
12352
+ error: "WebSocket not available in this environment"
12353
+ };
12354
+ }
12355
+ return new Promise((resolve) => {
12356
+ let resolved = false;
12357
+ const timer = setTimeout(() => {
12358
+ if (!resolved) {
12359
+ resolved = true;
12360
+ try {
12361
+ ws2.close();
12362
+ } catch {
12363
+ }
12364
+ resolve({
12365
+ healthy: false,
12366
+ url,
12367
+ responseTimeMs: null,
12368
+ error: `Connection timeout after ${timeoutMs}ms`
12369
+ });
12370
+ }
12371
+ }, timeoutMs);
12372
+ let ws2;
12373
+ try {
12374
+ ws2 = new WS(url);
12375
+ } catch (err) {
12376
+ clearTimeout(timer);
12377
+ return resolve({
12378
+ healthy: false,
12379
+ url,
12380
+ responseTimeMs: null,
12381
+ error: `Failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`
12382
+ });
12383
+ }
12384
+ ws2.onopen = () => {
12385
+ if (!resolved) {
12386
+ resolved = true;
12387
+ clearTimeout(timer);
12388
+ const responseTimeMs = Date.now() - startTime;
12389
+ try {
12390
+ ws2.close();
12391
+ } catch {
12392
+ }
12393
+ resolve({ healthy: true, url, responseTimeMs });
12394
+ }
12395
+ };
12396
+ ws2.onerror = (event) => {
12397
+ if (!resolved) {
12398
+ resolved = true;
12399
+ clearTimeout(timer);
12400
+ try {
12401
+ ws2.close();
12402
+ } catch {
12403
+ }
12404
+ resolve({
12405
+ healthy: false,
12406
+ url,
12407
+ responseTimeMs: null,
12408
+ error: "WebSocket connection error"
12409
+ });
12410
+ }
12411
+ };
12412
+ ws2.onclose = (event) => {
12413
+ if (!resolved) {
12414
+ resolved = true;
12415
+ clearTimeout(timer);
12416
+ resolve({
12417
+ healthy: false,
12418
+ url,
12419
+ responseTimeMs: null,
12420
+ error: `WebSocket closed: ${event.reason || `code ${event.code}`}`
12421
+ });
12422
+ }
12423
+ };
12424
+ });
12425
+ }
12426
+ async function checkOracle(url, timeoutMs) {
12427
+ const startTime = Date.now();
12428
+ try {
12429
+ const controller = new AbortController();
12430
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
12431
+ const response = await fetch(url, {
12432
+ method: "POST",
12433
+ headers: { "Content-Type": "application/json" },
12434
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "get_round_number", params: {} }),
12435
+ signal: controller.signal
12436
+ });
12437
+ clearTimeout(timer);
12438
+ const responseTimeMs = Date.now() - startTime;
12439
+ if (response.ok) {
12440
+ return { healthy: true, url, responseTimeMs };
12441
+ }
12442
+ return {
12443
+ healthy: false,
12444
+ url,
12445
+ responseTimeMs,
12446
+ error: `HTTP ${response.status} ${response.statusText}`
12447
+ };
12448
+ } catch (err) {
12449
+ return {
12450
+ healthy: false,
12451
+ url,
12452
+ responseTimeMs: null,
12453
+ error: err instanceof Error ? err.name === "AbortError" ? `Connection timeout after ${timeoutMs}ms` : err.message : String(err)
12454
+ };
12455
+ }
12456
+ }
12457
+ async function runCustomCheck(name, checkFn, timeoutMs) {
12458
+ try {
12459
+ const result = await Promise.race([
12460
+ checkFn(timeoutMs),
12461
+ new Promise(
12462
+ (resolve) => setTimeout(
12463
+ () => resolve({
12464
+ healthy: false,
12465
+ url: name,
12466
+ responseTimeMs: null,
12467
+ error: `Custom check '${name}' timeout after ${timeoutMs}ms`
12468
+ }),
12469
+ timeoutMs
12470
+ )
12471
+ )
12472
+ ]);
12473
+ return result;
12474
+ } catch (err) {
12475
+ return {
12476
+ healthy: false,
12477
+ url: name,
12478
+ responseTimeMs: null,
12479
+ error: err instanceof Error ? err.message : String(err)
12480
+ };
12481
+ }
12482
+ }
11959
12483
  // Annotate the CommonJS export names for ESM import in node:
11960
12484
  0 && (module.exports = {
11961
12485
  CHARSET,
@@ -11966,6 +12490,7 @@ init_bech32();
11966
12490
  base58Decode,
11967
12491
  base58Encode,
11968
12492
  bytesToHex,
12493
+ checkNetworkHealth,
11969
12494
  computeHash160,
11970
12495
  convertBits,
11971
12496
  createAddress,