@le-space/ui 0.3.32 → 0.4.1

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/react/index.js CHANGED
@@ -28,7 +28,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
28
28
  var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
29
29
 
30
30
  // src/shared/package-version.ts
31
- var UI_PACKAGE_VERSION = "0.3.32";
31
+ var UI_PACKAGE_VERSION = "0.4.1";
32
32
 
33
33
  // src/shared/format.ts
34
34
  function shortHash(value, head = 8, tail = 6) {
@@ -1237,6 +1237,16 @@ function normalizeBroadcastStatus(httpStatus, responseStatus) {
1237
1237
  if (httpStatus === 202) return "pending";
1238
1238
  return normalizeMessageStatus2(responseStatus);
1239
1239
  }
1240
+ function isInvalidMessageFormatResponse2(response, payload) {
1241
+ if (response.status !== 422) return false;
1242
+ const details = payload?.details;
1243
+ if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
1244
+ if (details && typeof details === "object") {
1245
+ const detailMessage = details.message;
1246
+ if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
1247
+ }
1248
+ return false;
1249
+ }
1240
1250
  function isRetryableBroadcastFailure(response, payload) {
1241
1251
  if (response.status >= 500) return true;
1242
1252
  const publicationStatus = payload?.publication_status?.status;
@@ -1258,22 +1268,25 @@ async function postBroadcastPayload2(body, options) {
1258
1268
  };
1259
1269
  }
1260
1270
  async function broadcastAlephMessage2(message, options) {
1261
- const requestBody = { sync: options.sync ?? false, message };
1262
- const maxAttempts = 3;
1263
- for (let index = 0; index < maxAttempts; index += 1) {
1264
- const result = await postBroadcastPayload2(requestBody, {
1271
+ const attempts = [
1272
+ { sync: options.sync ?? false, message },
1273
+ { ...message, sync: options.sync ?? false },
1274
+ { ...message }
1275
+ ];
1276
+ for (let index = 0; index < attempts.length; index += 1) {
1277
+ const result = await postBroadcastPayload2(attempts[index], {
1265
1278
  apiHost: options.apiHost,
1266
1279
  fetch: options.fetch
1267
1280
  });
1268
1281
  if (result.httpStatus === 202 || normalizeBroadcastStatus(result.httpStatus, result.response?.message_status) !== "unknown" || result.httpStatus >= 200 && result.httpStatus < 300) {
1269
1282
  return result;
1270
1283
  }
1271
- const canRetry = index < maxAttempts - 1 && isRetryableBroadcastFailure({ status: result.httpStatus }, result.response);
1284
+ const canRetry = index < attempts.length - 1 && (isInvalidMessageFormatResponse2({ status: result.httpStatus }, result.response) || isRetryableBroadcastFailure({ status: result.httpStatus }, result.response));
1272
1285
  if (!canRetry) {
1273
1286
  throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
1274
1287
  }
1275
1288
  }
1276
- throw new Error("Broadcast failed: retry budget exhausted");
1289
+ throw new Error("Broadcast failed: no compatible request format was accepted");
1277
1290
  }
1278
1291
 
1279
1292
  // ../core/src/forget.ts
@@ -1878,13 +1891,6 @@ async function waitForVmRuntime(args) {
1878
1891
  }
1879
1892
 
1880
1893
  // ../core/src/guest.ts
1881
- function proxyHostnameFromUrl(value) {
1882
- try {
1883
- return value ? new URL(value).hostname : null;
1884
- } catch {
1885
- return null;
1886
- }
1887
- }
1888
1894
  async function defaultHttpProbe(fetch2, url, timeoutMs = 1e4) {
1889
1895
  try {
1890
1896
  const controller = new AbortController();
@@ -2126,59 +2132,6 @@ async function fetchUcGoPeerMetadata(args) {
2126
2132
  `Relay metadata did not become ready after ${attempts} attempts: ${lastError ? lastError : typeof lastPayload === "string" ? lastPayload : JSON.stringify(lastPayload ?? {})}`
2127
2133
  );
2128
2134
  }
2129
- async function verifyUcGoPeerReachability(args) {
2130
- const checks = {};
2131
- const mappedPorts2 = args.mappedPorts ?? {};
2132
- const hostIpv4 = args.hostIpv4;
2133
- const skippedInternalPorts = new Set(
2134
- (args.skipInternalPorts ?? ["80"]).map((value) => String(value))
2135
- );
2136
- const httpProbe = args.httpProbe ?? ((url, timeoutMs) => defaultHttpProbe(args.fetch, url, timeoutMs));
2137
- if (hostIpv4) {
2138
- for (const [internalPort, mapping] of Object.entries(mappedPorts2)) {
2139
- if (skippedInternalPorts.has(String(internalPort))) continue;
2140
- if (mapping?.tcp === true && mapping?.host) {
2141
- checks[`tcp:${internalPort}`] = {
2142
- host: hostIpv4,
2143
- port: mapping.host,
2144
- ...await args.tcpProbe(
2145
- hostIpv4,
2146
- mapping.host,
2147
- Number(args.tcpTimeoutMs ?? 5e3)
2148
- )
2149
- };
2150
- } else if (mapping?.udp === true && mapping?.host) {
2151
- checks[`udp:${internalPort}`] = {
2152
- host: hostIpv4,
2153
- port: mapping.host,
2154
- ok: null,
2155
- note: "UDP mapping published; CI does not perform an application-level UDP handshake probe."
2156
- };
2157
- }
2158
- }
2159
- }
2160
- if (args.proxyUrl && args.verifyProxyHttp !== false) {
2161
- checks["https:proxy"] = await httpProbe(
2162
- args.proxyUrl,
2163
- Number(args.httpTimeoutMs ?? 1e4)
2164
- );
2165
- }
2166
- const proxyHostname = proxyHostnameFromUrl(args.proxyUrl);
2167
- if (proxyHostname) {
2168
- checks["tcp:proxy-443"] = await args.tcpProbe(
2169
- proxyHostname,
2170
- 443,
2171
- Number(args.tcpTimeoutMs ?? 5e3)
2172
- );
2173
- }
2174
- const failedChecks = Object.entries(checks).filter(
2175
- ([, value]) => value?.ok === false
2176
- );
2177
- return {
2178
- ok: failedChecks.length === 0,
2179
- checks
2180
- };
2181
- }
2182
2135
 
2183
2136
  // ../aleph-bootstrap/src/index.ts
2184
2137
  import { recoverMessageAddress } from "viem";
@@ -2598,7 +2551,7 @@ async function waitForRelayBootstrapRegistration(args) {
2598
2551
  return false;
2599
2552
  });
2600
2553
  if (match?.content) {
2601
- return {
2554
+ const visibleRegistration = {
2602
2555
  itemHash: match.itemHash ?? null,
2603
2556
  hash: match.hash ?? null,
2604
2557
  sender: typeof match.address === "string" ? match.address : null,
@@ -2607,7 +2560,10 @@ async function waitForRelayBootstrapRegistration(args) {
2607
2560
  multiaddrs: Array.isArray(match.content.multiaddrs) ? match.content.multiaddrs : [],
2608
2561
  browserMultiaddrs: Array.isArray(match.content.browserMultiaddrs) ? match.content.browserMultiaddrs : []
2609
2562
  };
2563
+ args.onAttempt?.(visibleRegistration, attempt + 1, attempts);
2564
+ return visibleRegistration;
2610
2565
  }
2566
+ args.onAttempt?.(null, attempt + 1, attempts);
2611
2567
  if (attempt < attempts - 1) {
2612
2568
  await sleep(delayMs);
2613
2569
  }
@@ -2720,16 +2676,6 @@ async function publishRelayBootstrapRegistration(args) {
2720
2676
 
2721
2677
  // ../core/src/bootstrap-config.ts
2722
2678
  var VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY = "vm-bootstrap-config";
2723
- var VM_BOOTSTRAP_CONFIG_SIGNAL_REF = "vm-bootstrap-config";
2724
- var VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE = "vm-bootstrap-config-status";
2725
- function parseJsonObject(value) {
2726
- try {
2727
- const parsed = JSON.parse(value);
2728
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
2729
- } catch {
2730
- return null;
2731
- }
2732
- }
2733
2679
  async function fetchVmBootstrapConfigAggregate(address, options) {
2734
2680
  const requestUrl = new URL(`/api/v0/aggregates/${address}.json`, options.apiHost ?? DEFAULT_ALEPH_API_HOST2);
2735
2681
  requestUrl.searchParams.set("keys", VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY);
@@ -2849,89 +2795,6 @@ async function deleteVmBootstrapConfig(args) {
2849
2795
  aggregate: content
2850
2796
  };
2851
2797
  }
2852
- function normalizeVmBootstrapConfigSignalRecord(value) {
2853
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2854
- const candidate = value;
2855
- const deploymentToken = typeof candidate.deploymentToken === "string" ? candidate.deploymentToken.trim() : "";
2856
- const status = typeof candidate.status === "string" ? candidate.status.trim() : "";
2857
- const profile = typeof candidate.profile === "string" ? candidate.profile.trim() : "";
2858
- const ownerAddress = typeof candidate.ownerAddress === "string" ? candidate.ownerAddress.trim() : "";
2859
- const instanceItemHash = typeof candidate.instanceItemHash === "string" ? candidate.instanceItemHash.trim() : "";
2860
- const updatedAt = typeof candidate.updatedAt === "string" ? candidate.updatedAt.trim() : "";
2861
- if (!deploymentToken || !status || !profile || !ownerAddress || !instanceItemHash || !updatedAt) {
2862
- return null;
2863
- }
2864
- return {
2865
- deploymentToken,
2866
- status,
2867
- profile,
2868
- ownerAddress,
2869
- instanceItemHash,
2870
- updatedAt,
2871
- publisherAddress: typeof candidate.publisherAddress === "string" && candidate.publisherAddress.trim() ? candidate.publisherAddress.trim() : null,
2872
- authorization: candidate.authorization,
2873
- peerId: typeof candidate.peerId === "string" && candidate.peerId.trim() ? candidate.peerId.trim() : null,
2874
- probeMultiaddrs: Array.isArray(candidate.probeMultiaddrs) ? candidate.probeMultiaddrs.filter(
2875
- (entry) => typeof entry === "string" && entry.trim().length > 0
2876
- ) : [],
2877
- browserBootstrapMultiaddrs: Array.isArray(candidate.browserBootstrapMultiaddrs) ? candidate.browserBootstrapMultiaddrs.filter(
2878
- (entry) => typeof entry === "string" && entry.trim().length > 0
2879
- ) : []
2880
- };
2881
- }
2882
- async function fetchVmBootstrapConfigSignals(args) {
2883
- const requestUrl = new URL("/api/v0/posts.json", args.apiHost ?? DEFAULT_ALEPH_API_HOST2);
2884
- requestUrl.searchParams.set("refs", args.ref ?? VM_BOOTSTRAP_CONFIG_SIGNAL_REF);
2885
- requestUrl.searchParams.set("types", args.postType ?? VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE);
2886
- requestUrl.searchParams.set("pagination", String(Math.max(1, Number(args.pagination ?? 100))));
2887
- requestUrl.searchParams.set("page", String(Math.max(1, Number(args.page ?? 1))));
2888
- requestUrl.searchParams.set("sortOrder", "-1");
2889
- const response = await args.fetch(requestUrl.toString(), { cache: "no-cache" });
2890
- if (!response.ok) {
2891
- throw new Error(`VM bootstrap config signal request failed: ${response.status}`);
2892
- }
2893
- const payload = await response.json();
2894
- const expectedDeploymentToken = args.deploymentToken?.trim() ?? "";
2895
- const expectedOwnerAddress = args.ownerAddress?.trim().toLowerCase() ?? "";
2896
- const expectedInstanceItemHash = args.instanceItemHash?.trim() ?? "";
2897
- return (payload.posts ?? []).map((entry) => {
2898
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
2899
- const parsed = "content" in entry && entry.content && typeof entry.content === "object" && !Array.isArray(entry.content) ? entry.content : "item_content" in entry && typeof entry.item_content === "string" ? parseJsonObject(entry.item_content) : null;
2900
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2901
- const content = "deploymentToken" in parsed ? parsed : "content" in parsed && parsed.content && typeof parsed.content === "object" && !Array.isArray(parsed.content) ? parsed.content : null;
2902
- if (!content || typeof content !== "object" || Array.isArray(content)) return null;
2903
- return normalizeVmBootstrapConfigSignalRecord(content);
2904
- }).filter((entry) => {
2905
- if (!entry) return false;
2906
- if (expectedDeploymentToken && entry.deploymentToken !== expectedDeploymentToken) return false;
2907
- if (expectedOwnerAddress && entry.ownerAddress.toLowerCase() !== expectedOwnerAddress) return false;
2908
- if (expectedInstanceItemHash && entry.instanceItemHash !== expectedInstanceItemHash) return false;
2909
- return true;
2910
- });
2911
- }
2912
- async function waitForVmBootstrapConfigSignal(args) {
2913
- const attempts = Math.max(1, Number(args.attempts ?? 24));
2914
- const delayMs = Math.max(0, Number(args.delayMs ?? 2500));
2915
- const sleep2 = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
2916
- const expectedStatus = args.expectedStatus ?? "applied";
2917
- for (let attempt = 0; attempt < attempts; attempt += 1) {
2918
- const signals = await fetchVmBootstrapConfigSignals({
2919
- deploymentToken: args.deploymentToken,
2920
- ownerAddress: args.ownerAddress,
2921
- instanceItemHash: args.instanceItemHash,
2922
- fetch: args.fetch,
2923
- apiHost: args.apiHost,
2924
- ref: args.ref,
2925
- postType: args.postType
2926
- }).catch(() => []);
2927
- const match = signals.find((entry) => entry.status === expectedStatus) ?? null;
2928
- if (match) return match;
2929
- if (attempt < attempts - 1) {
2930
- await sleep2(delayMs);
2931
- }
2932
- }
2933
- return null;
2934
- }
2935
2798
 
2936
2799
  // ../core/src/bootstrap-reconcile.ts
2937
2800
  function createRelayBootstrapRegistrationId(profile, instanceName, itemHash) {
@@ -3303,8 +3166,6 @@ var UI_DEPLOY_WAIT_ATTEMPTS = 60;
3303
3166
  var UI_DEPLOY_WAIT_DELAY_MS = 5e3;
3304
3167
  var UI_RUNTIME_WAIT_ATTEMPTS = 40;
3305
3168
  var UI_RUNTIME_WAIT_DELAY_MS = 5e3;
3306
- var UI_REACHABILITY_VERIFY_ATTEMPTS = 25;
3307
- var UI_REACHABILITY_VERIFY_DELAY_MS = 3e3;
3308
3169
  function hasUsableRuntime(details) {
3309
3170
  return Boolean(details.hostIpv4) || Boolean(details.vmIpv4) || details.mappedPorts.length > 0 || Boolean(details.webUrl) || Boolean(details.execution);
3310
3171
  }
@@ -3411,21 +3272,19 @@ var SponsorRelayController = class {
3411
3272
  this.progressEmitter.emit(nextEvent);
3412
3273
  }
3413
3274
  async configureRelayBootstrapRegistration(args) {
3414
- let runtime = args.runtime;
3415
3275
  const profile = relayProfileForManifest(this.state.manifest);
3416
3276
  if (!profile) return;
3417
3277
  if (!this.state.wallet.address) {
3418
3278
  throw new Error("A connected wallet is required to register relay bootstrap addresses.");
3419
3279
  }
3420
- if (!runtime.hostIpv4) {
3280
+ if (!args.runtime.hostIpv4) {
3421
3281
  throw new Error("Relay runtime did not expose a host IPv4 address.");
3422
3282
  }
3423
- let mappedPorts2 = runtime.mappedPorts ?? {};
3283
+ const mappedPorts2 = args.runtime.mappedPorts ?? {};
3424
3284
  const setupPort = mappedPorts2["80"]?.host ?? null;
3425
3285
  if (!setupPort) {
3426
3286
  throw new Error("Relay runtime did not expose the temporary setup endpoint.");
3427
3287
  }
3428
- let bootstrapSignalRelayMetadata = null;
3429
3288
  if (profile === "orbitdb-relay-pinner") {
3430
3289
  this.emitProgress({
3431
3290
  stage: "deployment-confirmed",
@@ -3436,12 +3295,22 @@ var SponsorRelayController = class {
3436
3295
  detail: "Waiting for the guest setup endpoint before collecting relay metadata."
3437
3296
  });
3438
3297
  const setupHealth = await waitForSetupEndpoint({
3439
- hostIpv4: runtime.hostIpv4,
3298
+ hostIpv4: args.runtime.hostIpv4,
3440
3299
  setupPort,
3441
3300
  fetch: (url, init) => fetch(url, init),
3442
3301
  attempts: 15,
3443
3302
  delayMs: 4e3,
3444
- httpTimeoutMs: 1e4
3303
+ httpTimeoutMs: 1e4,
3304
+ onAttempt: (result, attempt, attempts) => {
3305
+ this.emitProgress({
3306
+ stage: "deployment-confirmed",
3307
+ label: "Waiting for guest setup",
3308
+ progress: 93,
3309
+ status: result.ok ? "success" : "info",
3310
+ itemHash: args.itemHash,
3311
+ detail: result.ok ? `Setup endpoint ${attempt}/${attempts}: ready at http://${args.runtime.hostIpv4}:${setupPort}/health.` : `Setup endpoint ${attempt}/${attempts}: waiting for http://${args.runtime.hostIpv4}:${setupPort}/health.`
3312
+ });
3313
+ }
3445
3314
  });
3446
3315
  if (!setupHealth.ok) {
3447
3316
  throw new Error(
@@ -3462,12 +3331,12 @@ var SponsorRelayController = class {
3462
3331
  throw new Error("OrbitDB relay runtime is missing required mapped TCP/WS ports.");
3463
3332
  }
3464
3333
  const configureResult = await configureOrbitdbRelaySetup2({
3465
- hostIpv4: runtime.hostIpv4,
3466
- publicIpv6: runtime.ipv6,
3334
+ hostIpv4: args.runtime.hostIpv4,
3335
+ publicIpv6: args.runtime.ipv6,
3467
3336
  setupPort,
3468
3337
  tcpPort,
3469
3338
  wsPort,
3470
- proxyUrl: runtime.proxyUrl ?? void 0,
3339
+ proxyUrl: args.runtime.proxyUrl ?? void 0,
3471
3340
  metricsPort: mappedPorts2["9090"]?.host ?? null,
3472
3341
  metricsHttpsPort: mappedPorts2["443"]?.host ?? null,
3473
3342
  webrtcPort: mappedPorts2["9093"]?.host ?? null,
@@ -3484,54 +3353,6 @@ var SponsorRelayController = class {
3484
3353
  if (!args.deploymentToken) {
3485
3354
  throw new Error("Missing deployment token for Aleph bootstrap config handoff.");
3486
3355
  }
3487
- if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
3488
- this.emitProgress({
3489
- stage: "deployment-confirmed",
3490
- label: "Waiting for HTTPS route",
3491
- progress: 93,
3492
- status: "warning",
3493
- itemHash: args.itemHash,
3494
- detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
3495
- });
3496
- let latestRuntime = runtime;
3497
- for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
3498
- if (latestRuntime.webAccess?.active === true) {
3499
- break;
3500
- }
3501
- await new Promise(
3502
- (resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
3503
- );
3504
- latestRuntime = await fetchVmRuntime({
3505
- itemHash: args.itemHash,
3506
- fetch: (url, init) => fetch(url, init),
3507
- crnHash: runtime.selectedCrn?.hash ?? runtime.allocation?.crnHash ?? void 0,
3508
- crns: this.state.crns,
3509
- crnListUrl: this.props.crnListUrl
3510
- }).catch(() => latestRuntime);
3511
- this.emitProgress({
3512
- stage: "deployment-confirmed",
3513
- label: "Waiting for HTTPS route",
3514
- progress: 93,
3515
- status: latestRuntime.webAccess?.active === true ? "info" : "warning",
3516
- itemHash: args.itemHash,
3517
- detail: `2n6 activation ${attempt + 1}/${UI_RUNTIME_WAIT_ATTEMPTS}: ${latestRuntime.webAccess?.active === true ? "active" : "still pending"} for ${latestRuntime.proxyUrl ?? runtime.proxyUrl ?? "reserved proxy URL"}.`
3518
- });
3519
- this.trace("deploy:bootstrap-proxy-activation", {
3520
- itemHash: args.itemHash,
3521
- attempt: attempt + 1,
3522
- attempts: UI_RUNTIME_WAIT_ATTEMPTS,
3523
- active: latestRuntime.webAccess?.active === true,
3524
- proxyUrl: latestRuntime.proxyUrl ?? null
3525
- });
3526
- }
3527
- runtime = latestRuntime;
3528
- mappedPorts2 = runtime.mappedPorts ?? mappedPorts2;
3529
- }
3530
- if (!runtime.hostIpv4) {
3531
- throw new Error("Relay runtime lost its host IPv4 address while waiting for HTTPS activation.");
3532
- }
3533
- const runtimeHostIpv4 = runtime.hostIpv4;
3534
- const activeProxyUrl = runtime.webAccess?.active === true ? runtime.proxyUrl ?? null : null;
3535
3356
  this.emitProgress({
3536
3357
  stage: "deployment-confirmed",
3537
3358
  label: "Publishing relay config",
@@ -3549,9 +3370,9 @@ var SponsorRelayController = class {
3549
3370
  expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(),
3550
3371
  status: "pending",
3551
3372
  runtime: {
3552
- publicIpv4: runtimeHostIpv4,
3553
- publicIpv6: runtime.ipv6 ?? null,
3554
- proxyUrl: activeProxyUrl,
3373
+ publicIpv4: args.runtime.hostIpv4,
3374
+ publicIpv6: args.runtime.ipv6 ?? null,
3375
+ proxyUrl: args.runtime.proxyUrl ?? null,
3555
3376
  mappedPorts: mappedPorts2
3556
3377
  },
3557
3378
  bootstrap: {
@@ -3580,164 +3401,57 @@ var SponsorRelayController = class {
3580
3401
  aggregateItemHash: bootstrapConfigPublication.aggregateItemHash,
3581
3402
  aggregateStatus: bootstrapConfigPublication.aggregateStatus
3582
3403
  });
3583
- this.emitProgress({
3584
- stage: "deployment-confirmed",
3585
- label: "Waiting for guest config",
3586
- progress: 94,
3587
- status: "info",
3588
- itemHash: args.itemHash,
3589
- detail: "Waiting for the relay VM to acknowledge that it consumed the Aleph bootstrap config."
3590
- });
3591
- const bootstrapConfigSignal = await waitForVmBootstrapConfigSignal({
3592
- deploymentToken: args.deploymentToken,
3593
- ownerAddress: this.state.wallet.address,
3594
- instanceItemHash: args.itemHash,
3595
- fetch: asJsonFetch,
3596
- apiHost: this.client.apiHost,
3597
- attempts: 40,
3598
- delayMs: 3e3
3599
- });
3600
- this.trace("deploy:bootstrap-config-signal-visible", {
3601
- itemHash: args.itemHash,
3602
- deploymentToken: args.deploymentToken,
3603
- bootstrapConfigSignal
3604
- });
3605
- if (!bootstrapConfigSignal) {
3606
- throw new Error(
3607
- "The relay VM did not confirm that it applied the Aleph bootstrap config in time."
3608
- );
3609
- }
3610
- bootstrapSignalRelayMetadata = typeof bootstrapConfigSignal.peerId === "string" && bootstrapConfigSignal.peerId.length > 0 && Array.isArray(bootstrapConfigSignal.probeMultiaddrs) && bootstrapConfigSignal.probeMultiaddrs.length > 0 ? {
3611
- peerId: bootstrapConfigSignal.peerId,
3612
- probeMultiaddrs: bootstrapConfigSignal.probeMultiaddrs,
3613
- browserBootstrapMultiaddrs: bootstrapConfigSignal.browserBootstrapMultiaddrs ?? []
3614
- } : null;
3615
- if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
3616
- this.trace("deploy:bootstrap-proxy-fallback", {
3404
+ }
3405
+ this.emitProgress({
3406
+ stage: "publishing-bootstrap",
3407
+ label: "Reading relay multiaddrs",
3408
+ progress: 95,
3409
+ status: "info",
3410
+ itemHash: args.itemHash,
3411
+ detail: "Waiting for the relay to report its final public multiaddrs."
3412
+ });
3413
+ const relayMetadata = await fetchRelayMetadataForRuntime({
3414
+ runtime: args.runtime,
3415
+ fetch: (url, init) => fetch(url, init),
3416
+ preferSecureMetadata: profile === "uc-go-peer",
3417
+ attempts: 80,
3418
+ delayMs: 3e3,
3419
+ timeoutMs: 24e4,
3420
+ onAttempt: (result) => {
3421
+ const payload = result.payload && typeof result.payload === "object" ? result.payload : null;
3422
+ const metadata = payload?.metadata && typeof payload.metadata === "object" ? payload.metadata : null;
3423
+ this.trace("deploy:relay-metadata-attempt", {
3617
3424
  itemHash: args.itemHash,
3618
- deploymentToken: args.deploymentToken,
3619
- proxyUrl: runtime.proxyUrl,
3620
- bootstrapSignalRelayMetadata
3425
+ attempt: result.attempt,
3426
+ attempts: result.attempts,
3427
+ requestUrl: result.requestUrl,
3428
+ metadataUrl: result.metadataUrl,
3429
+ hostIpv4: result.hostIpv4,
3430
+ setupPort: result.setupPort,
3431
+ ok: result.ok,
3432
+ status: result.status,
3433
+ ready: result.ready,
3434
+ error: result.error ?? null,
3435
+ payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
3436
+ peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
3437
+ probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
3438
+ browserBootstrapMultiaddrCount: Array.isArray(
3439
+ metadata?.browser_bootstrap_multiaddrs
3440
+ ) ? metadata.browser_bootstrap_multiaddrs.length : 0
3621
3441
  });
3622
3442
  this.emitProgress({
3623
- stage: "deployment-confirmed",
3624
- label: "Continuing without HTTPS route",
3625
- progress: 94,
3626
- status: "warning",
3443
+ stage: "publishing-bootstrap",
3444
+ label: "Reading relay multiaddrs",
3445
+ progress: 95,
3446
+ status: result.ready ? "success" : "info",
3627
3447
  itemHash: args.itemHash,
3628
- detail: "The 2n6 HTTPS route stayed inactive, so guest setup continued without Caddy. Secure browser routing can be enabled later when the proxy becomes available."
3448
+ detail: result.ready ? `Relay metadata ${result.attempt}/${result.attempts}: public multiaddrs available.` : `Relay metadata ${result.attempt}/${result.attempts}: waiting for the relay to report public multiaddrs.`
3629
3449
  });
3630
- runtime = {
3631
- ...runtime,
3632
- proxyUrl: null,
3633
- webAccess: runtime.webAccess ? {
3634
- ...runtime.webAccess,
3635
- active: false
3636
- } : runtime.webAccess
3637
- };
3638
3450
  }
3639
- }
3640
- let relayMetadata = bootstrapSignalRelayMetadata;
3641
- if (relayMetadata) {
3642
- this.trace("deploy:relay-metadata-from-bootstrap-signal", {
3643
- itemHash: args.itemHash,
3644
- peerId: relayMetadata.peerId,
3645
- probeMultiaddrCount: relayMetadata.probeMultiaddrs.length,
3646
- browserBootstrapMultiaddrCount: relayMetadata.browserBootstrapMultiaddrs.length
3647
- });
3648
- } else {
3649
- this.emitProgress({
3650
- stage: "publishing-bootstrap",
3651
- label: "Reading relay multiaddrs",
3652
- progress: 95,
3653
- status: "info",
3654
- itemHash: args.itemHash,
3655
- detail: "Waiting for the relay to report its final public multiaddrs."
3656
- });
3657
- relayMetadata = await fetchRelayMetadataForRuntime({
3658
- runtime,
3659
- fetch: (url, init) => fetch(url, init),
3660
- preferSecureMetadata: profile === "uc-go-peer" && Boolean(runtime.proxyUrl),
3661
- attempts: 80,
3662
- delayMs: 3e3,
3663
- timeoutMs: 24e4,
3664
- onAttempt: (result) => {
3665
- const payload = result.payload && typeof result.payload === "object" ? result.payload : null;
3666
- const metadata = payload?.metadata && typeof payload.metadata === "object" ? payload.metadata : null;
3667
- this.trace("deploy:relay-metadata-attempt", {
3668
- itemHash: args.itemHash,
3669
- attempt: result.attempt,
3670
- attempts: result.attempts,
3671
- requestUrl: result.requestUrl,
3672
- metadataUrl: result.metadataUrl,
3673
- hostIpv4: result.hostIpv4,
3674
- setupPort: result.setupPort,
3675
- ok: result.ok,
3676
- status: result.status,
3677
- ready: result.ready,
3678
- error: result.error ?? null,
3679
- payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
3680
- peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
3681
- probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
3682
- browserBootstrapMultiaddrCount: Array.isArray(
3683
- metadata?.browser_bootstrap_multiaddrs
3684
- ) ? metadata.browser_bootstrap_multiaddrs.length : 0
3685
- });
3686
- }
3687
- });
3688
- }
3451
+ });
3689
3452
  if (!relayMetadata) {
3690
3453
  throw new Error("Relay metadata did not include a peer ID and public multiaddrs.");
3691
3454
  }
3692
- if (profile === "uc-go-peer" && runtime.proxyUrl) {
3693
- this.emitProgress({
3694
- stage: "publishing-bootstrap",
3695
- label: "Verifying reachability",
3696
- progress: 96,
3697
- status: "info",
3698
- itemHash: args.itemHash,
3699
- detail: "Checking that the secure relay endpoint is reachable before trusting the deployment as ready."
3700
- });
3701
- const verificationUrl = (() => {
3702
- try {
3703
- return new URL("/bootstrap/metadata", runtime.proxyUrl).toString();
3704
- } catch {
3705
- return runtime.proxyUrl ?? "";
3706
- }
3707
- })();
3708
- let verificationResult = null;
3709
- for (let attempt = 0; attempt < UI_REACHABILITY_VERIFY_ATTEMPTS; attempt += 1) {
3710
- verificationResult = await verifyUcGoPeerReachability({
3711
- proxyUrl: verificationUrl,
3712
- verifyProxyHttp: true,
3713
- fetch: (url, init) => fetch(url, init),
3714
- tcpProbe: async () => ({ ok: null })
3715
- });
3716
- this.trace("deploy:reachability-verification-attempt", {
3717
- itemHash: args.itemHash,
3718
- attempt: attempt + 1,
3719
- attempts: UI_REACHABILITY_VERIFY_ATTEMPTS,
3720
- verificationUrl,
3721
- ok: verificationResult.ok,
3722
- checks: verificationResult.checks
3723
- });
3724
- const proxyHttpCheck2 = verificationResult.checks["https:proxy"];
3725
- if (verificationResult.ok && proxyHttpCheck2?.ok === true) {
3726
- break;
3727
- }
3728
- if (attempt < UI_REACHABILITY_VERIFY_ATTEMPTS - 1) {
3729
- await new Promise(
3730
- (resolve) => globalThis.setTimeout(resolve, UI_REACHABILITY_VERIFY_DELAY_MS)
3731
- );
3732
- }
3733
- }
3734
- const proxyHttpCheck = verificationResult?.checks["https:proxy"];
3735
- if (!verificationResult?.ok || proxyHttpCheck?.ok !== true) {
3736
- throw new Error(
3737
- proxyHttpCheck?.error ? `Secure relay endpoint did not become reachable in time: ${proxyHttpCheck.error}` : "Secure relay endpoint did not become reachable in time."
3738
- );
3739
- }
3740
- }
3741
3455
  const { peerId } = relayMetadata;
3742
3456
  const registrationId = createRelayBootstrapRegistrationId(
3743
3457
  profile,
@@ -3758,7 +3472,17 @@ var SponsorRelayController = class {
3758
3472
  fetch: asJsonFetch,
3759
3473
  apiHost: this.client.apiHost,
3760
3474
  attempts: 24,
3761
- delayMs: 2500
3475
+ delayMs: 2500,
3476
+ onAttempt: (match, attempt, attempts) => {
3477
+ this.emitProgress({
3478
+ stage: "publishing-bootstrap",
3479
+ label: "Waiting for guest bootstrap registration",
3480
+ progress: 97,
3481
+ status: match ? "success" : "info",
3482
+ itemHash: args.itemHash,
3483
+ detail: match ? `Guest bootstrap registration ${attempt}/${attempts}: visible on Aleph.` : `Guest bootstrap registration ${attempt}/${attempts}: still waiting for the relay VM registration on Aleph.`
3484
+ });
3485
+ }
3762
3486
  });
3763
3487
  this.trace("deploy:bootstrap-registration-visible", {
3764
3488
  itemHash: args.itemHash,
@@ -3809,7 +3533,17 @@ var SponsorRelayController = class {
3809
3533
  fetch: asJsonFetch,
3810
3534
  apiHost: this.client.apiHost,
3811
3535
  attempts: 24,
3812
- delayMs: 2500
3536
+ delayMs: 2500,
3537
+ onAttempt: (match, attempt, attempts) => {
3538
+ this.emitProgress({
3539
+ stage: "publishing-bootstrap",
3540
+ label: "Waiting for bootstrap fallback",
3541
+ progress: 98,
3542
+ status: match ? "success" : "warning",
3543
+ itemHash: args.itemHash,
3544
+ detail: match ? `Bootstrap fallback ${attempt}/${attempts}: visible on Aleph.` : `Bootstrap fallback ${attempt}/${attempts}: waiting for the browser-published registration on Aleph.`
3545
+ });
3546
+ }
3813
3547
  });
3814
3548
  this.trace("deploy:bootstrap-registration-fallback-visible", {
3815
3549
  itemHash: args.itemHash,
@@ -3855,7 +3589,6 @@ var SponsorRelayController = class {
3855
3589
  const currentProgress = this.state.deploymentProgress;
3856
3590
  const currentProgressIsForLatestHash = currentProgress.itemHash === itemHash || currentProgress.itemHash == null && currentProgress.stage !== "idle" && currentProgress.stage !== "error";
3857
3591
  const keepTerminalSuccess = this.lastCompletedDeploymentHash === itemHash || currentProgressIsForLatestHash && currentProgress.stage === "completed" && currentProgress.status === "success";
3858
- const keepSpecificInFlightProgress = currentProgressIsForLatestHash && currentProgress.stage !== "completed" && currentProgress.stage !== "error" && currentProgress.progress >= 93;
3859
3592
  const status = latest.details.messageStatus;
3860
3593
  if (status === "rejected") {
3861
3594
  this.emitProgress({
@@ -3898,16 +3631,6 @@ var SponsorRelayController = class {
3898
3631
  });
3899
3632
  return;
3900
3633
  }
3901
- if (keepSpecificInFlightProgress) {
3902
- this.trace("progress:retain-inflight", {
3903
- reason: "latest refresh would overwrite a more specific in-flight deployment phase",
3904
- itemHash,
3905
- currentStage: currentProgress.stage,
3906
- currentLabel: currentProgress.label,
3907
- currentProgress: currentProgress.progress
3908
- });
3909
- return;
3910
- }
3911
3634
  this.emitProgress({
3912
3635
  stage: "deployment-confirmed",
3913
3636
  label: "Waiting for runtime",
@@ -3918,16 +3641,6 @@ var SponsorRelayController = class {
3918
3641
  });
3919
3642
  return;
3920
3643
  }
3921
- if (keepSpecificInFlightProgress) {
3922
- this.trace("progress:retain-inflight", {
3923
- reason: "latest refresh would overwrite a more specific in-flight deployment phase",
3924
- itemHash,
3925
- currentStage: currentProgress.stage,
3926
- currentLabel: currentProgress.label,
3927
- currentProgress: currentProgress.progress
3928
- });
3929
- return;
3930
- }
3931
3644
  this.emitProgress({
3932
3645
  stage: "deployment-confirmed",
3933
3646
  label: "Runtime ready",
@@ -4421,7 +4134,7 @@ var SponsorRelayController = class {
4421
4134
  fetch: (url, init) => fetch(url, init),
4422
4135
  attempts: UI_DEPLOY_WAIT_ATTEMPTS,
4423
4136
  delayMs: UI_DEPLOY_WAIT_DELAY_MS,
4424
- onAttempt: (inspectionResult) => {
4137
+ onAttempt: (inspectionResult, attempt, attempts) => {
4425
4138
  if (inspectionResult.status === "processed") {
4426
4139
  this.emitProgress({
4427
4140
  stage: "deployment-confirmed",
@@ -4429,7 +4142,7 @@ var SponsorRelayController = class {
4429
4142
  progress: 82,
4430
4143
  status: "success",
4431
4144
  itemHash: result.itemHash,
4432
- detail: "Aleph processed the instance message."
4145
+ detail: `Aleph processing ${attempt}/${attempts}: instance message processed.`
4433
4146
  });
4434
4147
  return;
4435
4148
  }
@@ -4451,7 +4164,7 @@ var SponsorRelayController = class {
4451
4164
  progress: 76,
4452
4165
  status: "info",
4453
4166
  itemHash: result.itemHash,
4454
- detail: "Deployment submitted. Waiting for Aleph to process the instance message."
4167
+ detail: `Aleph processing ${attempt}/${attempts}: waiting for the instance message to be processed.`
4455
4168
  });
4456
4169
  }
4457
4170
  });
@@ -4517,7 +4230,7 @@ var SponsorRelayController = class {
4517
4230
  crnListUrl: this.props.crnListUrl,
4518
4231
  attempts: UI_RUNTIME_WAIT_ATTEMPTS,
4519
4232
  delayMs: UI_RUNTIME_WAIT_DELAY_MS,
4520
- onAttempt: (runtime2) => {
4233
+ onAttempt: (runtime2, attempt, attempts) => {
4521
4234
  if (runtime2.hostIpv4 && Object.keys(runtime2.mappedPorts ?? {}).length > 0) {
4522
4235
  this.emitProgress({
4523
4236
  stage: "deployment-confirmed",
@@ -4525,7 +4238,7 @@ var SponsorRelayController = class {
4525
4238
  progress: 92,
4526
4239
  status: "info",
4527
4240
  itemHash: result.itemHash,
4528
- detail: "Runtime networking and mapped ports are now available. Finishing relay setup and bootstrap verification."
4241
+ detail: `Runtime ${attempt}/${attempts}: networking and mapped ports are now available.`
4529
4242
  });
4530
4243
  return;
4531
4244
  }
@@ -4535,7 +4248,7 @@ var SponsorRelayController = class {
4535
4248
  progress: 90,
4536
4249
  status: "warning",
4537
4250
  itemHash: result.itemHash,
4538
- detail: runtime2.diagnostics?.reason ?? "Waiting for CRN runtime networking and mapped ports."
4251
+ detail: runtime2.diagnostics?.reason ? `Runtime ${attempt}/${attempts}: ${runtime2.diagnostics.reason}` : `Runtime ${attempt}/${attempts}: waiting for CRN runtime networking and mapped ports.`
4539
4252
  });
4540
4253
  }
4541
4254
  });
@@ -4597,15 +4310,6 @@ var SponsorRelayController = class {
4597
4310
  });
4598
4311
  if (attemptItemHash) {
4599
4312
  if (attemptDeploymentToken) {
4600
- this.emitProgress({
4601
- stage: "refreshing-instances",
4602
- label: "Cleaning up failed attempt",
4603
- progress: 16,
4604
- status: "warning",
4605
- itemHash: attemptItemHash,
4606
- detail: `Removing bootstrap handoff state for failed deployment on ${attemptLabel}.`,
4607
- error: lastError.message
4608
- });
4609
4313
  this.trace("deploy:bootstrap-config-cleanup-start", {
4610
4314
  itemHash: attemptItemHash,
4611
4315
  deploymentToken: attemptDeploymentToken
@@ -4631,26 +4335,8 @@ var SponsorRelayController = class {
4631
4335
  deploymentToken: attemptDeploymentToken,
4632
4336
  error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4633
4337
  });
4634
- this.emitProgress({
4635
- stage: "refreshing-instances",
4636
- label: "Cleanup warning",
4637
- progress: 16,
4638
- status: "warning",
4639
- itemHash: attemptItemHash,
4640
- detail: `Bootstrap cleanup reported an issue for ${attemptLabel}, but retry cleanup will continue.`,
4641
- error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4642
- });
4643
4338
  });
4644
4339
  }
4645
- this.emitProgress({
4646
- stage: "refreshing-instances",
4647
- label: "Discarding failed deployment",
4648
- progress: 17,
4649
- status: "warning",
4650
- itemHash: attemptItemHash,
4651
- detail: `Forgetting failed deployment ${attemptItemHash.slice(0, 12)} on ${attemptLabel} before retrying.`,
4652
- error: lastError.message
4653
- });
4654
4340
  this.trace("deploy:forget-failed-attempt-start", {
4655
4341
  itemHash: attemptItemHash,
4656
4342
  crnHash: candidateCrn.hash,
@@ -4673,30 +4359,12 @@ var SponsorRelayController = class {
4673
4359
  itemHash: attemptItemHash,
4674
4360
  error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4675
4361
  });
4676
- this.emitProgress({
4677
- stage: "refreshing-instances",
4678
- label: "Cleanup warning",
4679
- progress: 17,
4680
- status: "warning",
4681
- itemHash: attemptItemHash,
4682
- detail: `Failed deployment cleanup reported an issue for ${attemptLabel}.`,
4683
- error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4684
- });
4685
4362
  });
4686
4363
  this.trace("deploy:forget-failed-attempt-complete", {
4687
4364
  itemHash: attemptItemHash,
4688
4365
  crnHash: candidateCrn.hash,
4689
4366
  crnName: candidateCrn.name
4690
4367
  });
4691
- this.emitProgress({
4692
- stage: "refreshing-instances",
4693
- label: "Cleanup complete",
4694
- progress: 18,
4695
- status: "info",
4696
- itemHash: attemptItemHash,
4697
- detail: `Finished cleaning up failed deployment on ${attemptLabel}.`,
4698
- error: null
4699
- });
4700
4368
  }
4701
4369
  if (candidateIndex < orderedCrns.length - 1) {
4702
4370
  this.emitProgress({
@@ -4930,6 +4598,53 @@ var fieldStyle = {
4930
4598
  fontSize: "0.98rem",
4931
4599
  lineHeight: 1.45
4932
4600
  };
4601
+ var secondaryButtonStyle = {
4602
+ borderRadius: "0.95rem",
4603
+ border: "1px solid rgba(255,255,255,0.22)",
4604
+ background: "rgba(255,255,255,0.10)",
4605
+ color: "#f8fafc",
4606
+ padding: "0.72rem 0.95rem",
4607
+ fontFamily: '"DM Sans", "Inter", sans-serif',
4608
+ fontSize: "0.92rem",
4609
+ fontWeight: 700,
4610
+ lineHeight: 1.1,
4611
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.06)",
4612
+ backdropFilter: "blur(8px)"
4613
+ };
4614
+ var primaryButtonStyle = {
4615
+ borderRadius: "0.95rem",
4616
+ border: "1px solid rgba(251, 191, 36, 0.42)",
4617
+ background: "linear-gradient(135deg, #f6c453 0%, #f59e0b 100%)",
4618
+ color: "#281606",
4619
+ padding: "0.82rem 1rem",
4620
+ fontFamily: '"DM Sans", "Inter", sans-serif',
4621
+ fontSize: "0.96rem",
4622
+ fontWeight: 800,
4623
+ lineHeight: 1.1,
4624
+ boxShadow: "0 10px 24px rgba(245, 158, 11, 0.24)"
4625
+ };
4626
+ var dangerButtonStyle = {
4627
+ borderRadius: "0.95rem",
4628
+ border: "1px solid rgba(248, 113, 113, 0.45)",
4629
+ background: "rgba(239, 68, 68, 0.18)",
4630
+ color: "#ffe2e2",
4631
+ padding: "0.72rem 0.95rem",
4632
+ fontFamily: '"DM Sans", "Inter", sans-serif',
4633
+ fontSize: "0.9rem",
4634
+ fontWeight: 700,
4635
+ lineHeight: 1.1
4636
+ };
4637
+ var warningButtonStyle = {
4638
+ borderRadius: "0.95rem",
4639
+ border: "1px solid rgba(251, 146, 60, 0.4)",
4640
+ background: "rgba(251, 146, 60, 0.16)",
4641
+ color: "#fed7aa",
4642
+ padding: "0.72rem 0.95rem",
4643
+ fontFamily: '"DM Sans", "Inter", sans-serif',
4644
+ fontSize: "0.9rem",
4645
+ fontWeight: 700,
4646
+ lineHeight: 1.1
4647
+ };
4933
4648
  var themedLauncherBackground = "linear-gradient(135deg, var(--le-space-sponsor-relay-launcher-start, #4f46e5) 0%, var(--le-space-sponsor-relay-launcher-end, #6366f1) 100%)";
4934
4649
  var themedLauncherBorder = "var(--le-space-sponsor-relay-launcher-border, rgba(199, 210, 254, 0.42))";
4935
4650
  var themedLauncherBadgeBackground = "var(--le-space-sponsor-relay-launcher-badge-bg, rgba(49, 46, 129, 0.92))";
@@ -5335,7 +5050,15 @@ function SponsorRelayFab(props) {
5335
5050
  }
5336
5051
  )
5337
5052
  ] }),
5338
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void controller.refresh(), children: state.busy.refreshing ? "Syncing" : "Refresh" })
5053
+ /* @__PURE__ */ jsx(
5054
+ "button",
5055
+ {
5056
+ type: "button",
5057
+ style: secondaryButtonStyle,
5058
+ onClick: () => void controller.refresh(),
5059
+ children: state.busy.refreshing ? "Syncing" : "Refresh"
5060
+ }
5061
+ )
5339
5062
  ]
5340
5063
  }
5341
5064
  ),
@@ -5694,7 +5417,7 @@ function SponsorRelayFab(props) {
5694
5417
  {
5695
5418
  type: "button",
5696
5419
  onClick: () => void (state.wallet.connected ? controller.deploy() : controller.connectWallet()),
5697
- style: { width: "100%", marginTop: "1rem" },
5420
+ style: { ...primaryButtonStyle, width: "100%", marginTop: "1rem" },
5698
5421
  children: state.wallet.connected ? state.busy.deploying ? "Deploying\u2026" : "Deploy Relay" : "Connect MetaMask"
5699
5422
  }
5700
5423
  ),
@@ -5751,7 +5474,7 @@ function SponsorRelayFab(props) {
5751
5474
  }
5752
5475
  }
5753
5476
  ),
5754
- "Registration confirmed"
5477
+ "Aleph bootstrap registered"
5755
5478
  ]
5756
5479
  }
5757
5480
  ) : null
@@ -5811,6 +5534,7 @@ function SponsorRelayFab(props) {
5811
5534
  "button",
5812
5535
  {
5813
5536
  type: "button",
5537
+ style: dangerButtonStyle,
5814
5538
  onClick: () => void controller.deleteInstance(
5815
5539
  entry.instance.item_hash
5816
5540
  ),
@@ -5876,6 +5600,7 @@ function SponsorRelayFab(props) {
5876
5600
  "button",
5877
5601
  {
5878
5602
  type: "button",
5603
+ style: warningButtonStyle,
5879
5604
  disabled: !registrationHash || state.busy.deletingRegistrationHash === registrationHash,
5880
5605
  onClick: () => registrationHash ? void controller.deleteBootstrapRegistration(
5881
5606
  registrationHash