@le-space/ui 0.9.2 → 0.9.5
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 +3 -3
- package/react/index.d.ts +1 -0
- package/react/index.js +104 -37
- package/shared/index.d.ts +45 -2
- package/shared/index.js +107 -37
- package/svelte/SponsorRelayFab.svelte +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/ui",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
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.
|
|
48
|
-
"@le-space/core": "0.9.
|
|
47
|
+
"@le-space/browser": "0.9.5",
|
|
48
|
+
"@le-space/core": "0.9.5",
|
|
49
49
|
"viem": "^2.53.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
package/react/index.d.ts
CHANGED
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.
|
|
99
|
+
var UI_PACKAGE_VERSION = "0.9.5";
|
|
100
100
|
|
|
101
101
|
// src/shared/format.ts
|
|
102
102
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -2830,7 +2830,8 @@ function browserMultiaddrTransport(addr) {
|
|
|
2830
2830
|
}
|
|
2831
2831
|
function selectCompactRelayBootstrapMultiaddrs(addrs, limit = DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT) {
|
|
2832
2832
|
const entries = filterPublicMultiaddrs(addrs, {
|
|
2833
|
-
browserDialableOnly: true
|
|
2833
|
+
browserDialableOnly: true,
|
|
2834
|
+
allowInsecureWebSockets: true
|
|
2834
2835
|
}).map((addr, index) => ({
|
|
2835
2836
|
addr,
|
|
2836
2837
|
index,
|
|
@@ -2900,12 +2901,23 @@ function isLocalHostname(host) {
|
|
|
2900
2901
|
function hasPeerId(addr) {
|
|
2901
2902
|
return splitMultiaddr(addr).includes("p2p");
|
|
2902
2903
|
}
|
|
2903
|
-
function
|
|
2904
|
+
function isSecureWebSocketMultiaddr(addr) {
|
|
2905
|
+
const parts = splitMultiaddr(addr.toLowerCase());
|
|
2906
|
+
if (parts.includes("wss")) return true;
|
|
2907
|
+
const wsIndex = parts.indexOf("ws");
|
|
2908
|
+
return wsIndex > 0 && parts.lastIndexOf("tls", wsIndex) >= 0;
|
|
2909
|
+
}
|
|
2910
|
+
function isWebSocketMultiaddr(addr) {
|
|
2911
|
+
const parts = splitMultiaddr(addr.toLowerCase());
|
|
2912
|
+
return parts.includes("ws") || parts.includes("wss");
|
|
2913
|
+
}
|
|
2914
|
+
function isBrowserDialableMultiaddr(addr, options = {}) {
|
|
2904
2915
|
const normalized = addr.toLowerCase();
|
|
2905
2916
|
if (normalized.includes("/webtransport") || normalized.includes("/webrtc-direct")) {
|
|
2906
2917
|
return normalized.includes("/certhash/");
|
|
2907
2918
|
}
|
|
2908
|
-
|
|
2919
|
+
if (!isWebSocketMultiaddr(addr)) return false;
|
|
2920
|
+
return (options.allowInsecureWebSockets ?? false) || isSecureWebSocketMultiaddr(addr);
|
|
2909
2921
|
}
|
|
2910
2922
|
function dedupeMultiaddrs(addrs) {
|
|
2911
2923
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2939,7 +2951,9 @@ function filterPublicMultiaddrs(addrs, options = {}) {
|
|
|
2939
2951
|
return dedupeMultiaddrs(addrs).filter((addr) => {
|
|
2940
2952
|
if (!isPublicMultiaddr(addr)) return false;
|
|
2941
2953
|
if (options.requirePeerId !== false && !hasPeerId(addr)) return false;
|
|
2942
|
-
if (options.browserDialableOnly && !isBrowserDialableMultiaddr(addr
|
|
2954
|
+
if (options.browserDialableOnly && !isBrowserDialableMultiaddr(addr, {
|
|
2955
|
+
allowInsecureWebSockets: options.allowInsecureWebSockets
|
|
2956
|
+
})) {
|
|
2943
2957
|
return false;
|
|
2944
2958
|
}
|
|
2945
2959
|
return true;
|
|
@@ -3054,7 +3068,8 @@ function buildRelayBootstrapPostContent(args) {
|
|
|
3054
3068
|
const updatedAt = args.now ?? Date.now();
|
|
3055
3069
|
const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
|
|
3056
3070
|
const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
|
|
3057
|
-
browserDialableOnly: true
|
|
3071
|
+
browserDialableOnly: true,
|
|
3072
|
+
allowInsecureWebSockets: true
|
|
3058
3073
|
}) : void 0;
|
|
3059
3074
|
const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
|
|
3060
3075
|
fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
|
|
@@ -3136,7 +3151,8 @@ async function signRelayBootstrapAuthorization(args) {
|
|
|
3136
3151
|
async function signRelayBootstrapProof(args) {
|
|
3137
3152
|
const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
|
|
3138
3153
|
const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
|
|
3139
|
-
browserDialableOnly: true
|
|
3154
|
+
browserDialableOnly: true,
|
|
3155
|
+
allowInsecureWebSockets: true
|
|
3140
3156
|
}) : void 0;
|
|
3141
3157
|
const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
|
|
3142
3158
|
fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
|
|
@@ -3191,20 +3207,24 @@ async function fetchAlephBootstrapPosts(options = {}) {
|
|
|
3191
3207
|
}
|
|
3192
3208
|
const payload = await response.json();
|
|
3193
3209
|
const posts = (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
|
|
3194
|
-
posts.
|
|
3195
|
-
return posts;
|
|
3210
|
+
return usableRelayBootstrapPosts(posts, options.onUnsupportedPost);
|
|
3196
3211
|
}
|
|
3197
|
-
function
|
|
3212
|
+
function unsupportedRelayBootstrapReason(post) {
|
|
3198
3213
|
if (post.type === "relay-bootstrap") {
|
|
3199
|
-
|
|
3200
|
-
"Legacy relay-bootstrap record encountered. Only relay-bootstrap-v2 is supported."
|
|
3201
|
-
);
|
|
3214
|
+
return "legacy relay-bootstrap record; only relay-bootstrap-v2 is supported";
|
|
3202
3215
|
}
|
|
3203
3216
|
if (post.type && post.type !== DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE) {
|
|
3204
|
-
|
|
3205
|
-
`Unsupported relay bootstrap post type: ${post.type}. Expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}.`
|
|
3206
|
-
);
|
|
3217
|
+
return `unsupported post type ${post.type}; expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}`;
|
|
3207
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
|
+
});
|
|
3208
3228
|
}
|
|
3209
3229
|
function compareRelayBootstrapPostRecency(left, right) {
|
|
3210
3230
|
const leftUpdatedAt = left.content?.updatedAt ?? 0;
|
|
@@ -3225,8 +3245,7 @@ function selectCurrentRelayBootstrapPosts(posts, options = {}) {
|
|
|
3225
3245
|
const maxAgeMs = options.maxAgeMs ?? DEFAULT_BOOTSTRAP_MAX_AGE_MS;
|
|
3226
3246
|
const now = options.now ?? Date.now();
|
|
3227
3247
|
const selected = /* @__PURE__ */ new Map();
|
|
3228
|
-
for (const post of posts) {
|
|
3229
|
-
assertSupportedRelayBootstrapPost(post);
|
|
3248
|
+
for (const post of usableRelayBootstrapPosts(posts, options.onUnsupportedPost)) {
|
|
3230
3249
|
const content = post.content;
|
|
3231
3250
|
if (!content) continue;
|
|
3232
3251
|
if (now - content.updatedAt > maxAgeMs) continue;
|
|
@@ -3938,12 +3957,51 @@ function validateUcanStoreBootstrapPackage(value) {
|
|
|
3938
3957
|
function deploymentNoun(profile) {
|
|
3939
3958
|
return profile === "ucan-store" ? "service" : "relay";
|
|
3940
3959
|
}
|
|
3960
|
+
function describeRequest(input, init) {
|
|
3961
|
+
const method = String(init?.method ?? "GET").toUpperCase();
|
|
3962
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : typeof input?.url === "string" ? input.url : "<unknown url>";
|
|
3963
|
+
return `${method} ${url}`;
|
|
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
|
+
}
|
|
3986
|
+
function deploymentFailureMessage(error, stage) {
|
|
3987
|
+
const message = describeThrown(error);
|
|
3988
|
+
const step = String(stage ?? "").trim();
|
|
3989
|
+
return step ? `${message} (while: ${step})` : message;
|
|
3990
|
+
}
|
|
3941
3991
|
function asJsonFetch(input, init) {
|
|
3942
|
-
return fetch(input, init).then(
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3992
|
+
return fetch(input, init).then(
|
|
3993
|
+
async (response) => ({
|
|
3994
|
+
ok: response.ok,
|
|
3995
|
+
status: response.status,
|
|
3996
|
+
json: async () => await response.json()
|
|
3997
|
+
}),
|
|
3998
|
+
(error) => {
|
|
3999
|
+
const reason = describeThrown(error);
|
|
4000
|
+
throw new Error(`${reason} (${describeRequest(input, init)})`, {
|
|
4001
|
+
cause: error
|
|
4002
|
+
});
|
|
4003
|
+
}
|
|
4004
|
+
);
|
|
3947
4005
|
}
|
|
3948
4006
|
function isConfirmedGuestSetupResult(value) {
|
|
3949
4007
|
return Boolean(
|
|
@@ -4126,7 +4184,7 @@ function defaultState(props = {}) {
|
|
|
4126
4184
|
};
|
|
4127
4185
|
}
|
|
4128
4186
|
function manifestLoadErrorState(error) {
|
|
4129
|
-
const message =
|
|
4187
|
+
const message = describeThrown(error);
|
|
4130
4188
|
return {
|
|
4131
4189
|
manifest: null,
|
|
4132
4190
|
valid: false,
|
|
@@ -4171,7 +4229,7 @@ function hostnameOfUrl(value) {
|
|
|
4171
4229
|
}
|
|
4172
4230
|
var INSTANCE_ADDRESS_TIMEOUT_MS = 1e4;
|
|
4173
4231
|
function describeInstanceAddressFailure(error, sourceUrl) {
|
|
4174
|
-
const raw =
|
|
4232
|
+
const raw = describeThrown(error);
|
|
4175
4233
|
if (error instanceof Error && error.name === "AbortError") {
|
|
4176
4234
|
return `${sourceUrl} did not answer within ${INSTANCE_ADDRESS_TIMEOUT_MS / 1e3}s. Press Refresh to try again.`;
|
|
4177
4235
|
}
|
|
@@ -4470,7 +4528,7 @@ async function inspectInstanceRuntime(args) {
|
|
|
4470
4528
|
return {
|
|
4471
4529
|
details: {
|
|
4472
4530
|
...details,
|
|
4473
|
-
error:
|
|
4531
|
+
error: describeThrown(error)
|
|
4474
4532
|
},
|
|
4475
4533
|
lookup
|
|
4476
4534
|
};
|
|
@@ -4586,11 +4644,15 @@ var SponsorRelayController = class {
|
|
|
4586
4644
|
};
|
|
4587
4645
|
this.emit();
|
|
4588
4646
|
}
|
|
4647
|
+
lastProgressLabel = null;
|
|
4589
4648
|
emitProgress(event) {
|
|
4590
4649
|
const nextEvent = {
|
|
4591
4650
|
...event,
|
|
4592
4651
|
timestamp: Date.now()
|
|
4593
4652
|
};
|
|
4653
|
+
if (nextEvent.stage !== "error") {
|
|
4654
|
+
this.lastProgressLabel = nextEvent.label;
|
|
4655
|
+
}
|
|
4594
4656
|
this.trace(`progress:${nextEvent.stage}`, {
|
|
4595
4657
|
label: nextEvent.label,
|
|
4596
4658
|
progress: nextEvent.progress,
|
|
@@ -5295,7 +5357,7 @@ var SponsorRelayController = class {
|
|
|
5295
5357
|
this.trace("deploy:bootstrap-config-cleanup-error", {
|
|
5296
5358
|
itemHash: args.itemHash,
|
|
5297
5359
|
deploymentToken: args.deploymentToken,
|
|
5298
|
-
error:
|
|
5360
|
+
error: describeThrown(error)
|
|
5299
5361
|
});
|
|
5300
5362
|
});
|
|
5301
5363
|
await this.cleanupStaleVmBootstrapConfigHistory({
|
|
@@ -5363,7 +5425,7 @@ var SponsorRelayController = class {
|
|
|
5363
5425
|
this.trace("deploy:bootstrap-config-history-scan-error", {
|
|
5364
5426
|
itemHash: args.itemHash,
|
|
5365
5427
|
currentAggregateItemHash: args.currentAggregateItemHash,
|
|
5366
|
-
error:
|
|
5428
|
+
error: describeThrown(error)
|
|
5367
5429
|
});
|
|
5368
5430
|
return [];
|
|
5369
5431
|
});
|
|
@@ -5406,7 +5468,7 @@ var SponsorRelayController = class {
|
|
|
5406
5468
|
itemHash: args.itemHash,
|
|
5407
5469
|
currentAggregateItemHash: args.currentAggregateItemHash,
|
|
5408
5470
|
staleHashes,
|
|
5409
|
-
error:
|
|
5471
|
+
error: describeThrown(error)
|
|
5410
5472
|
});
|
|
5411
5473
|
});
|
|
5412
5474
|
}
|
|
@@ -5727,7 +5789,7 @@ var SponsorRelayController = class {
|
|
|
5727
5789
|
} catch (error) {
|
|
5728
5790
|
this.patch({
|
|
5729
5791
|
busy: { connectingWallet: false },
|
|
5730
|
-
errorText:
|
|
5792
|
+
errorText: describeThrown(error),
|
|
5731
5793
|
statusText: "Wallet connection failed"
|
|
5732
5794
|
});
|
|
5733
5795
|
}
|
|
@@ -5943,7 +6005,7 @@ var SponsorRelayController = class {
|
|
|
5943
6005
|
this.trace("refresh:error", error);
|
|
5944
6006
|
this.patch({
|
|
5945
6007
|
busy: { refreshing: false },
|
|
5946
|
-
errorText:
|
|
6008
|
+
errorText: describeThrown(error),
|
|
5947
6009
|
statusText: "Refresh failed"
|
|
5948
6010
|
});
|
|
5949
6011
|
}
|
|
@@ -6278,7 +6340,7 @@ var SponsorRelayController = class {
|
|
|
6278
6340
|
});
|
|
6279
6341
|
return;
|
|
6280
6342
|
} catch (error) {
|
|
6281
|
-
lastError = error instanceof Error ? error : new Error(
|
|
6343
|
+
lastError = error instanceof Error ? error : new Error(describeThrown(error), { cause: error });
|
|
6282
6344
|
const attemptLabel = crnDisplayName;
|
|
6283
6345
|
const failedDueToInvalidIpv6 = /not globally routable|public guest IPv6/i.test(lastError.message);
|
|
6284
6346
|
attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
|
|
@@ -6507,10 +6569,15 @@ var SponsorRelayController = class {
|
|
|
6507
6569
|
attemptErrors.length > 0 ? `All compatible CRNs failed. ${attemptErrors.join(" | ")}` : lastError?.message ?? "All compatible CRNs failed."
|
|
6508
6570
|
);
|
|
6509
6571
|
} catch (error) {
|
|
6510
|
-
|
|
6572
|
+
const message = deploymentFailureMessage(error, this.lastProgressLabel);
|
|
6573
|
+
this.trace("deploy:error", {
|
|
6574
|
+
message,
|
|
6575
|
+
stage: this.lastProgressLabel,
|
|
6576
|
+
error
|
|
6577
|
+
});
|
|
6511
6578
|
this.patch({
|
|
6512
6579
|
busy: { deploying: false },
|
|
6513
|
-
errorText:
|
|
6580
|
+
errorText: message,
|
|
6514
6581
|
statusText: "Deployment failed"
|
|
6515
6582
|
});
|
|
6516
6583
|
this.emitProgress({
|
|
@@ -6518,7 +6585,7 @@ var SponsorRelayController = class {
|
|
|
6518
6585
|
label: "Deployment failed",
|
|
6519
6586
|
progress: 100,
|
|
6520
6587
|
status: "error",
|
|
6521
|
-
error:
|
|
6588
|
+
error: message
|
|
6522
6589
|
});
|
|
6523
6590
|
}
|
|
6524
6591
|
}
|
|
@@ -6702,7 +6769,7 @@ var SponsorRelayController = class {
|
|
|
6702
6769
|
this.trace("delete:error", error);
|
|
6703
6770
|
this.patch({
|
|
6704
6771
|
busy: { deletingInstanceHash: null },
|
|
6705
|
-
errorText:
|
|
6772
|
+
errorText: describeThrown(error),
|
|
6706
6773
|
statusText: "Delete failed"
|
|
6707
6774
|
});
|
|
6708
6775
|
}
|
|
@@ -6768,7 +6835,7 @@ var SponsorRelayController = class {
|
|
|
6768
6835
|
this.trace("registration-delete:error", error);
|
|
6769
6836
|
this.patch({
|
|
6770
6837
|
busy: { deletingRegistrationHash: null },
|
|
6771
|
-
errorText:
|
|
6838
|
+
errorText: describeThrown(error),
|
|
6772
6839
|
statusText: "Registration cleanup failed"
|
|
6773
6840
|
});
|
|
6774
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.
|
|
483
|
+
declare const UI_PACKAGE_VERSION = "0.9.5";
|
|
484
484
|
|
|
485
485
|
interface RootfsRequiredPortForward {
|
|
486
486
|
port: number;
|
|
@@ -516,6 +516,48 @@ declare function connectWallet(provider?: EthereumProviderLike | null): Promise<
|
|
|
516
516
|
declare function personalSign(address: string, message: string, provider?: EthereumProviderLike | null): Promise<string>;
|
|
517
517
|
declare function watchWallet(onChange: () => void, provider?: EthereumProviderLike | null): () => void;
|
|
518
518
|
|
|
519
|
+
/**
|
|
520
|
+
* What a request was, for an error that otherwise says nothing.
|
|
521
|
+
*
|
|
522
|
+
* `fetch` rejects with a bare `TypeError: Failed to fetch` for every network
|
|
523
|
+
* failure — no URL, no method, no cause. Carried up through the deploy path
|
|
524
|
+
* that becomes "Relay Button deployment failed: Failed to fetch", which is
|
|
525
|
+
* where a run of this project ended with no way to tell which of a dozen calls
|
|
526
|
+
* had failed.
|
|
527
|
+
*
|
|
528
|
+
* Exported for its test; `init.method` is read defensively because a call site
|
|
529
|
+
* may pass none.
|
|
530
|
+
*/
|
|
531
|
+
declare function describeRequest(input: RequestInfo | URL, init?: RequestInit): string;
|
|
532
|
+
/**
|
|
533
|
+
* The failure message, with the step it happened in.
|
|
534
|
+
*
|
|
535
|
+
* A deployment walks two dozen calls across Aleph, a CRN and a guest VM. When
|
|
536
|
+
* one of them fails with a message that names none of them — `Failed to fetch`
|
|
537
|
+
* is the standard case — the step is the only thing that narrows it down, and
|
|
538
|
+
* it is already known.
|
|
539
|
+
*
|
|
540
|
+
* Exported for its test.
|
|
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;
|
|
560
|
+
declare function deploymentFailureMessage(error: unknown, stage: string | null | undefined): string;
|
|
519
561
|
/**
|
|
520
562
|
* Normalise the two address documents in the fleet into one list of groups.
|
|
521
563
|
*
|
|
@@ -550,6 +592,7 @@ declare class SponsorRelayController {
|
|
|
550
592
|
private emit;
|
|
551
593
|
private trace;
|
|
552
594
|
private patch;
|
|
595
|
+
private lastProgressLabel;
|
|
553
596
|
private emitProgress;
|
|
554
597
|
private configureRelayBootstrapRegistration;
|
|
555
598
|
private probeRelayReachableOverProxy;
|
|
@@ -603,4 +646,4 @@ interface DeploymentProgressEmitter {
|
|
|
603
646
|
}
|
|
604
647
|
declare function createDeploymentProgressEmitter(): DeploymentProgressEmitter;
|
|
605
648
|
|
|
606
|
-
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, 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.
|
|
99
|
+
var UI_PACKAGE_VERSION = "0.9.5";
|
|
100
100
|
|
|
101
101
|
// src/shared/format.ts
|
|
102
102
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -2830,7 +2830,8 @@ function browserMultiaddrTransport(addr) {
|
|
|
2830
2830
|
}
|
|
2831
2831
|
function selectCompactRelayBootstrapMultiaddrs(addrs, limit = DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT) {
|
|
2832
2832
|
const entries = filterPublicMultiaddrs(addrs, {
|
|
2833
|
-
browserDialableOnly: true
|
|
2833
|
+
browserDialableOnly: true,
|
|
2834
|
+
allowInsecureWebSockets: true
|
|
2834
2835
|
}).map((addr, index) => ({
|
|
2835
2836
|
addr,
|
|
2836
2837
|
index,
|
|
@@ -2900,12 +2901,23 @@ function isLocalHostname(host) {
|
|
|
2900
2901
|
function hasPeerId(addr) {
|
|
2901
2902
|
return splitMultiaddr(addr).includes("p2p");
|
|
2902
2903
|
}
|
|
2903
|
-
function
|
|
2904
|
+
function isSecureWebSocketMultiaddr(addr) {
|
|
2905
|
+
const parts = splitMultiaddr(addr.toLowerCase());
|
|
2906
|
+
if (parts.includes("wss")) return true;
|
|
2907
|
+
const wsIndex = parts.indexOf("ws");
|
|
2908
|
+
return wsIndex > 0 && parts.lastIndexOf("tls", wsIndex) >= 0;
|
|
2909
|
+
}
|
|
2910
|
+
function isWebSocketMultiaddr(addr) {
|
|
2911
|
+
const parts = splitMultiaddr(addr.toLowerCase());
|
|
2912
|
+
return parts.includes("ws") || parts.includes("wss");
|
|
2913
|
+
}
|
|
2914
|
+
function isBrowserDialableMultiaddr(addr, options = {}) {
|
|
2904
2915
|
const normalized = addr.toLowerCase();
|
|
2905
2916
|
if (normalized.includes("/webtransport") || normalized.includes("/webrtc-direct")) {
|
|
2906
2917
|
return normalized.includes("/certhash/");
|
|
2907
2918
|
}
|
|
2908
|
-
|
|
2919
|
+
if (!isWebSocketMultiaddr(addr)) return false;
|
|
2920
|
+
return (options.allowInsecureWebSockets ?? false) || isSecureWebSocketMultiaddr(addr);
|
|
2909
2921
|
}
|
|
2910
2922
|
function dedupeMultiaddrs(addrs) {
|
|
2911
2923
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2939,7 +2951,9 @@ function filterPublicMultiaddrs(addrs, options = {}) {
|
|
|
2939
2951
|
return dedupeMultiaddrs(addrs).filter((addr) => {
|
|
2940
2952
|
if (!isPublicMultiaddr(addr)) return false;
|
|
2941
2953
|
if (options.requirePeerId !== false && !hasPeerId(addr)) return false;
|
|
2942
|
-
if (options.browserDialableOnly && !isBrowserDialableMultiaddr(addr
|
|
2954
|
+
if (options.browserDialableOnly && !isBrowserDialableMultiaddr(addr, {
|
|
2955
|
+
allowInsecureWebSockets: options.allowInsecureWebSockets
|
|
2956
|
+
})) {
|
|
2943
2957
|
return false;
|
|
2944
2958
|
}
|
|
2945
2959
|
return true;
|
|
@@ -3054,7 +3068,8 @@ function buildRelayBootstrapPostContent(args) {
|
|
|
3054
3068
|
const updatedAt = args.now ?? Date.now();
|
|
3055
3069
|
const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
|
|
3056
3070
|
const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
|
|
3057
|
-
browserDialableOnly: true
|
|
3071
|
+
browserDialableOnly: true,
|
|
3072
|
+
allowInsecureWebSockets: true
|
|
3058
3073
|
}) : void 0;
|
|
3059
3074
|
const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
|
|
3060
3075
|
fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
|
|
@@ -3136,7 +3151,8 @@ async function signRelayBootstrapAuthorization(args) {
|
|
|
3136
3151
|
async function signRelayBootstrapProof(args) {
|
|
3137
3152
|
const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
|
|
3138
3153
|
const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
|
|
3139
|
-
browserDialableOnly: true
|
|
3154
|
+
browserDialableOnly: true,
|
|
3155
|
+
allowInsecureWebSockets: true
|
|
3140
3156
|
}) : void 0;
|
|
3141
3157
|
const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
|
|
3142
3158
|
fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
|
|
@@ -3191,20 +3207,24 @@ async function fetchAlephBootstrapPosts(options = {}) {
|
|
|
3191
3207
|
}
|
|
3192
3208
|
const payload = await response.json();
|
|
3193
3209
|
const posts = (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
|
|
3194
|
-
posts.
|
|
3195
|
-
return posts;
|
|
3210
|
+
return usableRelayBootstrapPosts(posts, options.onUnsupportedPost);
|
|
3196
3211
|
}
|
|
3197
|
-
function
|
|
3212
|
+
function unsupportedRelayBootstrapReason(post) {
|
|
3198
3213
|
if (post.type === "relay-bootstrap") {
|
|
3199
|
-
|
|
3200
|
-
"Legacy relay-bootstrap record encountered. Only relay-bootstrap-v2 is supported."
|
|
3201
|
-
);
|
|
3214
|
+
return "legacy relay-bootstrap record; only relay-bootstrap-v2 is supported";
|
|
3202
3215
|
}
|
|
3203
3216
|
if (post.type && post.type !== DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE) {
|
|
3204
|
-
|
|
3205
|
-
`Unsupported relay bootstrap post type: ${post.type}. Expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}.`
|
|
3206
|
-
);
|
|
3217
|
+
return `unsupported post type ${post.type}; expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}`;
|
|
3207
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
|
+
});
|
|
3208
3228
|
}
|
|
3209
3229
|
function compareRelayBootstrapPostRecency(left, right) {
|
|
3210
3230
|
const leftUpdatedAt = left.content?.updatedAt ?? 0;
|
|
@@ -3225,8 +3245,7 @@ function selectCurrentRelayBootstrapPosts(posts, options = {}) {
|
|
|
3225
3245
|
const maxAgeMs = options.maxAgeMs ?? DEFAULT_BOOTSTRAP_MAX_AGE_MS;
|
|
3226
3246
|
const now = options.now ?? Date.now();
|
|
3227
3247
|
const selected = /* @__PURE__ */ new Map();
|
|
3228
|
-
for (const post of posts) {
|
|
3229
|
-
assertSupportedRelayBootstrapPost(post);
|
|
3248
|
+
for (const post of usableRelayBootstrapPosts(posts, options.onUnsupportedPost)) {
|
|
3230
3249
|
const content = post.content;
|
|
3231
3250
|
if (!content) continue;
|
|
3232
3251
|
if (now - content.updatedAt > maxAgeMs) continue;
|
|
@@ -3938,12 +3957,51 @@ function validateUcanStoreBootstrapPackage(value) {
|
|
|
3938
3957
|
function deploymentNoun(profile) {
|
|
3939
3958
|
return profile === "ucan-store" ? "service" : "relay";
|
|
3940
3959
|
}
|
|
3960
|
+
function describeRequest(input, init) {
|
|
3961
|
+
const method = String(init?.method ?? "GET").toUpperCase();
|
|
3962
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : typeof input?.url === "string" ? input.url : "<unknown url>";
|
|
3963
|
+
return `${method} ${url}`;
|
|
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
|
+
}
|
|
3986
|
+
function deploymentFailureMessage(error, stage) {
|
|
3987
|
+
const message = describeThrown(error);
|
|
3988
|
+
const step = String(stage ?? "").trim();
|
|
3989
|
+
return step ? `${message} (while: ${step})` : message;
|
|
3990
|
+
}
|
|
3941
3991
|
function asJsonFetch(input, init) {
|
|
3942
|
-
return fetch(input, init).then(
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3992
|
+
return fetch(input, init).then(
|
|
3993
|
+
async (response) => ({
|
|
3994
|
+
ok: response.ok,
|
|
3995
|
+
status: response.status,
|
|
3996
|
+
json: async () => await response.json()
|
|
3997
|
+
}),
|
|
3998
|
+
(error) => {
|
|
3999
|
+
const reason = describeThrown(error);
|
|
4000
|
+
throw new Error(`${reason} (${describeRequest(input, init)})`, {
|
|
4001
|
+
cause: error
|
|
4002
|
+
});
|
|
4003
|
+
}
|
|
4004
|
+
);
|
|
3947
4005
|
}
|
|
3948
4006
|
function isConfirmedGuestSetupResult(value) {
|
|
3949
4007
|
return Boolean(
|
|
@@ -4126,7 +4184,7 @@ function defaultState(props = {}) {
|
|
|
4126
4184
|
};
|
|
4127
4185
|
}
|
|
4128
4186
|
function manifestLoadErrorState(error) {
|
|
4129
|
-
const message =
|
|
4187
|
+
const message = describeThrown(error);
|
|
4130
4188
|
return {
|
|
4131
4189
|
manifest: null,
|
|
4132
4190
|
valid: false,
|
|
@@ -4171,7 +4229,7 @@ function hostnameOfUrl(value) {
|
|
|
4171
4229
|
}
|
|
4172
4230
|
var INSTANCE_ADDRESS_TIMEOUT_MS = 1e4;
|
|
4173
4231
|
function describeInstanceAddressFailure(error, sourceUrl) {
|
|
4174
|
-
const raw =
|
|
4232
|
+
const raw = describeThrown(error);
|
|
4175
4233
|
if (error instanceof Error && error.name === "AbortError") {
|
|
4176
4234
|
return `${sourceUrl} did not answer within ${INSTANCE_ADDRESS_TIMEOUT_MS / 1e3}s. Press Refresh to try again.`;
|
|
4177
4235
|
}
|
|
@@ -4470,7 +4528,7 @@ async function inspectInstanceRuntime(args) {
|
|
|
4470
4528
|
return {
|
|
4471
4529
|
details: {
|
|
4472
4530
|
...details,
|
|
4473
|
-
error:
|
|
4531
|
+
error: describeThrown(error)
|
|
4474
4532
|
},
|
|
4475
4533
|
lookup
|
|
4476
4534
|
};
|
|
@@ -4586,11 +4644,15 @@ var SponsorRelayController = class {
|
|
|
4586
4644
|
};
|
|
4587
4645
|
this.emit();
|
|
4588
4646
|
}
|
|
4647
|
+
lastProgressLabel = null;
|
|
4589
4648
|
emitProgress(event) {
|
|
4590
4649
|
const nextEvent = {
|
|
4591
4650
|
...event,
|
|
4592
4651
|
timestamp: Date.now()
|
|
4593
4652
|
};
|
|
4653
|
+
if (nextEvent.stage !== "error") {
|
|
4654
|
+
this.lastProgressLabel = nextEvent.label;
|
|
4655
|
+
}
|
|
4594
4656
|
this.trace(`progress:${nextEvent.stage}`, {
|
|
4595
4657
|
label: nextEvent.label,
|
|
4596
4658
|
progress: nextEvent.progress,
|
|
@@ -5295,7 +5357,7 @@ var SponsorRelayController = class {
|
|
|
5295
5357
|
this.trace("deploy:bootstrap-config-cleanup-error", {
|
|
5296
5358
|
itemHash: args.itemHash,
|
|
5297
5359
|
deploymentToken: args.deploymentToken,
|
|
5298
|
-
error:
|
|
5360
|
+
error: describeThrown(error)
|
|
5299
5361
|
});
|
|
5300
5362
|
});
|
|
5301
5363
|
await this.cleanupStaleVmBootstrapConfigHistory({
|
|
@@ -5363,7 +5425,7 @@ var SponsorRelayController = class {
|
|
|
5363
5425
|
this.trace("deploy:bootstrap-config-history-scan-error", {
|
|
5364
5426
|
itemHash: args.itemHash,
|
|
5365
5427
|
currentAggregateItemHash: args.currentAggregateItemHash,
|
|
5366
|
-
error:
|
|
5428
|
+
error: describeThrown(error)
|
|
5367
5429
|
});
|
|
5368
5430
|
return [];
|
|
5369
5431
|
});
|
|
@@ -5406,7 +5468,7 @@ var SponsorRelayController = class {
|
|
|
5406
5468
|
itemHash: args.itemHash,
|
|
5407
5469
|
currentAggregateItemHash: args.currentAggregateItemHash,
|
|
5408
5470
|
staleHashes,
|
|
5409
|
-
error:
|
|
5471
|
+
error: describeThrown(error)
|
|
5410
5472
|
});
|
|
5411
5473
|
});
|
|
5412
5474
|
}
|
|
@@ -5727,7 +5789,7 @@ var SponsorRelayController = class {
|
|
|
5727
5789
|
} catch (error) {
|
|
5728
5790
|
this.patch({
|
|
5729
5791
|
busy: { connectingWallet: false },
|
|
5730
|
-
errorText:
|
|
5792
|
+
errorText: describeThrown(error),
|
|
5731
5793
|
statusText: "Wallet connection failed"
|
|
5732
5794
|
});
|
|
5733
5795
|
}
|
|
@@ -5943,7 +6005,7 @@ var SponsorRelayController = class {
|
|
|
5943
6005
|
this.trace("refresh:error", error);
|
|
5944
6006
|
this.patch({
|
|
5945
6007
|
busy: { refreshing: false },
|
|
5946
|
-
errorText:
|
|
6008
|
+
errorText: describeThrown(error),
|
|
5947
6009
|
statusText: "Refresh failed"
|
|
5948
6010
|
});
|
|
5949
6011
|
}
|
|
@@ -6278,7 +6340,7 @@ var SponsorRelayController = class {
|
|
|
6278
6340
|
});
|
|
6279
6341
|
return;
|
|
6280
6342
|
} catch (error) {
|
|
6281
|
-
lastError = error instanceof Error ? error : new Error(
|
|
6343
|
+
lastError = error instanceof Error ? error : new Error(describeThrown(error), { cause: error });
|
|
6282
6344
|
const attemptLabel = crnDisplayName;
|
|
6283
6345
|
const failedDueToInvalidIpv6 = /not globally routable|public guest IPv6/i.test(lastError.message);
|
|
6284
6346
|
attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
|
|
@@ -6507,10 +6569,15 @@ var SponsorRelayController = class {
|
|
|
6507
6569
|
attemptErrors.length > 0 ? `All compatible CRNs failed. ${attemptErrors.join(" | ")}` : lastError?.message ?? "All compatible CRNs failed."
|
|
6508
6570
|
);
|
|
6509
6571
|
} catch (error) {
|
|
6510
|
-
|
|
6572
|
+
const message = deploymentFailureMessage(error, this.lastProgressLabel);
|
|
6573
|
+
this.trace("deploy:error", {
|
|
6574
|
+
message,
|
|
6575
|
+
stage: this.lastProgressLabel,
|
|
6576
|
+
error
|
|
6577
|
+
});
|
|
6511
6578
|
this.patch({
|
|
6512
6579
|
busy: { deploying: false },
|
|
6513
|
-
errorText:
|
|
6580
|
+
errorText: message,
|
|
6514
6581
|
statusText: "Deployment failed"
|
|
6515
6582
|
});
|
|
6516
6583
|
this.emitProgress({
|
|
@@ -6518,7 +6585,7 @@ var SponsorRelayController = class {
|
|
|
6518
6585
|
label: "Deployment failed",
|
|
6519
6586
|
progress: 100,
|
|
6520
6587
|
status: "error",
|
|
6521
|
-
error:
|
|
6588
|
+
error: message
|
|
6522
6589
|
});
|
|
6523
6590
|
}
|
|
6524
6591
|
}
|
|
@@ -6702,7 +6769,7 @@ var SponsorRelayController = class {
|
|
|
6702
6769
|
this.trace("delete:error", error);
|
|
6703
6770
|
this.patch({
|
|
6704
6771
|
busy: { deletingInstanceHash: null },
|
|
6705
|
-
errorText:
|
|
6772
|
+
errorText: describeThrown(error),
|
|
6706
6773
|
statusText: "Delete failed"
|
|
6707
6774
|
});
|
|
6708
6775
|
}
|
|
@@ -6768,7 +6835,7 @@ var SponsorRelayController = class {
|
|
|
6768
6835
|
this.trace("registration-delete:error", error);
|
|
6769
6836
|
this.patch({
|
|
6770
6837
|
busy: { deletingRegistrationHash: null },
|
|
6771
|
-
errorText:
|
|
6838
|
+
errorText: describeThrown(error),
|
|
6772
6839
|
statusText: "Registration cleanup failed"
|
|
6773
6840
|
});
|
|
6774
6841
|
}
|
|
@@ -6808,6 +6875,9 @@ export {
|
|
|
6808
6875
|
connectWallet,
|
|
6809
6876
|
createDeploymentProgressEmitter,
|
|
6810
6877
|
createSponsorRelayController,
|
|
6878
|
+
deploymentFailureMessage,
|
|
6879
|
+
describeRequest,
|
|
6880
|
+
describeThrown,
|
|
6811
6881
|
dragExceedsThreshold,
|
|
6812
6882
|
formatDateTime,
|
|
6813
6883
|
formatNumber,
|
|
@@ -212,8 +212,19 @@ export let apiHosts = undefined
|
|
|
212
212
|
state = next
|
|
213
213
|
})
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
// Before `controller.init()`, not after it, and the order is the whole
|
|
216
|
+
// feature. Placement reads storage and the viewport — no network — while
|
|
217
|
+
// `init()` fetches the manifest and the account's instances. Behind that
|
|
218
|
+
// await, `placement` stays null for as long as Aleph takes to answer, and
|
|
219
|
+
// `handleDragStart` returns early on a null placement: the launcher simply
|
|
220
|
+
// cannot be dragged. Measured in simple-todo: still immovable 20 seconds
|
|
221
|
+
// after the app was ready, with pointerdown, eight pointermoves and
|
|
222
|
+
// pointerup all arriving at the button.
|
|
223
|
+
//
|
|
224
|
+
// The React build never had this — it resolves placement in its own effect.
|
|
216
225
|
initPlacement()
|
|
226
|
+
|
|
227
|
+
await controller.init()
|
|
217
228
|
window.addEventListener('resize', handleViewportChange)
|
|
218
229
|
window.addEventListener('orientationchange', handleViewportChange)
|
|
219
230
|
return () => {
|