@le-space/ui 0.9.4 → 0.9.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/ui",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "Floating Aleph Relay Button UI components for Svelte and React.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,8 +44,8 @@
44
44
  "url": "https://github.com/NiKrause/relay-button/issues"
45
45
  },
46
46
  "dependencies": {
47
- "@le-space/browser": "0.9.4",
48
- "@le-space/core": "0.9.4",
47
+ "@le-space/browser": "0.9.6",
48
+ "@le-space/core": "0.9.6",
49
49
  "viem": "^2.53.1"
50
50
  },
51
51
  "peerDependencies": {
package/react/index.js CHANGED
@@ -96,7 +96,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
96
96
  var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
97
97
 
98
98
  // src/shared/package-version.ts
99
- var UI_PACKAGE_VERSION = "0.9.4";
99
+ var UI_PACKAGE_VERSION = "0.9.6";
100
100
 
101
101
  // src/shared/format.ts
102
102
  function shortHash(value, head = 8, tail = 6) {
@@ -3207,20 +3207,24 @@ async function fetchAlephBootstrapPosts(options = {}) {
3207
3207
  }
3208
3208
  const payload = await response.json();
3209
3209
  const posts = (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
3210
- posts.forEach(assertSupportedRelayBootstrapPost);
3211
- return posts;
3210
+ return usableRelayBootstrapPosts(posts, options.onUnsupportedPost);
3212
3211
  }
3213
- function assertSupportedRelayBootstrapPost(post) {
3212
+ function unsupportedRelayBootstrapReason(post) {
3214
3213
  if (post.type === "relay-bootstrap") {
3215
- throw new Error(
3216
- "Legacy relay-bootstrap record encountered. Only relay-bootstrap-v2 is supported."
3217
- );
3214
+ return "legacy relay-bootstrap record; only relay-bootstrap-v2 is supported";
3218
3215
  }
3219
3216
  if (post.type && post.type !== DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE) {
3220
- throw new Error(
3221
- `Unsupported relay bootstrap post type: ${post.type}. Expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}.`
3222
- );
3217
+ return `unsupported post type ${post.type}; expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}`;
3223
3218
  }
3219
+ return null;
3220
+ }
3221
+ function usableRelayBootstrapPosts(posts, onUnsupportedPost) {
3222
+ return posts.filter((post) => {
3223
+ const reason = unsupportedRelayBootstrapReason(post);
3224
+ if (!reason) return true;
3225
+ onUnsupportedPost?.({ hash: post.itemHash ?? post.hash, reason });
3226
+ return false;
3227
+ });
3224
3228
  }
3225
3229
  function compareRelayBootstrapPostRecency(left, right) {
3226
3230
  const leftUpdatedAt = left.content?.updatedAt ?? 0;
@@ -3241,8 +3245,7 @@ function selectCurrentRelayBootstrapPosts(posts, options = {}) {
3241
3245
  const maxAgeMs = options.maxAgeMs ?? DEFAULT_BOOTSTRAP_MAX_AGE_MS;
3242
3246
  const now = options.now ?? Date.now();
3243
3247
  const selected = /* @__PURE__ */ new Map();
3244
- for (const post of posts) {
3245
- assertSupportedRelayBootstrapPost(post);
3248
+ for (const post of usableRelayBootstrapPosts(posts, options.onUnsupportedPost)) {
3246
3249
  const content = post.content;
3247
3250
  if (!content) continue;
3248
3251
  if (now - content.updatedAt > maxAgeMs) continue;
@@ -3959,8 +3962,29 @@ function describeRequest(input, init) {
3959
3962
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : typeof input?.url === "string" ? input.url : "<unknown url>";
3960
3963
  return `${method} ${url}`;
3961
3964
  }
3965
+ function describeThrown(error) {
3966
+ if (error instanceof Error) {
3967
+ const cause = error.cause;
3968
+ const causeText = cause != null && cause !== error ? ` (cause: ${describeThrown(cause)})` : "";
3969
+ return `${error.message}${causeText}`;
3970
+ }
3971
+ if (typeof error === "object" && error !== null) {
3972
+ const message = error.message;
3973
+ if (typeof message === "string" && message.trim()) return message;
3974
+ try {
3975
+ const json = JSON.stringify(error);
3976
+ if (json && json !== "{}") {
3977
+ return json.length > 400 ? `${json.slice(0, 400)}\u2026` : json;
3978
+ }
3979
+ } catch {
3980
+ }
3981
+ const name = error.constructor?.name;
3982
+ return name && name !== "Object" ? `<${name}>` : "<object with no message>";
3983
+ }
3984
+ return String(error);
3985
+ }
3962
3986
  function deploymentFailureMessage(error, stage) {
3963
- const message = error instanceof Error ? error.message : String(error);
3987
+ const message = describeThrown(error);
3964
3988
  const step = String(stage ?? "").trim();
3965
3989
  return step ? `${message} (while: ${step})` : message;
3966
3990
  }
@@ -3972,7 +3996,7 @@ function asJsonFetch(input, init) {
3972
3996
  json: async () => await response.json()
3973
3997
  }),
3974
3998
  (error) => {
3975
- const reason = error instanceof Error ? error.message : String(error);
3999
+ const reason = describeThrown(error);
3976
4000
  throw new Error(`${reason} (${describeRequest(input, init)})`, {
3977
4001
  cause: error
3978
4002
  });
@@ -4160,7 +4184,7 @@ function defaultState(props = {}) {
4160
4184
  };
4161
4185
  }
4162
4186
  function manifestLoadErrorState(error) {
4163
- const message = error instanceof Error ? error.message : String(error);
4187
+ const message = describeThrown(error);
4164
4188
  return {
4165
4189
  manifest: null,
4166
4190
  valid: false,
@@ -4205,7 +4229,7 @@ function hostnameOfUrl(value) {
4205
4229
  }
4206
4230
  var INSTANCE_ADDRESS_TIMEOUT_MS = 1e4;
4207
4231
  function describeInstanceAddressFailure(error, sourceUrl) {
4208
- const raw = error instanceof Error ? error.message : String(error);
4232
+ const raw = describeThrown(error);
4209
4233
  if (error instanceof Error && error.name === "AbortError") {
4210
4234
  return `${sourceUrl} did not answer within ${INSTANCE_ADDRESS_TIMEOUT_MS / 1e3}s. Press Refresh to try again.`;
4211
4235
  }
@@ -4504,7 +4528,7 @@ async function inspectInstanceRuntime(args) {
4504
4528
  return {
4505
4529
  details: {
4506
4530
  ...details,
4507
- error: error instanceof Error ? error.message : String(error)
4531
+ error: describeThrown(error)
4508
4532
  },
4509
4533
  lookup
4510
4534
  };
@@ -5333,7 +5357,7 @@ var SponsorRelayController = class {
5333
5357
  this.trace("deploy:bootstrap-config-cleanup-error", {
5334
5358
  itemHash: args.itemHash,
5335
5359
  deploymentToken: args.deploymentToken,
5336
- error: error instanceof Error ? error.message : String(error)
5360
+ error: describeThrown(error)
5337
5361
  });
5338
5362
  });
5339
5363
  await this.cleanupStaleVmBootstrapConfigHistory({
@@ -5401,7 +5425,7 @@ var SponsorRelayController = class {
5401
5425
  this.trace("deploy:bootstrap-config-history-scan-error", {
5402
5426
  itemHash: args.itemHash,
5403
5427
  currentAggregateItemHash: args.currentAggregateItemHash,
5404
- error: error instanceof Error ? error.message : String(error)
5428
+ error: describeThrown(error)
5405
5429
  });
5406
5430
  return [];
5407
5431
  });
@@ -5444,7 +5468,7 @@ var SponsorRelayController = class {
5444
5468
  itemHash: args.itemHash,
5445
5469
  currentAggregateItemHash: args.currentAggregateItemHash,
5446
5470
  staleHashes,
5447
- error: error instanceof Error ? error.message : String(error)
5471
+ error: describeThrown(error)
5448
5472
  });
5449
5473
  });
5450
5474
  }
@@ -5765,7 +5789,7 @@ var SponsorRelayController = class {
5765
5789
  } catch (error) {
5766
5790
  this.patch({
5767
5791
  busy: { connectingWallet: false },
5768
- errorText: error instanceof Error ? error.message : String(error),
5792
+ errorText: describeThrown(error),
5769
5793
  statusText: "Wallet connection failed"
5770
5794
  });
5771
5795
  }
@@ -5981,7 +6005,7 @@ var SponsorRelayController = class {
5981
6005
  this.trace("refresh:error", error);
5982
6006
  this.patch({
5983
6007
  busy: { refreshing: false },
5984
- errorText: error instanceof Error ? error.message : String(error),
6008
+ errorText: describeThrown(error),
5985
6009
  statusText: "Refresh failed"
5986
6010
  });
5987
6011
  }
@@ -6316,7 +6340,7 @@ var SponsorRelayController = class {
6316
6340
  });
6317
6341
  return;
6318
6342
  } catch (error) {
6319
- lastError = error instanceof Error ? error : new Error(String(error));
6343
+ lastError = error instanceof Error ? error : new Error(describeThrown(error), { cause: error });
6320
6344
  const attemptLabel = crnDisplayName;
6321
6345
  const failedDueToInvalidIpv6 = /not globally routable|public guest IPv6/i.test(lastError.message);
6322
6346
  attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
@@ -6745,7 +6769,7 @@ var SponsorRelayController = class {
6745
6769
  this.trace("delete:error", error);
6746
6770
  this.patch({
6747
6771
  busy: { deletingInstanceHash: null },
6748
- errorText: error instanceof Error ? error.message : String(error),
6772
+ errorText: describeThrown(error),
6749
6773
  statusText: "Delete failed"
6750
6774
  });
6751
6775
  }
@@ -6811,7 +6835,7 @@ var SponsorRelayController = class {
6811
6835
  this.trace("registration-delete:error", error);
6812
6836
  this.patch({
6813
6837
  busy: { deletingRegistrationHash: null },
6814
- errorText: error instanceof Error ? error.message : String(error),
6838
+ errorText: describeThrown(error),
6815
6839
  statusText: "Registration cleanup failed"
6816
6840
  });
6817
6841
  }
package/shared/index.d.ts CHANGED
@@ -480,7 +480,7 @@ declare const STALE_INSTANCE_ALLOCATION_COOLDOWN_MS: number;
480
480
  declare const RECENT_INSTANCE_RUNTIME_GRACE_MS: number;
481
481
  declare const DEPLOYMENT_PENDING_WARNING_MS: number;
482
482
 
483
- declare const UI_PACKAGE_VERSION = "0.9.4";
483
+ declare const UI_PACKAGE_VERSION = "0.9.6";
484
484
 
485
485
  interface RootfsRequiredPortForward {
486
486
  port: number;
@@ -539,6 +539,24 @@ declare function describeRequest(input: RequestInfo | URL, init?: RequestInit):
539
539
  *
540
540
  * Exported for its test.
541
541
  */
542
+ /**
543
+ * Say what was thrown, even when it was not an `Error`.
544
+ *
545
+ * `String(value)` on a plain object is `"[object Object]"`, and that is what a
546
+ * user saw when every CRN failed at once: five candidates, five identical
547
+ * `[object Object]`, and no way to tell whether they had failed for one reason
548
+ * or five. Not every rejection in this path is an `Error` — an Aleph or CRN
549
+ * response rejected as `{ code, message }` is an object, and so is anything a
550
+ * dependency throws as a bare literal.
551
+ *
552
+ * Order matters: a `message` field is what the thrower meant to say, so it wins
553
+ * over a dump of the whole object. The dump is the fallback, truncated, because
554
+ * an unreadable line is still better than a line saying nothing — and a cyclic
555
+ * object must not take the error handler down with it.
556
+ *
557
+ * Exported for its test.
558
+ */
559
+ declare function describeThrown(error: unknown): string;
542
560
  declare function deploymentFailureMessage(error: unknown, stage: string | null | undefined): string;
543
561
  /**
544
562
  * Normalise the two address documents in the fleet into one list of groups.
@@ -628,4 +646,4 @@ interface DeploymentProgressEmitter {
628
646
  }
629
647
  declare function createDeploymentProgressEmitter(): DeploymentProgressEmitter;
630
648
 
631
- export { type CompactBootstrapRegistrationRecord, type CompactInstanceDetails, type CompactInstanceRecord, DEFAULT_FAB_CORNER, DEFAULT_INSTANCE_NAME, DEFAULT_MANIFEST_URL, DEFAULT_TIER_ID, DEFAULT_UCAN_STORE_ALLOWED_CAPABILITIES, DEFAULT_UCAN_STORE_MAX_DELEGATION_EXPIRATION_SECONDS, DEFAULT_UCAN_STORE_PWA_ORIGIN, DEFAULT_UCAN_STORE_SERVICE_DID, DEFAULT_UCAN_STORE_SERVICE_ORIGIN, DEFAULT_UCAN_STORE_USER_DELEGATION_EXPIRATION_SECONDS, DEPLOYMENT_PENDING_WARNING_MS, type DeploymentProgressEmitter, type EthereumProviderLike, FAB_DRAG_THRESHOLD, FAB_EDGE_MARGIN, type FabCorner, type FabPanelPlacement, type FabPanelSide, type FabPlacement, type FabPoint, type FabSize, type FabViewport, IDLE_DEPLOYMENT_PROGRESS, INSTANCE_RUNTIME_PENDING_REFRESH_MS, INSTANCE_RUNTIME_READY_REFRESH_MS, INSTANCE_RUNTIME_STALE_REFRESH_MS, MANIFEST_SOURCE_REFRESH_DEBOUNCE_MS, RECENT_INSTANCE_RUNTIME_GRACE_MS, REFRESH_INTERVAL_MS, ROOTFS_MISSING_STATE, STALE_INSTANCE_ALLOCATION_COOLDOWN_MS, type SponsorRelayAddressGroup, SponsorRelayController, type SponsorRelayHealthTone, type SponsorRelayInstanceAddresses, type SponsorRelayPricingSummary, type SponsorRelayProps, type SponsorRelayRootfsHealth, type SponsorRelayState, type SponsorRelaySubscriber, type SponsorRelayUcanStoreBootstrapInput, type SponsorRelayWalletState, UI_PACKAGE_VERSION, anchorPanelToLauncher, buildSshCommand, clampToViewport, connectWallet, createDeploymentProgressEmitter, createSponsorRelayController, deploymentFailureMessage, describeRequest, dragExceedsThreshold, formatDateTime, formatNumber, formatTierSpecLabel, getEthereumProvider, joinMappedPorts, joinRequiredPortForwards, parseInstanceAddressPayload, personalSign, readStoredPosition, resolveCornerPlacement, resolveManifestSource, rootfsHealth, shortHash, toChecksumAddress, watchWallet, writeStoredPosition };
649
+ export { type CompactBootstrapRegistrationRecord, type CompactInstanceDetails, type CompactInstanceRecord, DEFAULT_FAB_CORNER, DEFAULT_INSTANCE_NAME, DEFAULT_MANIFEST_URL, DEFAULT_TIER_ID, DEFAULT_UCAN_STORE_ALLOWED_CAPABILITIES, DEFAULT_UCAN_STORE_MAX_DELEGATION_EXPIRATION_SECONDS, DEFAULT_UCAN_STORE_PWA_ORIGIN, DEFAULT_UCAN_STORE_SERVICE_DID, DEFAULT_UCAN_STORE_SERVICE_ORIGIN, DEFAULT_UCAN_STORE_USER_DELEGATION_EXPIRATION_SECONDS, DEPLOYMENT_PENDING_WARNING_MS, type DeploymentProgressEmitter, type EthereumProviderLike, FAB_DRAG_THRESHOLD, FAB_EDGE_MARGIN, type FabCorner, type FabPanelPlacement, type FabPanelSide, type FabPlacement, type FabPoint, type FabSize, type FabViewport, IDLE_DEPLOYMENT_PROGRESS, INSTANCE_RUNTIME_PENDING_REFRESH_MS, INSTANCE_RUNTIME_READY_REFRESH_MS, INSTANCE_RUNTIME_STALE_REFRESH_MS, MANIFEST_SOURCE_REFRESH_DEBOUNCE_MS, RECENT_INSTANCE_RUNTIME_GRACE_MS, REFRESH_INTERVAL_MS, ROOTFS_MISSING_STATE, STALE_INSTANCE_ALLOCATION_COOLDOWN_MS, type SponsorRelayAddressGroup, SponsorRelayController, type SponsorRelayHealthTone, type SponsorRelayInstanceAddresses, type SponsorRelayPricingSummary, type SponsorRelayProps, type SponsorRelayRootfsHealth, type SponsorRelayState, type SponsorRelaySubscriber, type SponsorRelayUcanStoreBootstrapInput, type SponsorRelayWalletState, UI_PACKAGE_VERSION, anchorPanelToLauncher, buildSshCommand, clampToViewport, connectWallet, createDeploymentProgressEmitter, createSponsorRelayController, deploymentFailureMessage, describeRequest, describeThrown, dragExceedsThreshold, formatDateTime, formatNumber, formatTierSpecLabel, getEthereumProvider, joinMappedPorts, joinRequiredPortForwards, parseInstanceAddressPayload, personalSign, readStoredPosition, resolveCornerPlacement, resolveManifestSource, rootfsHealth, shortHash, toChecksumAddress, watchWallet, writeStoredPosition };
package/shared/index.js CHANGED
@@ -96,7 +96,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
96
96
  var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
97
97
 
98
98
  // src/shared/package-version.ts
99
- var UI_PACKAGE_VERSION = "0.9.4";
99
+ var UI_PACKAGE_VERSION = "0.9.6";
100
100
 
101
101
  // src/shared/format.ts
102
102
  function shortHash(value, head = 8, tail = 6) {
@@ -3207,20 +3207,24 @@ async function fetchAlephBootstrapPosts(options = {}) {
3207
3207
  }
3208
3208
  const payload = await response.json();
3209
3209
  const posts = (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
3210
- posts.forEach(assertSupportedRelayBootstrapPost);
3211
- return posts;
3210
+ return usableRelayBootstrapPosts(posts, options.onUnsupportedPost);
3212
3211
  }
3213
- function assertSupportedRelayBootstrapPost(post) {
3212
+ function unsupportedRelayBootstrapReason(post) {
3214
3213
  if (post.type === "relay-bootstrap") {
3215
- throw new Error(
3216
- "Legacy relay-bootstrap record encountered. Only relay-bootstrap-v2 is supported."
3217
- );
3214
+ return "legacy relay-bootstrap record; only relay-bootstrap-v2 is supported";
3218
3215
  }
3219
3216
  if (post.type && post.type !== DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE) {
3220
- throw new Error(
3221
- `Unsupported relay bootstrap post type: ${post.type}. Expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}.`
3222
- );
3217
+ return `unsupported post type ${post.type}; expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}`;
3223
3218
  }
3219
+ return null;
3220
+ }
3221
+ function usableRelayBootstrapPosts(posts, onUnsupportedPost) {
3222
+ return posts.filter((post) => {
3223
+ const reason = unsupportedRelayBootstrapReason(post);
3224
+ if (!reason) return true;
3225
+ onUnsupportedPost?.({ hash: post.itemHash ?? post.hash, reason });
3226
+ return false;
3227
+ });
3224
3228
  }
3225
3229
  function compareRelayBootstrapPostRecency(left, right) {
3226
3230
  const leftUpdatedAt = left.content?.updatedAt ?? 0;
@@ -3241,8 +3245,7 @@ function selectCurrentRelayBootstrapPosts(posts, options = {}) {
3241
3245
  const maxAgeMs = options.maxAgeMs ?? DEFAULT_BOOTSTRAP_MAX_AGE_MS;
3242
3246
  const now = options.now ?? Date.now();
3243
3247
  const selected = /* @__PURE__ */ new Map();
3244
- for (const post of posts) {
3245
- assertSupportedRelayBootstrapPost(post);
3248
+ for (const post of usableRelayBootstrapPosts(posts, options.onUnsupportedPost)) {
3246
3249
  const content = post.content;
3247
3250
  if (!content) continue;
3248
3251
  if (now - content.updatedAt > maxAgeMs) continue;
@@ -3959,8 +3962,29 @@ function describeRequest(input, init) {
3959
3962
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : typeof input?.url === "string" ? input.url : "<unknown url>";
3960
3963
  return `${method} ${url}`;
3961
3964
  }
3965
+ function describeThrown(error) {
3966
+ if (error instanceof Error) {
3967
+ const cause = error.cause;
3968
+ const causeText = cause != null && cause !== error ? ` (cause: ${describeThrown(cause)})` : "";
3969
+ return `${error.message}${causeText}`;
3970
+ }
3971
+ if (typeof error === "object" && error !== null) {
3972
+ const message = error.message;
3973
+ if (typeof message === "string" && message.trim()) return message;
3974
+ try {
3975
+ const json = JSON.stringify(error);
3976
+ if (json && json !== "{}") {
3977
+ return json.length > 400 ? `${json.slice(0, 400)}\u2026` : json;
3978
+ }
3979
+ } catch {
3980
+ }
3981
+ const name = error.constructor?.name;
3982
+ return name && name !== "Object" ? `<${name}>` : "<object with no message>";
3983
+ }
3984
+ return String(error);
3985
+ }
3962
3986
  function deploymentFailureMessage(error, stage) {
3963
- const message = error instanceof Error ? error.message : String(error);
3987
+ const message = describeThrown(error);
3964
3988
  const step = String(stage ?? "").trim();
3965
3989
  return step ? `${message} (while: ${step})` : message;
3966
3990
  }
@@ -3972,7 +3996,7 @@ function asJsonFetch(input, init) {
3972
3996
  json: async () => await response.json()
3973
3997
  }),
3974
3998
  (error) => {
3975
- const reason = error instanceof Error ? error.message : String(error);
3999
+ const reason = describeThrown(error);
3976
4000
  throw new Error(`${reason} (${describeRequest(input, init)})`, {
3977
4001
  cause: error
3978
4002
  });
@@ -4160,7 +4184,7 @@ function defaultState(props = {}) {
4160
4184
  };
4161
4185
  }
4162
4186
  function manifestLoadErrorState(error) {
4163
- const message = error instanceof Error ? error.message : String(error);
4187
+ const message = describeThrown(error);
4164
4188
  return {
4165
4189
  manifest: null,
4166
4190
  valid: false,
@@ -4205,7 +4229,7 @@ function hostnameOfUrl(value) {
4205
4229
  }
4206
4230
  var INSTANCE_ADDRESS_TIMEOUT_MS = 1e4;
4207
4231
  function describeInstanceAddressFailure(error, sourceUrl) {
4208
- const raw = error instanceof Error ? error.message : String(error);
4232
+ const raw = describeThrown(error);
4209
4233
  if (error instanceof Error && error.name === "AbortError") {
4210
4234
  return `${sourceUrl} did not answer within ${INSTANCE_ADDRESS_TIMEOUT_MS / 1e3}s. Press Refresh to try again.`;
4211
4235
  }
@@ -4504,7 +4528,7 @@ async function inspectInstanceRuntime(args) {
4504
4528
  return {
4505
4529
  details: {
4506
4530
  ...details,
4507
- error: error instanceof Error ? error.message : String(error)
4531
+ error: describeThrown(error)
4508
4532
  },
4509
4533
  lookup
4510
4534
  };
@@ -5333,7 +5357,7 @@ var SponsorRelayController = class {
5333
5357
  this.trace("deploy:bootstrap-config-cleanup-error", {
5334
5358
  itemHash: args.itemHash,
5335
5359
  deploymentToken: args.deploymentToken,
5336
- error: error instanceof Error ? error.message : String(error)
5360
+ error: describeThrown(error)
5337
5361
  });
5338
5362
  });
5339
5363
  await this.cleanupStaleVmBootstrapConfigHistory({
@@ -5401,7 +5425,7 @@ var SponsorRelayController = class {
5401
5425
  this.trace("deploy:bootstrap-config-history-scan-error", {
5402
5426
  itemHash: args.itemHash,
5403
5427
  currentAggregateItemHash: args.currentAggregateItemHash,
5404
- error: error instanceof Error ? error.message : String(error)
5428
+ error: describeThrown(error)
5405
5429
  });
5406
5430
  return [];
5407
5431
  });
@@ -5444,7 +5468,7 @@ var SponsorRelayController = class {
5444
5468
  itemHash: args.itemHash,
5445
5469
  currentAggregateItemHash: args.currentAggregateItemHash,
5446
5470
  staleHashes,
5447
- error: error instanceof Error ? error.message : String(error)
5471
+ error: describeThrown(error)
5448
5472
  });
5449
5473
  });
5450
5474
  }
@@ -5765,7 +5789,7 @@ var SponsorRelayController = class {
5765
5789
  } catch (error) {
5766
5790
  this.patch({
5767
5791
  busy: { connectingWallet: false },
5768
- errorText: error instanceof Error ? error.message : String(error),
5792
+ errorText: describeThrown(error),
5769
5793
  statusText: "Wallet connection failed"
5770
5794
  });
5771
5795
  }
@@ -5981,7 +6005,7 @@ var SponsorRelayController = class {
5981
6005
  this.trace("refresh:error", error);
5982
6006
  this.patch({
5983
6007
  busy: { refreshing: false },
5984
- errorText: error instanceof Error ? error.message : String(error),
6008
+ errorText: describeThrown(error),
5985
6009
  statusText: "Refresh failed"
5986
6010
  });
5987
6011
  }
@@ -6316,7 +6340,7 @@ var SponsorRelayController = class {
6316
6340
  });
6317
6341
  return;
6318
6342
  } catch (error) {
6319
- lastError = error instanceof Error ? error : new Error(String(error));
6343
+ lastError = error instanceof Error ? error : new Error(describeThrown(error), { cause: error });
6320
6344
  const attemptLabel = crnDisplayName;
6321
6345
  const failedDueToInvalidIpv6 = /not globally routable|public guest IPv6/i.test(lastError.message);
6322
6346
  attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
@@ -6745,7 +6769,7 @@ var SponsorRelayController = class {
6745
6769
  this.trace("delete:error", error);
6746
6770
  this.patch({
6747
6771
  busy: { deletingInstanceHash: null },
6748
- errorText: error instanceof Error ? error.message : String(error),
6772
+ errorText: describeThrown(error),
6749
6773
  statusText: "Delete failed"
6750
6774
  });
6751
6775
  }
@@ -6811,7 +6835,7 @@ var SponsorRelayController = class {
6811
6835
  this.trace("registration-delete:error", error);
6812
6836
  this.patch({
6813
6837
  busy: { deletingRegistrationHash: null },
6814
- errorText: error instanceof Error ? error.message : String(error),
6838
+ errorText: describeThrown(error),
6815
6839
  statusText: "Registration cleanup failed"
6816
6840
  });
6817
6841
  }
@@ -6853,6 +6877,7 @@ export {
6853
6877
  createSponsorRelayController,
6854
6878
  deploymentFailureMessage,
6855
6879
  describeRequest,
6880
+ describeThrown,
6856
6881
  dragExceedsThreshold,
6857
6882
  formatDateTime,
6858
6883
  formatNumber,