@le-space/ui 0.6.43 → 0.6.45

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.6.43",
3
+ "version": "0.6.45",
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.6.43",
48
- "@le-space/core": "0.6.43",
47
+ "@le-space/browser": "0.6.45",
48
+ "@le-space/core": "0.6.45",
49
49
  "viem": "^2.53.1"
50
50
  },
51
51
  "peerDependencies": {
package/react/index.js CHANGED
@@ -43,7 +43,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
43
43
  var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
44
44
 
45
45
  // src/shared/package-version.ts
46
- var UI_PACKAGE_VERSION = "0.6.43";
46
+ var UI_PACKAGE_VERSION = "0.6.45";
47
47
 
48
48
  // src/shared/format.ts
49
49
  function shortHash(value, head = 8, tail = 6) {
@@ -3425,12 +3425,30 @@ async function waitForVmBootstrapConfigSignal(args) {
3425
3425
  }).catch(() => []);
3426
3426
  const match = signals.find((entry) => entry.status === expectedStatus) ?? null;
3427
3427
  if (match) return match;
3428
+ if (args.shouldAbort && await args.shouldAbort().catch(() => false)) {
3429
+ return null;
3430
+ }
3428
3431
  if (attempt < attempts - 1) {
3429
3432
  await sleep2(delayMs);
3430
3433
  }
3431
3434
  }
3432
3435
  return null;
3433
3436
  }
3437
+ async function crnExecutionAbsent(args) {
3438
+ const base = String(args.crnUrl ?? "").trim();
3439
+ if (!base) return false;
3440
+ try {
3441
+ const response = await args.fetch(new URL("/about/executions/list", base).toString(), {
3442
+ cache: "no-store"
3443
+ });
3444
+ if (!response.ok) return false;
3445
+ const payload = await response.json();
3446
+ if (!payload || typeof payload !== "object") return false;
3447
+ return !(args.itemHash in payload);
3448
+ } catch {
3449
+ return false;
3450
+ }
3451
+ }
3434
3452
 
3435
3453
  // ../core/src/bootstrap-reconcile.ts
3436
3454
  function createRelayBootstrapRegistrationId(profile, instanceName, itemHash) {
@@ -4442,10 +4460,29 @@ var SponsorRelayController = class {
4442
4460
  detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
4443
4461
  });
4444
4462
  let latestRuntime = runtime;
4463
+ let activationAbsentStreak = 0;
4445
4464
  for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
4446
4465
  if (latestRuntime.webAccess?.active === true) {
4447
4466
  break;
4448
4467
  }
4468
+ if (attempt > 0 && attempt % 6 === 0) {
4469
+ const absent = await crnExecutionAbsent({
4470
+ crnUrl: args.crnUrl,
4471
+ itemHash: args.itemHash,
4472
+ fetch: asJsonFetch
4473
+ });
4474
+ activationAbsentStreak = absent ? activationAbsentStreak + 1 : 0;
4475
+ if (activationAbsentStreak >= 4) {
4476
+ this.trace("deploy:crn-execution-absent-abort", {
4477
+ itemHash: args.itemHash,
4478
+ crnUrl: args.crnUrl ?? null,
4479
+ phase: "https-activation"
4480
+ });
4481
+ throw new Error(
4482
+ "The CRN never scheduled the relay VM (execution absent while waiting for HTTPS activation)."
4483
+ );
4484
+ }
4485
+ }
4449
4486
  await new Promise(
4450
4487
  (resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
4451
4488
  );
@@ -4552,7 +4589,37 @@ var SponsorRelayController = class {
4552
4589
  // recovery path, which used to poll the guest over plain HTTP (mixed
4553
4590
  // content on an HTTPS origin).
4554
4591
  attempts: 100,
4555
- delayMs: 3e3
4592
+ delayMs: 3e3,
4593
+ // Early failover (relay-button#83): a silently broken CRN accepts the
4594
+ // allocation but never schedules the VM. Probe the CRN's execution
4595
+ // list alongside the signal wait; after 4 consecutive "absent"
4596
+ // sightings (~2 min into the window) stop waiting — the full window
4597
+ // costs ~5 min and the VM will never publish a signal.
4598
+ shouldAbort: /* @__PURE__ */ (() => {
4599
+ let absentStreak = 0;
4600
+ let probeCountdown = 0;
4601
+ return async () => {
4602
+ if (probeCountdown > 0) {
4603
+ probeCountdown -= 1;
4604
+ return false;
4605
+ }
4606
+ probeCountdown = 9;
4607
+ const absent = await crnExecutionAbsent({
4608
+ crnUrl: args.crnUrl,
4609
+ itemHash: args.itemHash,
4610
+ fetch: asJsonFetch
4611
+ });
4612
+ absentStreak = absent ? absentStreak + 1 : 0;
4613
+ if (absentStreak >= 4) {
4614
+ this.trace("deploy:crn-execution-absent-abort", {
4615
+ itemHash: args.itemHash,
4616
+ crnUrl: args.crnUrl ?? null
4617
+ });
4618
+ return true;
4619
+ }
4620
+ return false;
4621
+ };
4622
+ })()
4556
4623
  });
4557
4624
  this.trace("deploy:bootstrap-config-signal-visible", {
4558
4625
  itemHash: args.itemHash,
@@ -5738,7 +5805,8 @@ var SponsorRelayController = class {
5738
5805
  await this.configureRelayBootstrapRegistration({
5739
5806
  itemHash: result.itemHash,
5740
5807
  runtime,
5741
- deploymentToken: attemptDeploymentToken
5808
+ deploymentToken: attemptDeploymentToken,
5809
+ crnUrl: candidateCrn.address ?? null
5742
5810
  });
5743
5811
  this.patch({
5744
5812
  busy: { deploying: false }
package/shared/index.d.ts CHANGED
@@ -341,7 +341,7 @@ declare const STALE_INSTANCE_ALLOCATION_COOLDOWN_MS: number;
341
341
  declare const RECENT_INSTANCE_RUNTIME_GRACE_MS: number;
342
342
  declare const DEPLOYMENT_PENDING_WARNING_MS: number;
343
343
 
344
- declare const UI_PACKAGE_VERSION = "0.6.43";
344
+ declare const UI_PACKAGE_VERSION = "0.6.45";
345
345
 
346
346
  interface RootfsRequiredPortForward {
347
347
  port: number;
package/shared/index.js CHANGED
@@ -42,7 +42,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
42
42
  var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
43
43
 
44
44
  // src/shared/package-version.ts
45
- var UI_PACKAGE_VERSION = "0.6.43";
45
+ var UI_PACKAGE_VERSION = "0.6.45";
46
46
 
47
47
  // src/shared/format.ts
48
48
  function shortHash(value, head = 8, tail = 6) {
@@ -3424,12 +3424,30 @@ async function waitForVmBootstrapConfigSignal(args) {
3424
3424
  }).catch(() => []);
3425
3425
  const match = signals.find((entry) => entry.status === expectedStatus) ?? null;
3426
3426
  if (match) return match;
3427
+ if (args.shouldAbort && await args.shouldAbort().catch(() => false)) {
3428
+ return null;
3429
+ }
3427
3430
  if (attempt < attempts - 1) {
3428
3431
  await sleep2(delayMs);
3429
3432
  }
3430
3433
  }
3431
3434
  return null;
3432
3435
  }
3436
+ async function crnExecutionAbsent(args) {
3437
+ const base = String(args.crnUrl ?? "").trim();
3438
+ if (!base) return false;
3439
+ try {
3440
+ const response = await args.fetch(new URL("/about/executions/list", base).toString(), {
3441
+ cache: "no-store"
3442
+ });
3443
+ if (!response.ok) return false;
3444
+ const payload = await response.json();
3445
+ if (!payload || typeof payload !== "object") return false;
3446
+ return !(args.itemHash in payload);
3447
+ } catch {
3448
+ return false;
3449
+ }
3450
+ }
3433
3451
 
3434
3452
  // ../core/src/bootstrap-reconcile.ts
3435
3453
  function createRelayBootstrapRegistrationId(profile, instanceName, itemHash) {
@@ -4441,10 +4459,29 @@ var SponsorRelayController = class {
4441
4459
  detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
4442
4460
  });
4443
4461
  let latestRuntime = runtime;
4462
+ let activationAbsentStreak = 0;
4444
4463
  for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
4445
4464
  if (latestRuntime.webAccess?.active === true) {
4446
4465
  break;
4447
4466
  }
4467
+ if (attempt > 0 && attempt % 6 === 0) {
4468
+ const absent = await crnExecutionAbsent({
4469
+ crnUrl: args.crnUrl,
4470
+ itemHash: args.itemHash,
4471
+ fetch: asJsonFetch
4472
+ });
4473
+ activationAbsentStreak = absent ? activationAbsentStreak + 1 : 0;
4474
+ if (activationAbsentStreak >= 4) {
4475
+ this.trace("deploy:crn-execution-absent-abort", {
4476
+ itemHash: args.itemHash,
4477
+ crnUrl: args.crnUrl ?? null,
4478
+ phase: "https-activation"
4479
+ });
4480
+ throw new Error(
4481
+ "The CRN never scheduled the relay VM (execution absent while waiting for HTTPS activation)."
4482
+ );
4483
+ }
4484
+ }
4448
4485
  await new Promise(
4449
4486
  (resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
4450
4487
  );
@@ -4551,7 +4588,37 @@ var SponsorRelayController = class {
4551
4588
  // recovery path, which used to poll the guest over plain HTTP (mixed
4552
4589
  // content on an HTTPS origin).
4553
4590
  attempts: 100,
4554
- delayMs: 3e3
4591
+ delayMs: 3e3,
4592
+ // Early failover (relay-button#83): a silently broken CRN accepts the
4593
+ // allocation but never schedules the VM. Probe the CRN's execution
4594
+ // list alongside the signal wait; after 4 consecutive "absent"
4595
+ // sightings (~2 min into the window) stop waiting — the full window
4596
+ // costs ~5 min and the VM will never publish a signal.
4597
+ shouldAbort: /* @__PURE__ */ (() => {
4598
+ let absentStreak = 0;
4599
+ let probeCountdown = 0;
4600
+ return async () => {
4601
+ if (probeCountdown > 0) {
4602
+ probeCountdown -= 1;
4603
+ return false;
4604
+ }
4605
+ probeCountdown = 9;
4606
+ const absent = await crnExecutionAbsent({
4607
+ crnUrl: args.crnUrl,
4608
+ itemHash: args.itemHash,
4609
+ fetch: asJsonFetch
4610
+ });
4611
+ absentStreak = absent ? absentStreak + 1 : 0;
4612
+ if (absentStreak >= 4) {
4613
+ this.trace("deploy:crn-execution-absent-abort", {
4614
+ itemHash: args.itemHash,
4615
+ crnUrl: args.crnUrl ?? null
4616
+ });
4617
+ return true;
4618
+ }
4619
+ return false;
4620
+ };
4621
+ })()
4555
4622
  });
4556
4623
  this.trace("deploy:bootstrap-config-signal-visible", {
4557
4624
  itemHash: args.itemHash,
@@ -5737,7 +5804,8 @@ var SponsorRelayController = class {
5737
5804
  await this.configureRelayBootstrapRegistration({
5738
5805
  itemHash: result.itemHash,
5739
5806
  runtime,
5740
- deploymentToken: attemptDeploymentToken
5807
+ deploymentToken: attemptDeploymentToken,
5808
+ crnUrl: candidateCrn.address ?? null
5741
5809
  });
5742
5810
  this.patch({
5743
5811
  busy: { deploying: false }