@le-space/ui 0.6.44 → 0.6.46
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 +8 -0
- package/react/index.js +62 -4
- package/shared/index.d.ts +9 -1
- package/shared/index.js +46 -3
- package/svelte/SponsorRelayFab.svelte +13 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.46",
|
|
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.
|
|
48
|
-
"@le-space/core": "0.6.
|
|
47
|
+
"@le-space/browser": "0.6.46",
|
|
48
|
+
"@le-space/core": "0.6.46",
|
|
49
49
|
"viem": "^2.53.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
package/react/index.d.ts
CHANGED
|
@@ -315,6 +315,12 @@ interface SponsorRelayState {
|
|
|
315
315
|
balance: BalanceResponse | null;
|
|
316
316
|
crns: Crn[];
|
|
317
317
|
selectedCrn: Crn | null;
|
|
318
|
+
crnOptions: Array<{
|
|
319
|
+
hash: string;
|
|
320
|
+
name: string | null;
|
|
321
|
+
score: number | null;
|
|
322
|
+
}>;
|
|
323
|
+
crnPinnedByUser: boolean;
|
|
318
324
|
instances: CompactInstanceRecord[];
|
|
319
325
|
bootstrapRegistrations: CompactBootstrapRegistrationRecord[];
|
|
320
326
|
orphanBootstrapRegistrations: CompactBootstrapRegistrationRecord[];
|
|
@@ -366,6 +372,8 @@ declare class SponsorRelayController {
|
|
|
366
372
|
setUcanStoreBootstrapField(field: keyof SponsorRelayUcanStoreBootstrapInput, value: string): void;
|
|
367
373
|
setTierId(tierId: string): void;
|
|
368
374
|
private recomputePricingSummary;
|
|
375
|
+
/** Pin a specific CRN as the first deploy attempt (null = automatic). */
|
|
376
|
+
selectCrn(hash: string | null): void;
|
|
369
377
|
private queueManifestRefresh;
|
|
370
378
|
connectWallet(): Promise<void>;
|
|
371
379
|
private refreshWalletDerivedState;
|
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.
|
|
46
|
+
var UI_PACKAGE_VERSION = "0.6.46";
|
|
47
47
|
|
|
48
48
|
// src/shared/format.ts
|
|
49
49
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -3826,6 +3826,8 @@ function defaultState(props = {}) {
|
|
|
3826
3826
|
balance: null,
|
|
3827
3827
|
crns: [],
|
|
3828
3828
|
selectedCrn: null,
|
|
3829
|
+
crnOptions: [],
|
|
3830
|
+
crnPinnedByUser: false,
|
|
3829
3831
|
instances: [],
|
|
3830
3832
|
bootstrapRegistrations: [],
|
|
3831
3833
|
orphanBootstrapRegistrations: [],
|
|
@@ -4460,10 +4462,29 @@ var SponsorRelayController = class {
|
|
|
4460
4462
|
detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
|
|
4461
4463
|
});
|
|
4462
4464
|
let latestRuntime = runtime;
|
|
4465
|
+
let activationAbsentStreak = 0;
|
|
4463
4466
|
for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
|
|
4464
4467
|
if (latestRuntime.webAccess?.active === true) {
|
|
4465
4468
|
break;
|
|
4466
4469
|
}
|
|
4470
|
+
if (attempt > 0 && attempt % 6 === 0) {
|
|
4471
|
+
const absent = await crnExecutionAbsent({
|
|
4472
|
+
crnUrl: args.crnUrl,
|
|
4473
|
+
itemHash: args.itemHash,
|
|
4474
|
+
fetch: asJsonFetch
|
|
4475
|
+
});
|
|
4476
|
+
activationAbsentStreak = absent ? activationAbsentStreak + 1 : 0;
|
|
4477
|
+
if (activationAbsentStreak >= 4) {
|
|
4478
|
+
this.trace("deploy:crn-execution-absent-abort", {
|
|
4479
|
+
itemHash: args.itemHash,
|
|
4480
|
+
crnUrl: args.crnUrl ?? null,
|
|
4481
|
+
phase: "https-activation"
|
|
4482
|
+
});
|
|
4483
|
+
throw new Error(
|
|
4484
|
+
"The CRN never scheduled the relay VM (execution absent while waiting for HTTPS activation)."
|
|
4485
|
+
);
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4467
4488
|
await new Promise(
|
|
4468
4489
|
(resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
|
|
4469
4490
|
);
|
|
@@ -5238,15 +5259,26 @@ var SponsorRelayController = class {
|
|
|
5238
5259
|
const balance = this.state.balance;
|
|
5239
5260
|
const quote = pricing && tier && balance ? buildPaymentQuote(tier, pricing, balance) : null;
|
|
5240
5261
|
const spec = pricing && tier ? tierSpec(pricing, tier) : null;
|
|
5241
|
-
const
|
|
5262
|
+
const compatibleList = compatibleCrnsForTier(this.state.crns, {
|
|
5242
5263
|
...this.state,
|
|
5243
5264
|
pricingSummary: {
|
|
5244
5265
|
...this.state.pricingSummary,
|
|
5245
5266
|
pricing,
|
|
5246
5267
|
tier
|
|
5247
5268
|
}
|
|
5248
|
-
})
|
|
5269
|
+
});
|
|
5270
|
+
const pinned = this.state.crnPinnedByUser ? compatibleList.find(
|
|
5271
|
+
(crn) => crn.hash === this.state.selectedCrn?.hash
|
|
5272
|
+
) ?? null : null;
|
|
5273
|
+
const selectedCrn = pinned ?? compatibleList[0] ?? null;
|
|
5274
|
+
const crnOptions = compatibleList.slice(0, 15).map((crn) => ({
|
|
5275
|
+
hash: crn.hash,
|
|
5276
|
+
name: crn.name ?? null,
|
|
5277
|
+
score: typeof crn.score === "number" ? crn.score : null
|
|
5278
|
+
}));
|
|
5249
5279
|
this.patch({
|
|
5280
|
+
crnOptions,
|
|
5281
|
+
crnPinnedByUser: this.state.crnPinnedByUser && pinned != null,
|
|
5250
5282
|
pricingSummary: {
|
|
5251
5283
|
pricing,
|
|
5252
5284
|
tier,
|
|
@@ -5259,6 +5291,17 @@ var SponsorRelayController = class {
|
|
|
5259
5291
|
selectedCrn
|
|
5260
5292
|
});
|
|
5261
5293
|
}
|
|
5294
|
+
/** Pin a specific CRN as the first deploy attempt (null = automatic). */
|
|
5295
|
+
selectCrn(hash) {
|
|
5296
|
+
if (!hash) {
|
|
5297
|
+
this.patch({ crnPinnedByUser: false });
|
|
5298
|
+
this.recomputePricingSummary();
|
|
5299
|
+
return;
|
|
5300
|
+
}
|
|
5301
|
+
const match = this.state.crns.find((crn) => crn.hash === hash) ?? null;
|
|
5302
|
+
if (!match) return;
|
|
5303
|
+
this.patch({ selectedCrn: match, crnPinnedByUser: true });
|
|
5304
|
+
}
|
|
5262
5305
|
queueManifestRefresh() {
|
|
5263
5306
|
if (!this.state.ready) {
|
|
5264
5307
|
return;
|
|
@@ -7375,7 +7418,22 @@ function SponsorRelayFab(props) {
|
|
|
7375
7418
|
" credits required"
|
|
7376
7419
|
] }),
|
|
7377
7420
|
/* @__PURE__ */ jsx("div", { children: state.rootfsHealth.label }),
|
|
7378
|
-
/* @__PURE__ */ jsx("div", { children:
|
|
7421
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
|
|
7422
|
+
"select",
|
|
7423
|
+
{
|
|
7424
|
+
"aria-label": "Select CRN",
|
|
7425
|
+
value: state.crnPinnedByUser ? state.selectedCrn?.hash ?? "" : "",
|
|
7426
|
+
onChange: (event) => controller.selectCrn(event.currentTarget.value || null),
|
|
7427
|
+
children: [
|
|
7428
|
+
/* @__PURE__ */ jsxs("option", { value: "", children: [
|
|
7429
|
+
"Auto (",
|
|
7430
|
+
state.crnOptions[0]?.name ?? "best score",
|
|
7431
|
+
")"
|
|
7432
|
+
] }),
|
|
7433
|
+
state.crnOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option.hash, children: (option.name ?? option.hash.slice(0, 8)) + (option.score != null ? ` \xB7 ${option.score.toFixed(2)}` : "") }, option.hash))
|
|
7434
|
+
]
|
|
7435
|
+
}
|
|
7436
|
+
) }),
|
|
7379
7437
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
7380
7438
|
"Ports",
|
|
7381
7439
|
" ",
|
package/shared/index.d.ts
CHANGED
|
@@ -313,6 +313,12 @@ interface SponsorRelayState {
|
|
|
313
313
|
balance: BalanceResponse | null;
|
|
314
314
|
crns: Crn[];
|
|
315
315
|
selectedCrn: Crn | null;
|
|
316
|
+
crnOptions: Array<{
|
|
317
|
+
hash: string;
|
|
318
|
+
name: string | null;
|
|
319
|
+
score: number | null;
|
|
320
|
+
}>;
|
|
321
|
+
crnPinnedByUser: boolean;
|
|
316
322
|
instances: CompactInstanceRecord[];
|
|
317
323
|
bootstrapRegistrations: CompactBootstrapRegistrationRecord[];
|
|
318
324
|
orphanBootstrapRegistrations: CompactBootstrapRegistrationRecord[];
|
|
@@ -341,7 +347,7 @@ declare const STALE_INSTANCE_ALLOCATION_COOLDOWN_MS: number;
|
|
|
341
347
|
declare const RECENT_INSTANCE_RUNTIME_GRACE_MS: number;
|
|
342
348
|
declare const DEPLOYMENT_PENDING_WARNING_MS: number;
|
|
343
349
|
|
|
344
|
-
declare const UI_PACKAGE_VERSION = "0.6.
|
|
350
|
+
declare const UI_PACKAGE_VERSION = "0.6.46";
|
|
345
351
|
|
|
346
352
|
interface RootfsRequiredPortForward {
|
|
347
353
|
port: number;
|
|
@@ -425,6 +431,8 @@ declare class SponsorRelayController {
|
|
|
425
431
|
setUcanStoreBootstrapField(field: keyof SponsorRelayUcanStoreBootstrapInput, value: string): void;
|
|
426
432
|
setTierId(tierId: string): void;
|
|
427
433
|
private recomputePricingSummary;
|
|
434
|
+
/** Pin a specific CRN as the first deploy attempt (null = automatic). */
|
|
435
|
+
selectCrn(hash: string | null): void;
|
|
428
436
|
private queueManifestRefresh;
|
|
429
437
|
connectWallet(): Promise<void>;
|
|
430
438
|
private refreshWalletDerivedState;
|
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.
|
|
45
|
+
var UI_PACKAGE_VERSION = "0.6.46";
|
|
46
46
|
|
|
47
47
|
// src/shared/format.ts
|
|
48
48
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -3825,6 +3825,8 @@ function defaultState(props = {}) {
|
|
|
3825
3825
|
balance: null,
|
|
3826
3826
|
crns: [],
|
|
3827
3827
|
selectedCrn: null,
|
|
3828
|
+
crnOptions: [],
|
|
3829
|
+
crnPinnedByUser: false,
|
|
3828
3830
|
instances: [],
|
|
3829
3831
|
bootstrapRegistrations: [],
|
|
3830
3832
|
orphanBootstrapRegistrations: [],
|
|
@@ -4459,10 +4461,29 @@ var SponsorRelayController = class {
|
|
|
4459
4461
|
detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
|
|
4460
4462
|
});
|
|
4461
4463
|
let latestRuntime = runtime;
|
|
4464
|
+
let activationAbsentStreak = 0;
|
|
4462
4465
|
for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
|
|
4463
4466
|
if (latestRuntime.webAccess?.active === true) {
|
|
4464
4467
|
break;
|
|
4465
4468
|
}
|
|
4469
|
+
if (attempt > 0 && attempt % 6 === 0) {
|
|
4470
|
+
const absent = await crnExecutionAbsent({
|
|
4471
|
+
crnUrl: args.crnUrl,
|
|
4472
|
+
itemHash: args.itemHash,
|
|
4473
|
+
fetch: asJsonFetch
|
|
4474
|
+
});
|
|
4475
|
+
activationAbsentStreak = absent ? activationAbsentStreak + 1 : 0;
|
|
4476
|
+
if (activationAbsentStreak >= 4) {
|
|
4477
|
+
this.trace("deploy:crn-execution-absent-abort", {
|
|
4478
|
+
itemHash: args.itemHash,
|
|
4479
|
+
crnUrl: args.crnUrl ?? null,
|
|
4480
|
+
phase: "https-activation"
|
|
4481
|
+
});
|
|
4482
|
+
throw new Error(
|
|
4483
|
+
"The CRN never scheduled the relay VM (execution absent while waiting for HTTPS activation)."
|
|
4484
|
+
);
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4466
4487
|
await new Promise(
|
|
4467
4488
|
(resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
|
|
4468
4489
|
);
|
|
@@ -5237,15 +5258,26 @@ var SponsorRelayController = class {
|
|
|
5237
5258
|
const balance = this.state.balance;
|
|
5238
5259
|
const quote = pricing && tier && balance ? buildPaymentQuote(tier, pricing, balance) : null;
|
|
5239
5260
|
const spec = pricing && tier ? tierSpec(pricing, tier) : null;
|
|
5240
|
-
const
|
|
5261
|
+
const compatibleList = compatibleCrnsForTier(this.state.crns, {
|
|
5241
5262
|
...this.state,
|
|
5242
5263
|
pricingSummary: {
|
|
5243
5264
|
...this.state.pricingSummary,
|
|
5244
5265
|
pricing,
|
|
5245
5266
|
tier
|
|
5246
5267
|
}
|
|
5247
|
-
})
|
|
5268
|
+
});
|
|
5269
|
+
const pinned = this.state.crnPinnedByUser ? compatibleList.find(
|
|
5270
|
+
(crn) => crn.hash === this.state.selectedCrn?.hash
|
|
5271
|
+
) ?? null : null;
|
|
5272
|
+
const selectedCrn = pinned ?? compatibleList[0] ?? null;
|
|
5273
|
+
const crnOptions = compatibleList.slice(0, 15).map((crn) => ({
|
|
5274
|
+
hash: crn.hash,
|
|
5275
|
+
name: crn.name ?? null,
|
|
5276
|
+
score: typeof crn.score === "number" ? crn.score : null
|
|
5277
|
+
}));
|
|
5248
5278
|
this.patch({
|
|
5279
|
+
crnOptions,
|
|
5280
|
+
crnPinnedByUser: this.state.crnPinnedByUser && pinned != null,
|
|
5249
5281
|
pricingSummary: {
|
|
5250
5282
|
pricing,
|
|
5251
5283
|
tier,
|
|
@@ -5258,6 +5290,17 @@ var SponsorRelayController = class {
|
|
|
5258
5290
|
selectedCrn
|
|
5259
5291
|
});
|
|
5260
5292
|
}
|
|
5293
|
+
/** Pin a specific CRN as the first deploy attempt (null = automatic). */
|
|
5294
|
+
selectCrn(hash) {
|
|
5295
|
+
if (!hash) {
|
|
5296
|
+
this.patch({ crnPinnedByUser: false });
|
|
5297
|
+
this.recomputePricingSummary();
|
|
5298
|
+
return;
|
|
5299
|
+
}
|
|
5300
|
+
const match = this.state.crns.find((crn) => crn.hash === hash) ?? null;
|
|
5301
|
+
if (!match) return;
|
|
5302
|
+
this.patch({ selectedCrn: match, crnPinnedByUser: true });
|
|
5303
|
+
}
|
|
5261
5304
|
queueManifestRefresh() {
|
|
5262
5305
|
if (!this.state.ready) {
|
|
5263
5306
|
return;
|
|
@@ -260,7 +260,19 @@ export let apiHosts = undefined
|
|
|
260
260
|
</div>
|
|
261
261
|
<div class="metric-card">
|
|
262
262
|
<span>CRN</span>
|
|
263
|
-
<
|
|
263
|
+
<select
|
|
264
|
+
class="crn-select"
|
|
265
|
+
aria-label="Select CRN"
|
|
266
|
+
value={state.crnPinnedByUser ? (state.selectedCrn?.hash ?? '') : ''}
|
|
267
|
+
on:change={(event) => controller.selectCrn(event.currentTarget.value || null)}
|
|
268
|
+
>
|
|
269
|
+
<option value="">Auto ({state.crnOptions[0]?.name ?? 'best score'})</option>
|
|
270
|
+
{#each state.crnOptions as option (option.hash)}
|
|
271
|
+
<option value={option.hash}>
|
|
272
|
+
{option.name ?? option.hash.slice(0, 8)}{option.score != null ? ` · ${option.score.toFixed(2)}` : ''}
|
|
273
|
+
</option>
|
|
274
|
+
{/each}
|
|
275
|
+
</select>
|
|
264
276
|
<small>{state.selectedCrn?.address ?? 'Auto-picked best compatible CRN'}</small>
|
|
265
277
|
</div>
|
|
266
278
|
<div class="metric-card">
|