@xenosystem/blocks 0.6.0 → 0.8.0
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/dist/agent/host-source.d.ts +118 -0
- package/dist/agent/host-source.js +228 -0
- package/dist/agent/index.d.ts +568 -0
- package/dist/agent/index.js +1439 -0
- package/dist/auth/index.d.ts +400 -5
- package/dist/auth/index.js +621 -4
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +11 -11
- package/dist/edit/index.d.ts +6 -6
- package/dist/edit/index.js +14 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/ops/index.d.ts +2 -2
- package/dist/ops/index.js +7 -7
- package/dist/time/index.d.ts +1 -1
- package/dist/time/index.js +4 -4
- package/dist/trust/index.d.ts +2 -2
- package/dist/trust/index.js +2 -2
- package/dist/types-C5T3uVwd.d.ts +425 -0
- package/package.json +11 -7
package/dist/auth/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import "../chunk-2KG3PWR4.js";
|
|
|
3
3
|
// src/auth/gate/panel.ts
|
|
4
4
|
import { createElement } from "react";
|
|
5
5
|
import { createRoot } from "react-dom/client";
|
|
6
|
-
import { bindConfig } from "@xenosystem/
|
|
6
|
+
import { bindConfig } from "@xenosystem/block-sdk";
|
|
7
7
|
|
|
8
8
|
// src/auth/gate/types.ts
|
|
9
9
|
function deriveAuthGatePhase(state) {
|
|
@@ -348,24 +348,641 @@ function Behind({ render }) {
|
|
|
348
348
|
return render();
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
-
// src/auth/
|
|
352
|
-
|
|
351
|
+
// src/auth/account/panel.ts
|
|
352
|
+
import { createElement as createElement3 } from "react";
|
|
353
|
+
import { createRoot as createRoot3 } from "react-dom/client";
|
|
354
|
+
import {
|
|
355
|
+
bindConfig as bindConfig2
|
|
356
|
+
} from "@xenosystem/block-sdk";
|
|
357
|
+
|
|
358
|
+
// src/auth/account/types.ts
|
|
359
|
+
var STEP_UP_ACTIONS = /* @__PURE__ */ new Set([
|
|
360
|
+
"signOut",
|
|
361
|
+
"manageBilling",
|
|
362
|
+
"purchase"
|
|
363
|
+
]);
|
|
364
|
+
function isSignedIn(status) {
|
|
365
|
+
return status === "signedIn" || status === "refreshing" || status === "offline";
|
|
366
|
+
}
|
|
367
|
+
function requiresStepUp(kind) {
|
|
368
|
+
return STEP_UP_ACTIONS.has(kind);
|
|
369
|
+
}
|
|
370
|
+
function creditsAge(credits, now) {
|
|
371
|
+
if (!credits) return null;
|
|
372
|
+
return Math.max(0, now - credits.asOf);
|
|
373
|
+
}
|
|
374
|
+
function formatAmount(amount, unit = "") {
|
|
375
|
+
const sign = amount > 0 ? "+" : "";
|
|
376
|
+
const suffix = unit ? ` ${unit}` : "";
|
|
377
|
+
return `${sign}${amount.toLocaleString()}${suffix}`;
|
|
378
|
+
}
|
|
379
|
+
function formatAge(ms) {
|
|
380
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s ago`;
|
|
381
|
+
if (ms < 36e5) return `${Math.round(ms / 6e4)}m ago`;
|
|
382
|
+
if (ms < 864e5) return `${Math.round(ms / 36e5)}h ago`;
|
|
383
|
+
return `${Math.round(ms / 864e5)}d ago`;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/auth/account/controller.ts
|
|
387
|
+
var EMPTY_STATE = { status: "unknown" };
|
|
388
|
+
function ageBucket(ms) {
|
|
389
|
+
if (ms < 6e4) return `s${Math.round(ms / 1e3)}`;
|
|
390
|
+
if (ms < 36e5) return `m${Math.round(ms / 6e4)}`;
|
|
391
|
+
if (ms < 864e5) return `h${Math.round(ms / 36e5)}`;
|
|
392
|
+
return `d${Math.round(ms / 864e5)}`;
|
|
393
|
+
}
|
|
394
|
+
var AccountController = class {
|
|
395
|
+
host;
|
|
396
|
+
now;
|
|
397
|
+
staleAfterMs;
|
|
398
|
+
maxLedger;
|
|
399
|
+
account = EMPTY_STATE;
|
|
400
|
+
ledger = [];
|
|
401
|
+
ledgerHasMore = false;
|
|
402
|
+
ledgerRev = -1;
|
|
403
|
+
ledgerExpanded = false;
|
|
404
|
+
listeners = /* @__PURE__ */ new Set();
|
|
405
|
+
snapshot = null;
|
|
406
|
+
constructor(options) {
|
|
407
|
+
this.host = options.host;
|
|
408
|
+
this.now = options.host.now ?? (() => Date.now());
|
|
409
|
+
this.staleAfterMs = options.staleAfterMs ?? 6e4;
|
|
410
|
+
this.maxLedger = Math.max(1, options.maxLedger ?? 500);
|
|
411
|
+
}
|
|
412
|
+
/* ── Subscription ──────────────────────────────────────────────────────── */
|
|
413
|
+
subscribe = (listener) => {
|
|
414
|
+
this.listeners.add(listener);
|
|
415
|
+
return () => this.listeners.delete(listener);
|
|
416
|
+
};
|
|
417
|
+
getState = () => {
|
|
418
|
+
if (!this.snapshot) {
|
|
419
|
+
const ageMs = creditsAge(this.account.credits, this.now());
|
|
420
|
+
this.snapshot = {
|
|
421
|
+
status: this.account.status,
|
|
422
|
+
identity: this.account.identity ?? null,
|
|
423
|
+
plan: this.account.plan ?? null,
|
|
424
|
+
credits: this.account.credits ?? null,
|
|
425
|
+
entitlements: this.account.entitlements ?? [],
|
|
426
|
+
seats: this.account.seats ?? [],
|
|
427
|
+
message: this.account.message ?? null,
|
|
428
|
+
ledger: this.ledger,
|
|
429
|
+
ledgerHasMore: this.ledgerHasMore,
|
|
430
|
+
ledgerExpanded: this.ledgerExpanded,
|
|
431
|
+
creditsAgeMs: ageMs,
|
|
432
|
+
// Stale is a fact about the DATA, not about the session. A signed-in user with a
|
|
433
|
+
// ten-minute-old balance is looking at a stale number and must be told so.
|
|
434
|
+
creditsStale: ageMs !== null && ageMs > this.staleAfterMs
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
return this.snapshot;
|
|
438
|
+
};
|
|
439
|
+
notify() {
|
|
440
|
+
this.snapshot = null;
|
|
441
|
+
for (const listener of this.listeners) listener();
|
|
442
|
+
}
|
|
443
|
+
/* ── Host-pushed state ─────────────────────────────────────────────────── */
|
|
444
|
+
/**
|
|
445
|
+
* Replace the account state.
|
|
446
|
+
*
|
|
447
|
+
* The host is authoritative. The panel does not merge cleverly, does not keep a "better" older
|
|
448
|
+
* value, and does not infer a status the host did not send — a panel that second-guesses its host
|
|
449
|
+
* is a second source of truth about who is signed in.
|
|
450
|
+
*
|
|
451
|
+
* @param next - The new state.
|
|
452
|
+
*/
|
|
453
|
+
setAccount(next) {
|
|
454
|
+
this.account = next;
|
|
455
|
+
this.notify();
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* A partial update, for hosts that push only what changed.
|
|
459
|
+
*
|
|
460
|
+
* `credits` is replaced wholesale rather than field-merged: a balance and its `asOf` are one fact,
|
|
461
|
+
* and merging a new number onto an old timestamp manufactures a lie.
|
|
462
|
+
*
|
|
463
|
+
* @param patch - Fields to overwrite.
|
|
464
|
+
*/
|
|
465
|
+
patchAccount(patch) {
|
|
466
|
+
this.account = { ...this.account, ...patch };
|
|
467
|
+
this.notify();
|
|
468
|
+
}
|
|
469
|
+
/** Set the session status alone. */
|
|
470
|
+
setStatus(status, message) {
|
|
471
|
+
if (status === "signedOut") {
|
|
472
|
+
this.account = { status, message };
|
|
473
|
+
this.ledger = [];
|
|
474
|
+
this.ledgerHasMore = false;
|
|
475
|
+
this.ledgerRev = -1;
|
|
476
|
+
this.notify();
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
this.account = { ...this.account, status, message };
|
|
480
|
+
this.notify();
|
|
481
|
+
}
|
|
482
|
+
/* ── Ledger ────────────────────────────────────────────────────────────── */
|
|
483
|
+
/**
|
|
484
|
+
* Apply a ledger delta.
|
|
485
|
+
*
|
|
486
|
+
* Append-oriented, with `rev` gating out-of-order deliveries — the same discipline as runs and
|
|
487
|
+
* console.
|
|
488
|
+
*
|
|
489
|
+
* @param delta - The update.
|
|
490
|
+
* @returns Whether it was applied.
|
|
491
|
+
*/
|
|
492
|
+
applyLedger(delta) {
|
|
493
|
+
if (delta.rev !== void 0 && delta.rev <= this.ledgerRev) return false;
|
|
494
|
+
if (delta.rev !== void 0) this.ledgerRev = delta.rev;
|
|
495
|
+
if (delta.clear) {
|
|
496
|
+
this.ledger = [];
|
|
497
|
+
this.ledgerHasMore = false;
|
|
498
|
+
}
|
|
499
|
+
if (delta.replace) {
|
|
500
|
+
this.ledger = delta.replace.slice(-this.maxLedger);
|
|
501
|
+
}
|
|
502
|
+
if (delta.append?.length) {
|
|
503
|
+
const byId = new Map(this.ledger.map((e) => [e.id, e]));
|
|
504
|
+
for (const entry of delta.append) byId.set(entry.id, entry);
|
|
505
|
+
this.ledger = [...byId.values()].sort((a, b) => b.ts - a.ts).slice(0, this.maxLedger);
|
|
506
|
+
}
|
|
507
|
+
if (delta.hasMore !== void 0) this.ledgerHasMore = delta.hasMore;
|
|
508
|
+
this.notify();
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
/* ── The passage of time ───────────────────────────────────────────────── */
|
|
512
|
+
/**
|
|
513
|
+
* Re-evaluate the balance's age.
|
|
514
|
+
*
|
|
515
|
+
* `getState` memoizes — it must, or `useSyncExternalStore` re-renders forever — which means the
|
|
516
|
+
* age would otherwise freeze at the moment of the last push, and a balance would sit on screen
|
|
517
|
+
* reading "as of 2s ago" for an hour. That is precisely the stale-as-fresh failure `asOf` exists
|
|
518
|
+
* to prevent, reintroduced by a caching bug.
|
|
519
|
+
*
|
|
520
|
+
* So time is an INPUT here, and the panel drives this on an interval. Notifying only when the
|
|
521
|
+
* rendered age actually changes keeps it off the re-render hot path: a one-second tick that
|
|
522
|
+
* re-renders once per second is honest; one that re-renders sixty times is waste.
|
|
523
|
+
*
|
|
524
|
+
* @returns Whether anything the view shows changed.
|
|
525
|
+
*/
|
|
526
|
+
tick() {
|
|
527
|
+
if (!this.account.credits) return false;
|
|
528
|
+
const previous = this.snapshot;
|
|
529
|
+
if (!previous) return false;
|
|
530
|
+
const ageMs = creditsAge(this.account.credits, this.now()) ?? 0;
|
|
531
|
+
const stale = ageMs > this.staleAfterMs;
|
|
532
|
+
if (stale === previous.creditsStale && ageBucket(ageMs) === ageBucket(previous.creditsAgeMs ?? 0)) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
this.notify();
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
/** Expand or collapse the ledger. A view preference. */
|
|
539
|
+
setLedgerExpanded(expanded) {
|
|
540
|
+
this.ledgerExpanded = expanded;
|
|
541
|
+
this.notify();
|
|
542
|
+
}
|
|
543
|
+
/* ── Intents ───────────────────────────────────────────────────────────── */
|
|
544
|
+
/**
|
|
545
|
+
* Ask the host to do something.
|
|
546
|
+
*
|
|
547
|
+
* **The panel never renders the result.** It does not flip to `signedOut` because the user
|
|
548
|
+
* clicked Sign out, and it does not add credits because the user clicked Purchase. It emits, and
|
|
549
|
+
* waits to be told — the same intents-only doctrine the rest of the family follows, and the only
|
|
550
|
+
* shape compatible with `XENO AUTH - SPEC.md` L12, where a sensitive action may be REFUSED at the
|
|
551
|
+
* step-up prompt after the click.
|
|
552
|
+
*
|
|
553
|
+
* @param kind - What to ask for.
|
|
554
|
+
* @param extra - `seatId` or `productId`.
|
|
555
|
+
* @returns The emitted action.
|
|
556
|
+
*/
|
|
557
|
+
request(kind, extra = {}) {
|
|
558
|
+
const action = { kind, ...extra };
|
|
559
|
+
if (requiresStepUp(kind)) action.stepUp = true;
|
|
560
|
+
this.host.emit("action", action);
|
|
561
|
+
return action;
|
|
562
|
+
}
|
|
563
|
+
/** Ask the host to re-read the balance. Cheap, idempotent, no step-up. */
|
|
564
|
+
refresh() {
|
|
565
|
+
return this.request("refresh");
|
|
566
|
+
}
|
|
567
|
+
/* ── Lifecycle ─────────────────────────────────────────────────────────── */
|
|
568
|
+
/**
|
|
569
|
+
* Serialize.
|
|
570
|
+
*
|
|
571
|
+
* **View preferences only.** No identity, no plan, no balance, no token.
|
|
572
|
+
*
|
|
573
|
+
* `XENO AUTH - SPEC.md` L9 forbids a refresh token in `localStorage` or `~/.xeno/*`, and this
|
|
574
|
+
* panel's one capability is `storage.local` — the very store L9 names. Persisting a balance would
|
|
575
|
+
* also reintroduce the stale-as-fresh problem `asOf` exists to solve: a number restored from disk
|
|
576
|
+
* has no honest timestamp.
|
|
577
|
+
*/
|
|
578
|
+
serialize() {
|
|
579
|
+
return { ledgerExpanded: this.ledgerExpanded };
|
|
580
|
+
}
|
|
581
|
+
/** Restore view preferences. */
|
|
582
|
+
deserialize(state) {
|
|
583
|
+
if (!state || typeof state !== "object") return;
|
|
584
|
+
const s = state;
|
|
585
|
+
if (typeof s.ledgerExpanded === "boolean") this.ledgerExpanded = s.ledgerExpanded;
|
|
586
|
+
this.notify();
|
|
587
|
+
}
|
|
588
|
+
/** Tear down. */
|
|
589
|
+
dispose() {
|
|
590
|
+
this.listeners.clear();
|
|
591
|
+
this.account = EMPTY_STATE;
|
|
592
|
+
this.ledger = [];
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
// src/auth/account/manifest.ts
|
|
597
|
+
import { WELL_KNOWN_PORT_SCHEMAS } from "@xenosystem/block-sdk";
|
|
598
|
+
var ACCOUNT_PANEL_ID = "xeno.core.account";
|
|
599
|
+
var accountManifest = {
|
|
600
|
+
id: ACCOUNT_PANEL_ID,
|
|
601
|
+
version: "0.1.0",
|
|
602
|
+
title: "Account",
|
|
603
|
+
icon: "user-round",
|
|
604
|
+
description: "Identity, plan, credits, entitlements and ledger \u2014 all host-pushed, never fetched. Balances carry the moment they were true. Sensitive actions are emitted as step-up intents, never performed.",
|
|
605
|
+
defaultSlot: "inspector",
|
|
606
|
+
inputs: [
|
|
607
|
+
{
|
|
608
|
+
id: "account",
|
|
609
|
+
name: "Account",
|
|
610
|
+
type: "object",
|
|
611
|
+
schema: WELL_KNOWN_PORT_SCHEMAS.ACCOUNT,
|
|
612
|
+
description: "{status, identity?, plan?, credits?, entitlements?, seats?, message?}. The host is authoritative. Contains NO token \u2014 see AUTH L9.",
|
|
613
|
+
multiple: false
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
id: "ledger",
|
|
617
|
+
name: "Ledger",
|
|
618
|
+
type: "object",
|
|
619
|
+
schema: WELL_KNOWN_PORT_SCHEMAS.LEDGER,
|
|
620
|
+
description: "{rev?, append?, replace?, clear?, hasMore?}. Append-oriented; `rev` gates out-of-order deliveries.",
|
|
621
|
+
multiple: true
|
|
622
|
+
}
|
|
623
|
+
],
|
|
624
|
+
outputs: [
|
|
625
|
+
{
|
|
626
|
+
id: "action",
|
|
627
|
+
name: "Action",
|
|
628
|
+
type: "object",
|
|
629
|
+
schema: WELL_KNOWN_PORT_SCHEMAS.ACCOUNT_ACTION,
|
|
630
|
+
description: "{kind, seatId?, productId?, stepUp?}. An INTENT. `stepUp: true` marks an action AUTH L12 requires a fresh interactive re-auth for; the host must not satisfy it silently from the broker."
|
|
631
|
+
}
|
|
632
|
+
],
|
|
633
|
+
commands: [
|
|
634
|
+
{
|
|
635
|
+
id: "refresh",
|
|
636
|
+
title: "Refresh",
|
|
637
|
+
description: "Ask the host to re-read the balance. Idempotent; no step-up.",
|
|
638
|
+
parameters: {}
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
id: "get_account",
|
|
642
|
+
title: "Get Account",
|
|
643
|
+
description: "Return the rendered account state \u2014 status, plan, balance and its asOf. Never a token.",
|
|
644
|
+
parameters: {}
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
id: "get_ledger",
|
|
648
|
+
title: "Get Ledger",
|
|
649
|
+
description: "Return the held ledger entries, newest first.",
|
|
650
|
+
parameters: {}
|
|
651
|
+
}
|
|
652
|
+
// NOTE: `signOut`, `purchase` and `manageBilling` are deliberately NOT commands. They require a
|
|
653
|
+
// fresh interactive re-auth (AUTH L12), and an agent-invocable command is by definition not
|
|
654
|
+
// interactive. Exposing them would make this panel the bypass for the step-up requirement.
|
|
655
|
+
],
|
|
656
|
+
config: [
|
|
657
|
+
{
|
|
658
|
+
key: "showLedger",
|
|
659
|
+
label: "Show ledger",
|
|
660
|
+
type: "boolean",
|
|
661
|
+
defaultValue: true,
|
|
662
|
+
description: "Show recent credit activity."
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
key: "showEntitlements",
|
|
666
|
+
label: "Show entitlements",
|
|
667
|
+
type: "boolean",
|
|
668
|
+
defaultValue: true,
|
|
669
|
+
description: "List what the plan includes. Display only \u2014 entitlement is enforced server-side (AUTH L10)."
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
key: "staleAfterMs",
|
|
673
|
+
label: "Stale after (ms)",
|
|
674
|
+
type: "number",
|
|
675
|
+
defaultValue: 6e4,
|
|
676
|
+
description: "How old a balance may be before it is labelled stale."
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
key: "maxLedger",
|
|
680
|
+
label: "Max ledger entries",
|
|
681
|
+
type: "number",
|
|
682
|
+
defaultValue: 500,
|
|
683
|
+
description: "Entries retained in memory."
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
key: "emptyHint",
|
|
687
|
+
label: "Empty hint",
|
|
688
|
+
type: "text",
|
|
689
|
+
description: "Shown when no account state has arrived.",
|
|
690
|
+
placeholder: "Waiting for account state\u2026"
|
|
691
|
+
}
|
|
692
|
+
],
|
|
693
|
+
capabilities: ["storage.local"],
|
|
694
|
+
sdk: "^1.1.0"
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// src/auth/account/react/AccountPanelView.tsx
|
|
698
|
+
import { useSyncExternalStore } from "react";
|
|
699
|
+
import {
|
|
700
|
+
Badge,
|
|
701
|
+
EmptyState,
|
|
702
|
+
Row,
|
|
703
|
+
RowList,
|
|
704
|
+
ScrollArea,
|
|
705
|
+
Section,
|
|
706
|
+
StatTile,
|
|
707
|
+
StatusBar,
|
|
708
|
+
TextButton,
|
|
709
|
+
Toolbar,
|
|
710
|
+
ToolbarGroup
|
|
711
|
+
} from "@xenosystem/workbench/primitives/react";
|
|
712
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
713
|
+
var STATUS_LABEL = {
|
|
714
|
+
unknown: "Loading",
|
|
715
|
+
signedOut: "Signed out",
|
|
716
|
+
signedIn: "Signed in",
|
|
717
|
+
refreshing: "Refreshing",
|
|
718
|
+
expired: "Session expired",
|
|
719
|
+
offline: "Offline"
|
|
720
|
+
};
|
|
721
|
+
var STATUS_TONE = {
|
|
722
|
+
unknown: "neutral",
|
|
723
|
+
signedOut: "neutral",
|
|
724
|
+
signedIn: "success",
|
|
725
|
+
refreshing: "neutral",
|
|
726
|
+
expired: "warning",
|
|
727
|
+
offline: "warning"
|
|
728
|
+
};
|
|
729
|
+
function AccountPanelView({
|
|
730
|
+
controller,
|
|
731
|
+
showLedger = true,
|
|
732
|
+
showEntitlements = true,
|
|
733
|
+
emptyHint
|
|
734
|
+
}) {
|
|
735
|
+
const state = useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);
|
|
736
|
+
if (state.status === "unknown") {
|
|
737
|
+
return /* @__PURE__ */ jsx2(EmptyState, { title: "Account", hint: emptyHint ?? "Waiting for account state\u2026" });
|
|
738
|
+
}
|
|
739
|
+
if (!isSignedIn(state.status)) {
|
|
740
|
+
return /* @__PURE__ */ jsx2(
|
|
741
|
+
EmptyState,
|
|
742
|
+
{
|
|
743
|
+
title: STATUS_LABEL[state.status],
|
|
744
|
+
hint: state.message ?? "Sign in to see your plan and credits.",
|
|
745
|
+
action: /* @__PURE__ */ jsx2(TextButton, { strong: true, onClick: () => controller.request("signIn"), children: "Sign in" })
|
|
746
|
+
}
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
const { identity, plan, credits } = state;
|
|
750
|
+
const unit = credits?.unit ?? "credits";
|
|
751
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
|
|
752
|
+
/* @__PURE__ */ jsx2(
|
|
753
|
+
Toolbar,
|
|
754
|
+
{
|
|
755
|
+
left: /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
756
|
+
/* @__PURE__ */ jsx2(Badge, { tone: STATUS_TONE[state.status], children: STATUS_LABEL[state.status] }),
|
|
757
|
+
plan ? /* @__PURE__ */ jsx2(Badge, { title: `Plan: ${plan.label}`, children: plan.label }) : null
|
|
758
|
+
] }),
|
|
759
|
+
right: /* @__PURE__ */ jsx2(ToolbarGroup, { end: true, children: /* @__PURE__ */ jsx2(TextButton, { onClick: () => controller.refresh(), children: "Refresh" }) })
|
|
760
|
+
}
|
|
761
|
+
),
|
|
762
|
+
/* @__PURE__ */ jsxs(ScrollArea, { children: [
|
|
763
|
+
/* @__PURE__ */ jsxs("div", { style: { padding: 8, display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
764
|
+
/* @__PURE__ */ jsx2("div", { style: { fontSize: 12, fontWeight: 600 }, children: identity?.name ?? identity?.sub }),
|
|
765
|
+
identity?.email ? /* @__PURE__ */ jsx2("div", { style: { fontSize: 10, opacity: 0.65 }, children: identity.email }) : null,
|
|
766
|
+
state.message ? /* @__PURE__ */ jsx2("div", { style: { fontSize: 10, opacity: 0.75, marginTop: 4 }, children: state.message }) : null
|
|
767
|
+
] }),
|
|
768
|
+
credits ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6, padding: "0 8px 8px" }, children: [
|
|
769
|
+
/* @__PURE__ */ jsx2(
|
|
770
|
+
StatTile,
|
|
771
|
+
{
|
|
772
|
+
label: unit,
|
|
773
|
+
value: credits.balance.toLocaleString(),
|
|
774
|
+
sub: state.creditsAgeMs === null ? void 0 : state.creditsStale ? `as of ${formatAge(state.creditsAgeMs)} \u2014 may be out of date` : `as of ${formatAge(state.creditsAgeMs)}`,
|
|
775
|
+
icon: state.creditsStale ? /* @__PURE__ */ jsx2(Badge, { tone: "warning", children: "stale" }) : void 0,
|
|
776
|
+
title: "Pushed by the host, not fetched by this panel."
|
|
777
|
+
}
|
|
778
|
+
),
|
|
779
|
+
credits.included !== void 0 ? /* @__PURE__ */ jsx2(
|
|
780
|
+
StatTile,
|
|
781
|
+
{
|
|
782
|
+
label: "included",
|
|
783
|
+
value: credits.included.toLocaleString(),
|
|
784
|
+
sub: credits.used === void 0 ? void 0 : `${credits.used.toLocaleString()} used`
|
|
785
|
+
}
|
|
786
|
+
) : null
|
|
787
|
+
] }) : null,
|
|
788
|
+
state.seats.length > 1 ? /* @__PURE__ */ jsx2(Section, { title: "Seats", flush: true, children: /* @__PURE__ */ jsx2(RowList, { children: state.seats.map((seat) => /* @__PURE__ */ jsx2(
|
|
789
|
+
Row,
|
|
790
|
+
{
|
|
791
|
+
noIcon: true,
|
|
792
|
+
label: seat.label,
|
|
793
|
+
trailing: seat.active ? /* @__PURE__ */ jsx2(Badge, { children: "active" }) : /* @__PURE__ */ jsx2(
|
|
794
|
+
TextButton,
|
|
795
|
+
{
|
|
796
|
+
onClick: () => controller.request("switchSeat", { seatId: seat.id }),
|
|
797
|
+
children: "Switch"
|
|
798
|
+
}
|
|
799
|
+
)
|
|
800
|
+
},
|
|
801
|
+
seat.id
|
|
802
|
+
)) }) }) : null,
|
|
803
|
+
showEntitlements && state.entitlements.length > 0 ? /* @__PURE__ */ jsx2(Section, { title: "Included", flush: true, children: /* @__PURE__ */ jsx2(RowList, { children: state.entitlements.map((ent) => /* @__PURE__ */ jsx2(
|
|
804
|
+
Row,
|
|
805
|
+
{
|
|
806
|
+
noIcon: true,
|
|
807
|
+
label: ent.label,
|
|
808
|
+
meta: ent.detail,
|
|
809
|
+
trailing: /* @__PURE__ */ jsx2(Badge, { tone: ent.included ? "success" : "neutral", children: ent.included ? "yes" : "no" })
|
|
810
|
+
},
|
|
811
|
+
ent.id
|
|
812
|
+
)) }) }) : null,
|
|
813
|
+
showLedger ? /* @__PURE__ */ jsxs(
|
|
814
|
+
Section,
|
|
815
|
+
{
|
|
816
|
+
title: "Recent activity",
|
|
817
|
+
flush: true,
|
|
818
|
+
collapsed: !state.ledgerExpanded,
|
|
819
|
+
onCollapsedChange: (collapsed) => controller.setLedgerExpanded(!collapsed),
|
|
820
|
+
badge: state.ledger.length > 0 ? /* @__PURE__ */ jsx2(Badge, { children: state.ledger.length }) : void 0,
|
|
821
|
+
children: [
|
|
822
|
+
state.ledger.length === 0 ? /* @__PURE__ */ jsx2(EmptyState, { title: "No activity", hint: "Credit spends and grants appear here." }) : /* @__PURE__ */ jsx2(RowList, { children: state.ledger.map((entry) => /* @__PURE__ */ jsx2(
|
|
823
|
+
Row,
|
|
824
|
+
{
|
|
825
|
+
noIcon: true,
|
|
826
|
+
label: entry.description,
|
|
827
|
+
meta: entry.source,
|
|
828
|
+
trailing: /* @__PURE__ */ jsx2("span", { style: { fontVariantNumeric: "tabular-nums", fontSize: 11 }, children: formatAmount(entry.amount) })
|
|
829
|
+
},
|
|
830
|
+
entry.id
|
|
831
|
+
)) }),
|
|
832
|
+
state.ledgerHasMore ? /* @__PURE__ */ jsx2("div", { style: { padding: "2px 8px", fontSize: 9, opacity: 0.6 }, children: "Older entries are not held by this panel." }) : null
|
|
833
|
+
]
|
|
834
|
+
}
|
|
835
|
+
) : null
|
|
836
|
+
] }),
|
|
837
|
+
/* @__PURE__ */ jsx2("div", { style: { padding: "0 8px", fontSize: 9, opacity: 0.55 }, children: "Billing and sign-out ask you to confirm who you are." }),
|
|
838
|
+
/* @__PURE__ */ jsx2(
|
|
839
|
+
Toolbar,
|
|
840
|
+
{
|
|
841
|
+
divided: false,
|
|
842
|
+
right: /* @__PURE__ */ jsxs(ToolbarGroup, { end: true, children: [
|
|
843
|
+
/* @__PURE__ */ jsx2(TextButton, { onClick: () => controller.request("manageBilling"), children: "Billing\u2026" }),
|
|
844
|
+
/* @__PURE__ */ jsx2(TextButton, { onClick: () => controller.request("signOut"), children: "Sign out\u2026" })
|
|
845
|
+
] })
|
|
846
|
+
}
|
|
847
|
+
),
|
|
848
|
+
/* @__PURE__ */ jsx2(
|
|
849
|
+
StatusBar,
|
|
850
|
+
{
|
|
851
|
+
left: plan?.renewsAt ? `renews ${new Date(plan.renewsAt).toLocaleDateString()}` : void 0,
|
|
852
|
+
right: state.status === "offline" ? "offline \u2014 showing last known" : void 0
|
|
853
|
+
}
|
|
854
|
+
)
|
|
855
|
+
] });
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/auth/account/panel.ts
|
|
859
|
+
function createAccountPanel(options = {}) {
|
|
860
|
+
return {
|
|
861
|
+
manifest: accountManifest,
|
|
862
|
+
activate(host) {
|
|
863
|
+
const config = host.config ?? {};
|
|
864
|
+
const controller = new AccountController({
|
|
865
|
+
host: { emit: (portId, value) => host.emit(portId, value) },
|
|
866
|
+
staleAfterMs: typeof config.staleAfterMs === "number" ? config.staleAfterMs : void 0,
|
|
867
|
+
maxLedger: typeof config.maxLedger === "number" ? config.maxLedger : void 0
|
|
868
|
+
});
|
|
869
|
+
const resolve = (config2) => ({
|
|
870
|
+
showLedger: config2.showLedger !== false,
|
|
871
|
+
showEntitlements: config2.showEntitlements !== false,
|
|
872
|
+
emptyHint: typeof config2.emptyHint === "string" ? config2.emptyHint : void 0
|
|
873
|
+
});
|
|
874
|
+
let renderConfig = resolve(host.config ?? {});
|
|
875
|
+
let unrender = null;
|
|
876
|
+
let root = null;
|
|
877
|
+
let ticker = null;
|
|
878
|
+
let currentEl = null;
|
|
879
|
+
const draw = (el) => {
|
|
880
|
+
unrender?.();
|
|
881
|
+
if (ticker === null) ticker = setInterval(() => controller.tick(), 1e3);
|
|
882
|
+
if (options.render) {
|
|
883
|
+
unrender = options.render(el, { controller, config: renderConfig });
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
root = createRoot3(el);
|
|
887
|
+
root.render(createElement3(AccountPanelView, { controller, ...renderConfig }));
|
|
888
|
+
unrender = () => {
|
|
889
|
+
root?.unmount();
|
|
890
|
+
root = null;
|
|
891
|
+
};
|
|
892
|
+
};
|
|
893
|
+
const unbindConfig = bindConfig2(host, (config2) => {
|
|
894
|
+
renderConfig = resolve(config2);
|
|
895
|
+
if (currentEl) draw(currentEl);
|
|
896
|
+
});
|
|
897
|
+
return {
|
|
898
|
+
render(el) {
|
|
899
|
+
currentEl = el;
|
|
900
|
+
draw(el);
|
|
901
|
+
},
|
|
902
|
+
onInput(portId, value) {
|
|
903
|
+
if (portId === "account") {
|
|
904
|
+
if (value && typeof value === "object" && "status" in value) {
|
|
905
|
+
controller.setAccount(value);
|
|
906
|
+
}
|
|
907
|
+
} else if (portId === "ledger") {
|
|
908
|
+
if (Array.isArray(value)) {
|
|
909
|
+
controller.applyLedger({ append: value });
|
|
910
|
+
} else if (value && typeof value === "object") {
|
|
911
|
+
controller.applyLedger(value);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
async onCommand(commandId) {
|
|
916
|
+
switch (commandId) {
|
|
917
|
+
case "refresh":
|
|
918
|
+
return controller.refresh();
|
|
919
|
+
case "get_account": {
|
|
920
|
+
const s = controller.getState();
|
|
921
|
+
return {
|
|
922
|
+
status: s.status,
|
|
923
|
+
sub: s.identity?.sub,
|
|
924
|
+
plan: s.plan?.id,
|
|
925
|
+
balance: s.credits?.balance,
|
|
926
|
+
asOf: s.credits?.asOf,
|
|
927
|
+
stale: s.creditsStale
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
case "get_ledger":
|
|
931
|
+
return controller.getState().ledger;
|
|
932
|
+
default:
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
serialize() {
|
|
937
|
+
return controller.serialize();
|
|
938
|
+
},
|
|
939
|
+
deserialize(state) {
|
|
940
|
+
controller.deserialize(state);
|
|
941
|
+
},
|
|
942
|
+
dispose() {
|
|
943
|
+
unbindConfig();
|
|
944
|
+
currentEl = null;
|
|
945
|
+
if (ticker !== null) {
|
|
946
|
+
clearInterval(ticker);
|
|
947
|
+
ticker = null;
|
|
948
|
+
}
|
|
949
|
+
unrender?.();
|
|
950
|
+
unrender = null;
|
|
951
|
+
controller.dispose();
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
var accountPanel = createAccountPanel();
|
|
353
958
|
export {
|
|
959
|
+
ACCOUNT_PANEL_ID,
|
|
354
960
|
AUTH_GATE_DECLARATION,
|
|
355
961
|
AUTH_GATE_INITIAL,
|
|
356
962
|
AUTH_GATE_PANEL_ID,
|
|
963
|
+
AccountController,
|
|
964
|
+
AccountPanelView,
|
|
357
965
|
AuthGateController,
|
|
358
966
|
AuthGateView,
|
|
359
967
|
BEHAVIOURS,
|
|
360
968
|
PHASE_INTENTS,
|
|
969
|
+
STEP_UP_ACTIONS,
|
|
970
|
+
accountManifest,
|
|
971
|
+
accountPanel,
|
|
361
972
|
authGateManifest,
|
|
362
973
|
authGatePanel,
|
|
363
974
|
authGateView,
|
|
975
|
+
createAccountPanel,
|
|
364
976
|
createAuthGatePanel,
|
|
977
|
+
creditsAge,
|
|
365
978
|
deriveAuthGatePhase,
|
|
979
|
+
formatAge,
|
|
980
|
+
formatAmount,
|
|
366
981
|
formatSince,
|
|
367
982
|
hostContradiction,
|
|
368
983
|
intentOffered,
|
|
984
|
+
isSignedIn,
|
|
369
985
|
mountXenoAuthGate,
|
|
370
|
-
plateCopy
|
|
986
|
+
plateCopy,
|
|
987
|
+
requiresStepUp
|
|
371
988
|
};
|
package/dist/data/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PanelModule, PanelManifest, LogSink } from '@xenosystem/
|
|
1
|
+
import { PanelModule, PanelManifest, LogSink } from '@xenosystem/block-sdk';
|
|
2
2
|
import * as _xenosystem_data_core from '@xenosystem/data-core';
|
|
3
3
|
import { XenoColumn, XenoQuery, XenoResultSet, XenoValue, XenoRow, XenoAggFn, XenoValueFormat, XenoFilterGroup, XenoColumnRole, XenoSchemaCatalog, XenoFilter, XenoSourceSchema } from '@xenosystem/data-core';
|
|
4
4
|
import { ReactNode } from 'react';
|