@stealthscale/sdk-host 0.1.0 → 0.4.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.
@@ -1,16 +1,17 @@
1
- import { a as HostContent, c as sampledFrom, i as HostNotFound, n as HostRoot, o as createHost, r as HostProvider, s as internalsOf, t as createHostRoutes } from "../create-routes-CkgZV-dU.js";
1
+ import { c as HostProvider, f as onCaughtError, h as sampledFrom, l as HostNotFound, m as internalsOf, p as createHost, t as createHostRoutes } from "../create-routes-D4iCBYZe.js";
2
+ import { t as ProductFrame } from "../frame-HZM1zS7g.js";
2
3
  import { standaloneSources } from "../standalone.js";
3
4
  import { DataError } from "@stealthscale/provider-data";
4
- import { hostContract } from "@stealthscale/sdk-core";
5
- import { Into, Slot, useResolvedProduct, useSlot } from "@stealthscale/sdk-plugin";
5
+ import { NOBODY, hostContract } from "@stealthscale/sdk-core";
6
+ import { Into, useResolvedProduct } from "@stealthscale/sdk-plugin";
6
7
  import { localStore } from "@stealthscale/settings";
7
- import { c } from "react/compiler-runtime";
8
8
  import { RouterProvider, createAppRootRoute, createRouter, routeHref, routeMap, routerOptions, useRouteContext, useRouter, useRouterState } from "@stealthscale/provider-router";
9
- import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { c } from "react/compiler-runtime";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
11
  import { Fragment as Fragment$1, createContext, use, useState, useSyncExternalStore } from "react";
11
12
  import { useTranslation } from "@stealthscale/provider-i18n";
12
13
  import { Stack } from "@stealthscale/component-layout";
13
- import { AppShell, FloatingPanel } from "@stealthscale/component-screen";
14
+ import { FloatingPanel } from "@stealthscale/component-screen";
14
15
  import { Field, Fieldset, NativeSelect, SegmentGroup, Switch } from "@stealthscale/component-forms";
15
16
  import { createRoot } from "react-dom/client";
16
17
  import { Shell, useThemeChoice } from "@stealthscale/provider-shell";
@@ -113,6 +114,34 @@ function standaloneTransport(product, modes) {
113
114
  };
114
115
  }
115
116
  //#endregion
117
+ //#region src/standalone/start.ts
118
+ /**
119
+ * Reads what the standalone page starts from: the address it opens at, and the flags the panel
120
+ * offers.
121
+ */
122
+ /**
123
+ * Returns the address the standalone page opens at: the plugin's first menu entry, else its first
124
+ * route that a sample or an empty path fills. Undefined where the plugin has neither.
125
+ *
126
+ * @param product - The standalone product, whose id is the plugin's.
127
+ * @param routes - The router's map.
128
+ */
129
+ function startOf(product, routes) {
130
+ const filled = product.routes.filter(({ plugin }) => plugin === product.productId).filter(({ path, sample }) => sample !== void 0 || !path.includes("$"));
131
+ const [start] = [...filled.filter(({ navigation }) => navigation !== void 0), ...filled];
132
+ return start === void 0 ? void 0 : routeHref(routes, { id: start.id }, start.sample);
133
+ }
134
+ /**
135
+ * Returns every flag the installed plugins' contracts declare, in install order, with the variants
136
+ * of each experiment.
137
+ */
138
+ function flagsOf(product) {
139
+ return Object.values(product.manifests).flatMap(({ contract }) => Object.values(contract.featureFlags).map(({ id, variants }) => ({
140
+ id,
141
+ variants: variants?.map(String)
142
+ })));
143
+ }
144
+ //#endregion
116
145
  //#region src/standalone/hosted.ts
117
146
  /**
118
147
  * Creates the host of the standalone page.
@@ -128,16 +157,18 @@ function standaloneTransport(product, modes) {
128
157
  function standaloneHosted(product, glyphs, store = localStore()) {
129
158
  const sources = standaloneSources(product);
130
159
  const modes = operationModes();
160
+ const host = createHost({
161
+ access: sources.access,
162
+ data: { transport: standaloneTransport(product, modes) },
163
+ flags: sources.flags,
164
+ glyphs,
165
+ product,
166
+ session: sources.session,
167
+ store
168
+ });
131
169
  return {
132
- host: createHost({
133
- access: sources.access,
134
- data: { transport: standaloneTransport(product, modes) },
135
- flags: sources.flags,
136
- glyphs,
137
- product,
138
- session: sources.session,
139
- store
140
- }),
170
+ flags: flagsOf(product),
171
+ host,
141
172
  modes,
142
173
  sources
143
174
  };
@@ -150,7 +181,7 @@ function standaloneHosted(product, glyphs, store = localStore()) {
150
181
  * @param props - The legend and the controls.
151
182
  * @returns The fieldset.
152
183
  */
153
- function Group(t0) {
184
+ function Group$1(t0) {
154
185
  const $ = c(7);
155
186
  const { children, title } = t0;
156
187
  let t1;
@@ -279,7 +310,7 @@ function AccessControls() {
279
310
  } else t4 = $[12];
280
311
  let t5;
281
312
  if ($[13] !== t0 || $[14] !== t4) {
282
- t5 = /* @__PURE__ */ jsx(Group, {
313
+ t5 = /* @__PURE__ */ jsx(Group$1, {
283
314
  title: t0,
284
315
  children: t4
285
316
  });
@@ -321,7 +352,7 @@ function Picker(t0) {
321
352
  } else t2 = $[3];
322
353
  let t3;
323
354
  if ($[4] !== choices) {
324
- t3 = choices.map(_temp$7);
355
+ t3 = choices.map(_temp$5);
325
356
  $[4] = choices;
326
357
  $[5] = t3;
327
358
  } else t3 = $[5];
@@ -365,7 +396,7 @@ function Picker(t0) {
365
396
  } else t7 = $[17];
366
397
  return t7;
367
398
  }
368
- function _temp$7(choice) {
399
+ function _temp$5(choice) {
369
400
  return /* @__PURE__ */ jsx("option", {
370
401
  value: choice.value,
371
402
  children: choice.label
@@ -392,16 +423,16 @@ const WORDS = {
392
423
  unauthenticated: "standalone.panel.data.modes.unauthenticated"
393
424
  };
394
425
  /**
395
- * Renders one picker per declared query and mutation, named by its qualified id, which sets how the
396
- * page's transport serves its operation.
426
+ * Renders one picker per declared query and mutation, or nothing when the product declares neither.
397
427
  *
398
428
  * @remarks
399
- * A query's pick resets every query of the page's data client and invalidates the router, so a
400
- * page on screen loads again from nothing in the new mode: a refusal shows the page's error, a
429
+ * Each picker is named by the operation's qualified id and sets how the page's transport serves
430
+ * it. A query's pick resets every query of the page's data client and invalidates the router, so
431
+ * a page on screen loads again from nothing in the new mode: a refusal shows the page's error, a
401
432
  * delay its pending state, and a page that failed renders again. An invalidation of the queries
402
433
  * alone would keep the cached data through a refetch that fails or waits. A mutation runs in its
403
434
  * mode the next time a person sends it, and its pick loads nothing again.
404
- * @returns The group.
435
+ * @returns The group, or null when the product declares no query and no mutation.
405
436
  */
406
437
  function DataControls() {
407
438
  const $ = c(22);
@@ -428,27 +459,29 @@ function DataControls() {
428
459
  const choices = t1;
429
460
  let t2;
430
461
  if ($[3] !== queries) {
431
- t2 = new Set(queries.map(_temp$6));
462
+ t2 = new Set(queries.map(_temp$4));
432
463
  $[3] = queries;
433
464
  $[4] = t2;
434
465
  } else t2 = $[4];
435
466
  const loaded = t2;
436
467
  let t3;
437
- if ($[5] !== t) {
438
- t3 = t("standalone.panel.data.title");
439
- $[5] = t;
440
- $[6] = t3;
441
- } else t3 = $[6];
468
+ if ($[5] !== mutations || $[6] !== queries) {
469
+ t3 = [...queries, ...mutations];
470
+ $[5] = mutations;
471
+ $[6] = queries;
472
+ $[7] = t3;
473
+ } else t3 = $[7];
474
+ const operations = t3;
475
+ if (operations.length === 0) return null;
442
476
  let t4;
443
- if ($[7] !== mutations || $[8] !== queries) {
444
- t4 = [...queries, ...mutations];
445
- $[7] = mutations;
446
- $[8] = queries;
477
+ if ($[8] !== t) {
478
+ t4 = t("standalone.panel.data.title");
479
+ $[8] = t;
447
480
  $[9] = t4;
448
481
  } else t4 = $[9];
449
482
  let t5;
450
- if ($[10] !== choices || $[11] !== current || $[12] !== glyphs || $[13] !== host || $[14] !== loaded || $[15] !== modes || $[16] !== router || $[17] !== t4) {
451
- t5 = t4.map((t6) => {
483
+ if ($[10] !== choices || $[11] !== current || $[12] !== glyphs || $[13] !== host || $[14] !== loaded || $[15] !== modes || $[16] !== operations || $[17] !== router) {
484
+ t5 = operations.map((t6) => {
452
485
  const { id, operation: operation_0 } = t6;
453
486
  return /* @__PURE__ */ jsx(Picker, {
454
487
  choices,
@@ -469,23 +502,23 @@ function DataControls() {
469
502
  $[13] = host;
470
503
  $[14] = loaded;
471
504
  $[15] = modes;
472
- $[16] = router;
473
- $[17] = t4;
505
+ $[16] = operations;
506
+ $[17] = router;
474
507
  $[18] = t5;
475
508
  } else t5 = $[18];
476
509
  let t6;
477
- if ($[19] !== t3 || $[20] !== t5) {
478
- t6 = /* @__PURE__ */ jsx(Group, {
479
- title: t3,
510
+ if ($[19] !== t4 || $[20] !== t5) {
511
+ t6 = /* @__PURE__ */ jsx(Group$1, {
512
+ title: t4,
480
513
  children: t5
481
514
  });
482
- $[19] = t3;
515
+ $[19] = t4;
483
516
  $[20] = t5;
484
517
  $[21] = t6;
485
518
  } else t6 = $[21];
486
519
  return t6;
487
520
  }
488
- function _temp$6(t0) {
521
+ function _temp$4(t0) {
489
522
  const { operation } = t0;
490
523
  return operation.id;
491
524
  }
@@ -495,46 +528,49 @@ function _temp$6(t0) {
495
528
  * Renders a small switch that spreads across the panel, its words at the start and its control at
496
529
  * the end.
497
530
  *
498
- * @param props - The state, the words and the callback a press calls.
531
+ * @param props - The state, whether it takes a press, the words and the callback a press calls.
499
532
  * @returns The switch.
500
533
  */
501
534
  function Toggle(t0) {
502
- const $ = c(9);
503
- const { checked, label, onCheckedChange } = t0;
504
- let t1;
535
+ const $ = c(10);
536
+ const { checked, disabled: t1, label, onCheckedChange } = t0;
537
+ const disabled = t1 === void 0 ? false : t1;
538
+ let t2;
505
539
  if ($[0] !== onCheckedChange) {
506
- t1 = (details) => {
540
+ t2 = (details) => {
507
541
  onCheckedChange(details.checked);
508
542
  };
509
543
  $[0] = onCheckedChange;
510
- $[1] = t1;
511
- } else t1 = $[1];
512
- let t2;
544
+ $[1] = t2;
545
+ } else t2 = $[1];
546
+ let t3;
513
547
  if ($[2] !== label) {
514
- t2 = /* @__PURE__ */ jsx(Switch.Label, { children: label });
548
+ t3 = /* @__PURE__ */ jsx(Switch.Label, { children: label });
515
549
  $[2] = label;
516
- $[3] = t2;
517
- } else t2 = $[3];
518
- let t3;
519
- if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
520
- t3 = /* @__PURE__ */ jsx(Switch.Control, { children: /* @__PURE__ */ jsx(Switch.Thumb, {}) });
521
- $[4] = t3;
522
- } else t3 = $[4];
550
+ $[3] = t3;
551
+ } else t3 = $[3];
523
552
  let t4;
524
- if ($[5] !== checked || $[6] !== t1 || $[7] !== t2) {
525
- t4 = /* @__PURE__ */ jsxs(Switch.Root, {
553
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
554
+ t4 = /* @__PURE__ */ jsx(Switch.Control, { children: /* @__PURE__ */ jsx(Switch.Thumb, {}) });
555
+ $[4] = t4;
556
+ } else t4 = $[4];
557
+ let t5;
558
+ if ($[5] !== checked || $[6] !== disabled || $[7] !== t2 || $[8] !== t3) {
559
+ t5 = /* @__PURE__ */ jsxs(Switch.Root, {
526
560
  checked,
527
- onCheckedChange: t1,
561
+ disabled,
562
+ onCheckedChange: t2,
528
563
  size: "sm",
529
564
  spread: true,
530
- children: [t2, t3]
565
+ children: [t3, t4]
531
566
  });
532
567
  $[5] = checked;
533
- $[6] = t1;
568
+ $[6] = disabled;
534
569
  $[7] = t2;
535
- $[8] = t4;
536
- } else t4 = $[8];
537
- return t4;
570
+ $[8] = t3;
571
+ $[9] = t5;
572
+ } else t5 = $[9];
573
+ return t5;
538
574
  }
539
575
  //#endregion
540
576
  //#region src/standalone/flag-controls.tsx
@@ -543,81 +579,45 @@ function Toggle(t0) {
543
579
  * experiment.
544
580
  */
545
581
  /**
546
- * Renders one control per flag the installed plugins declare, named by its qualified id, which
547
- * overrides the flag in this tab.
582
+ * Renders one control per flag the installed plugins declare, or nothing when they declare none.
548
583
  *
549
584
  * @remarks
550
- * An override replaces the value every condition, menu and `useFeatureFlag` on the page reads, as
551
- * the flag store's overrides do in a product outside production.
552
- * @returns The group.
585
+ * Each control is named by the flag's qualified id and overrides the flag in this tab. An
586
+ * override replaces the value every condition, menu and `useFeatureFlag` on the page reads, as
587
+ * the flag store's overrides do in a product outside production. The React Compiler is disabled
588
+ * for this component. Each control reads its flag from the store, which changes outside React,
589
+ * and a memoized render would keep the controls of an earlier change.
590
+ * @returns The group, or null when no plugin declares a flag.
553
591
  */
554
592
  function FlagControls() {
555
- const $ = c(13);
593
+ "use no memo";
556
594
  const { flags: declared, glyphs } = useStandalone();
557
- let t0;
558
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
559
- t0 = { strict: false };
560
- $[0] = t0;
561
- } else t0 = $[0];
562
- const { host } = useRouteContext(t0);
595
+ const { host } = useRouteContext({ strict: false });
563
596
  const { flags } = host.stores;
564
597
  const { t } = useTranslation("host");
565
598
  useSyncExternalStore(flags.subscribe, flags.get, flags.get);
566
- let t1;
567
- if ($[1] !== t) {
568
- t1 = t("standalone.panel.flags.title");
569
- $[1] = t;
570
- $[2] = t1;
571
- } else t1 = $[2];
572
- let t2;
573
- if ($[3] !== declared || $[4] !== flags || $[5] !== glyphs) {
574
- let t3;
575
- if ($[7] !== flags || $[8] !== glyphs) {
576
- t3 = (t4) => {
577
- const { id, variants } = t4;
578
- return variants === void 0 ? /* @__PURE__ */ jsx(Toggle, {
579
- checked: flags.read(id) === true,
580
- label: id,
581
- onCheckedChange: (on) => {
582
- flags.override(id, on);
583
- }
584
- }, id) : /* @__PURE__ */ jsx(Picker, {
585
- choices: variants.map(_temp$5),
586
- indicator: glyphs.select?.indicator,
587
- label: id,
588
- onValueChange: (variant_0) => {
589
- flags.override(id, variant_0);
590
- },
591
- value: String(flags.read(id))
592
- }, id);
593
- };
594
- $[7] = flags;
595
- $[8] = glyphs;
596
- $[9] = t3;
597
- } else t3 = $[9];
598
- t2 = declared.map(t3);
599
- $[3] = declared;
600
- $[4] = flags;
601
- $[5] = glyphs;
602
- $[6] = t2;
603
- } else t2 = $[6];
604
- let t3;
605
- if ($[10] !== t1 || $[11] !== t2) {
606
- t3 = /* @__PURE__ */ jsx(Group, {
607
- title: t1,
608
- children: t2
609
- });
610
- $[10] = t1;
611
- $[11] = t2;
612
- $[12] = t3;
613
- } else t3 = $[12];
614
- return t3;
615
- }
616
- function _temp$5(variant) {
617
- return {
618
- label: variant,
619
- value: variant
620
- };
599
+ if (declared.length === 0) return null;
600
+ return /* @__PURE__ */ jsx(Group$1, {
601
+ title: t("standalone.panel.flags.title"),
602
+ children: declared.map(({ id, variants }) => variants === void 0 ? /* @__PURE__ */ jsx(Toggle, {
603
+ checked: flags.read(id) === true,
604
+ label: id,
605
+ onCheckedChange: (on) => {
606
+ flags.override(id, on);
607
+ }
608
+ }, id) : /* @__PURE__ */ jsx(Picker, {
609
+ choices: variants.map((variant) => ({
610
+ label: variant,
611
+ value: variant
612
+ })),
613
+ indicator: glyphs.select?.indicator,
614
+ label: id,
615
+ onValueChange: (variant) => {
616
+ flags.override(id, variant);
617
+ },
618
+ value: String(flags.read(id))
619
+ }, id))
620
+ });
621
621
  }
622
622
  //#endregion
623
623
  //#region src/standalone/page-controls.tsx
@@ -636,7 +636,7 @@ function PageControls() {
636
636
  const router = useRouter();
637
637
  let t0;
638
638
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
639
- t0 = { select: _temp$4 };
639
+ t0 = { select: _temp$3 };
640
640
  $[0] = t0;
641
641
  } else t0 = $[0];
642
642
  const pathname = useRouterState(t0);
@@ -713,7 +713,7 @@ function PageControls() {
713
713
  } else t6 = $[18];
714
714
  return t6;
715
715
  }
716
- function _temp$4(state) {
716
+ function _temp$3(state) {
717
717
  return state.location.pathname;
718
718
  }
719
719
  //#endregion
@@ -727,67 +727,36 @@ function _temp$4(state) {
727
727
  * @remarks
728
728
  * The standalone product's id is the plugin's. The first switch writes the person's switch, as
729
729
  * the Plugins page does. The second overrides the kill switch in this tab, so the plugin's pages
730
- * turn not found and its contributions leave the page.
730
+ * turn not found and its contributions leave the page. The React Compiler is disabled for this
731
+ * component. The kill switch's control reads the flag store, which changes outside React, and a
732
+ * memoized render would keep the control of an earlier change.
731
733
  * @returns The group.
732
734
  */
733
735
  function PluginControls() {
734
- const $ = c(9);
735
- let t0;
736
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
737
- t0 = { strict: false };
738
- $[0] = t0;
739
- } else t0 = $[0];
740
- const { host } = useRouteContext(t0);
736
+ "use no memo";
737
+ const { host } = useRouteContext({ strict: false });
741
738
  const { switches } = internalsOf(host);
742
739
  const { flags } = host.stores;
743
740
  const { t } = useTranslation("host");
744
741
  const { plugins, productId } = useResolvedProduct();
745
742
  const switched = useSyncExternalStore(switches.subscribe, switches.get, switches.get);
746
743
  useSyncExternalStore(flags.subscribe, flags.get, flags.get);
747
- const T0 = Group;
748
- let t1;
749
- if ($[1] !== t) {
750
- t1 = t("standalone.panel.plugin.title");
751
- $[1] = t;
752
- $[2] = t1;
753
- } else t1 = $[2];
754
- let t2;
755
- if ($[3] !== productId) {
756
- t2 = (t3) => {
757
- const { id } = t3;
758
- return id === productId;
759
- };
760
- $[3] = productId;
761
- $[4] = t2;
762
- } else t2 = $[4];
763
- const t4 = plugins.filter(t2).map((t3) => {
764
- const { id: id_0, killSwitch } = t3;
765
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Toggle, {
766
- checked: switched[id_0] !== false,
744
+ return /* @__PURE__ */ jsx(Group$1, {
745
+ title: t("standalone.panel.plugin.title"),
746
+ children: plugins.filter(({ id }) => id === productId).map(({ id, killSwitch }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Toggle, {
747
+ checked: switched[id] !== false,
767
748
  label: t("standalone.panel.plugin.switch"),
768
749
  onCheckedChange: (on) => {
769
- switches.set(id_0, on);
750
+ switches.set(id, on);
770
751
  }
771
752
  }), /* @__PURE__ */ jsx(Toggle, {
772
753
  checked: flags.read(killSwitch) !== false,
773
754
  label: t("standalone.panel.plugin.kill"),
774
- onCheckedChange: (on_0) => {
775
- flags.override(killSwitch, on_0);
755
+ onCheckedChange: (on) => {
756
+ flags.override(killSwitch, on);
776
757
  }
777
- })] }, id_0);
758
+ })] }, id))
778
759
  });
779
- let t5;
780
- if ($[5] !== T0 || $[6] !== t1 || $[7] !== t4) {
781
- t5 = /* @__PURE__ */ jsx(T0, {
782
- title: t1,
783
- children: t4
784
- });
785
- $[5] = T0;
786
- $[6] = t1;
787
- $[7] = t4;
788
- $[8] = t5;
789
- } else t5 = $[8];
790
- return t5;
791
760
  }
792
761
  //#endregion
793
762
  //#region src/standalone/session-controls.tsx
@@ -810,16 +779,19 @@ function grantedIn(session, list, id, on) {
810
779
  * its qualified id.
811
780
  *
812
781
  * @remarks
813
- * Each switch replaces the session, so every condition, menu and decision on the page follows at
814
- * once.
782
+ * Each switch replaces the session. Every condition, menu and decision on the page reads the new
783
+ * session at once. Signing out writes `NOBODY`, whose permission and entitlement lists are empty.
784
+ * Signing in again restores the session the switches showed before. The permission and
785
+ * entitlement switches take no press while nobody is signed in.
815
786
  * @returns The group.
816
787
  */
817
788
  function SessionControls() {
818
- const $ = c(20);
789
+ const $ = c(21);
819
790
  const { session } = useStandalone();
820
791
  const { t } = useTranslation("host");
821
792
  const { entitlements, permissions } = useResolvedProduct();
822
793
  const current = useSyncExternalStore(session.subscribe, session.read, session.read);
794
+ const [signedIn, keep] = useState(current);
823
795
  let t0;
824
796
  if ($[0] !== t) {
825
797
  t0 = t("standalone.panel.session.title");
@@ -834,65 +806,68 @@ function SessionControls() {
834
806
  $[3] = t2;
835
807
  } else t2 = $[3];
836
808
  let t3;
837
- if ($[4] !== current || $[5] !== session) {
809
+ if ($[4] !== current || $[5] !== session || $[6] !== signedIn) {
838
810
  t3 = (authenticated) => {
839
- session.set({
840
- ...current,
811
+ if (!authenticated) keep(current);
812
+ session.set(authenticated ? {
813
+ ...signedIn,
841
814
  authenticated
842
- });
815
+ } : NOBODY);
843
816
  };
844
817
  $[4] = current;
845
818
  $[5] = session;
846
- $[6] = t3;
847
- } else t3 = $[6];
819
+ $[6] = signedIn;
820
+ $[7] = t3;
821
+ } else t3 = $[7];
848
822
  let t4;
849
- if ($[7] !== current.authenticated || $[8] !== t2 || $[9] !== t3) {
823
+ if ($[8] !== current.authenticated || $[9] !== t2 || $[10] !== t3) {
850
824
  t4 = /* @__PURE__ */ jsx(Toggle, {
851
825
  checked: t1,
852
826
  label: t2,
853
827
  onCheckedChange: t3
854
828
  });
855
- $[7] = current.authenticated;
856
- $[8] = t2;
857
- $[9] = t3;
858
- $[10] = t4;
859
- } else t4 = $[10];
829
+ $[8] = current.authenticated;
830
+ $[9] = t2;
831
+ $[10] = t3;
832
+ $[11] = t4;
833
+ } else t4 = $[11];
860
834
  let t5;
861
- if ($[11] !== current || $[12] !== entitlements || $[13] !== permissions || $[14] !== session) {
862
- t5 = [...permissions.map(_temp$3), ...entitlements.map(_temp2$1)].map((t6) => {
835
+ if ($[12] !== current || $[13] !== entitlements || $[14] !== permissions || $[15] !== session) {
836
+ t5 = [...permissions.map(_temp$2), ...entitlements.map(_temp2$1)].map((t6) => {
863
837
  const [list, id_1] = t6;
864
838
  return /* @__PURE__ */ jsx(Toggle, {
865
839
  checked: current[list].includes(id_1),
840
+ disabled: !current.authenticated,
866
841
  label: id_1,
867
842
  onCheckedChange: (on) => {
868
843
  session.set(grantedIn(current, list, id_1, on));
869
844
  }
870
845
  }, id_1);
871
846
  });
872
- $[11] = current;
873
- $[12] = entitlements;
874
- $[13] = permissions;
875
- $[14] = session;
876
- $[15] = t5;
877
- } else t5 = $[15];
847
+ $[12] = current;
848
+ $[13] = entitlements;
849
+ $[14] = permissions;
850
+ $[15] = session;
851
+ $[16] = t5;
852
+ } else t5 = $[16];
878
853
  let t6;
879
- if ($[16] !== t0 || $[17] !== t4 || $[18] !== t5) {
880
- t6 = /* @__PURE__ */ jsxs(Group, {
854
+ if ($[17] !== t0 || $[18] !== t4 || $[19] !== t5) {
855
+ t6 = /* @__PURE__ */ jsxs(Group$1, {
881
856
  title: t0,
882
857
  children: [t4, t5]
883
858
  });
884
- $[16] = t0;
885
- $[17] = t4;
886
- $[18] = t5;
887
- $[19] = t6;
888
- } else t6 = $[19];
859
+ $[17] = t0;
860
+ $[18] = t4;
861
+ $[19] = t5;
862
+ $[20] = t6;
863
+ } else t6 = $[20];
889
864
  return t6;
890
865
  }
891
866
  function _temp2$1(t0) {
892
867
  const { id: id_0 } = t0;
893
868
  return ["entitlements", id_0];
894
869
  }
895
- function _temp$3(t0) {
870
+ function _temp$2(t0) {
896
871
  const { id } = t0;
897
872
  return ["permissions", id];
898
873
  }
@@ -940,7 +915,7 @@ function ShellControls() {
940
915
  let t1;
941
916
  if ($[2] !== glyphs || $[3] !== locale || $[4] !== locales || $[5] !== setLocale || $[6] !== t) {
942
917
  t1 = locales.length > 1 ? /* @__PURE__ */ jsx(Picker, {
943
- choices: locales.map(_temp$2),
918
+ choices: locales.map(_temp$1),
944
919
  indicator: glyphs.select?.indicator,
945
920
  label: t("standalone.panel.shell.language"),
946
921
  onValueChange: setLocale,
@@ -1013,7 +988,7 @@ function ShellControls() {
1013
988
  } else t6 = $[24];
1014
989
  let t7;
1015
990
  if ($[25] !== t0 || $[26] !== t1 || $[27] !== t2 || $[28] !== t6) {
1016
- t7 = /* @__PURE__ */ jsxs(Group, {
991
+ t7 = /* @__PURE__ */ jsxs(Group$1, {
1017
992
  title: t0,
1018
993
  children: [
1019
994
  t1,
@@ -1035,7 +1010,7 @@ function _temp2(one_0) {
1035
1010
  value: one_0
1036
1011
  };
1037
1012
  }
1038
- function _temp$2(one) {
1013
+ function _temp$1(one) {
1039
1014
  return {
1040
1015
  label: one,
1041
1016
  value: one
@@ -1116,7 +1091,7 @@ function StandalonePanel() {
1116
1091
  const $ = c(22);
1117
1092
  const { glyphs } = useStandalone();
1118
1093
  const { t } = useTranslation("host");
1119
- const [t0] = useState(_temp$1);
1094
+ const [t0] = useState(_temp);
1120
1095
  const { position, size } = t0;
1121
1096
  let t1;
1122
1097
  if ($[0] !== t) {
@@ -1198,143 +1173,36 @@ function StandalonePanel() {
1198
1173
  } else t10 = $[21];
1199
1174
  return t10;
1200
1175
  }
1201
- function _temp$1() {
1176
+ function _temp() {
1202
1177
  return placementOf(window.innerWidth, window.innerHeight);
1203
1178
  }
1204
1179
  //#endregion
1205
1180
  //#region src/standalone/frame.tsx
1206
1181
  /**
1207
- * Renders the shell with a bar, a column and a band for each region a contribution fills, the page
1208
- * in the main region, and the panel through `Into`.
1182
+ * Renders the frame a product renders, and the development panel through `Into`.
1209
1183
  *
1210
1184
  * @remarks
1211
- * A region nobody fills renders no part of the shell, so an empty aside takes no column, as a
1212
- * product's frame leaves it out.
1213
- * @returns The host's root around the shell.
1185
+ * A standalone product states no frame options, so the page shows the plugin in the `sidebar`
1186
+ * layout with every region the installed plugins fill, as a product that states none shows it.
1187
+ * The panel renders in the `overlay` region, after the shell.
1188
+ * @returns The product's frame and the panel's contribution.
1214
1189
  */
1215
1190
  function StandaloneFrame() {
1216
- const $ = c(22);
1217
- const { slots } = hostContract;
1218
- const header = [
1219
- useSlot(slots.brand),
1220
- useSlot(slots.header),
1221
- useSlot(slots.userMenu)
1222
- ].some(_temp);
1223
- const navigation = useSlot(slots.navigation).filled;
1224
- const aside = useSlot(slots.aside).filled;
1225
- const footer = useSlot(slots.footer).filled;
1226
- const status = useSlot(slots.status).filled;
1191
+ const $ = c(2);
1227
1192
  let t0;
1228
- if ($[0] !== header) {
1229
- t0 = header ? /* @__PURE__ */ jsxs(AppShell.Header, { children: [
1230
- /* @__PURE__ */ jsx(Slot, { slot: slots.brand }),
1231
- /* @__PURE__ */ jsx(Slot, { slot: slots.header }),
1232
- /* @__PURE__ */ jsx(Slot, { slot: slots.userMenu })
1233
- ] }) : null;
1234
- $[0] = header;
1235
- $[1] = t0;
1236
- } else t0 = $[1];
1193
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
1194
+ t0 = /* @__PURE__ */ jsx(ProductFrame, {});
1195
+ $[0] = t0;
1196
+ } else t0 = $[0];
1237
1197
  let t1;
1238
- if ($[2] !== navigation) {
1239
- t1 = navigation ? /* @__PURE__ */ jsx(AppShell.Navbar, { children: /* @__PURE__ */ jsx(Slot, { slot: slots.navigation }) }) : null;
1240
- $[2] = navigation;
1241
- $[3] = t1;
1242
- } else t1 = $[3];
1243
- let t2;
1244
- if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
1245
- t2 = /* @__PURE__ */ jsx(AppShell.Main, { children: /* @__PURE__ */ jsx(HostContent, {}) });
1246
- $[4] = t2;
1247
- } else t2 = $[4];
1248
- let t3;
1249
- if ($[5] !== aside) {
1250
- t3 = aside ? /* @__PURE__ */ jsx(AppShell.Aside, { children: /* @__PURE__ */ jsx(Slot, { slot: slots.aside }) }) : null;
1251
- $[5] = aside;
1252
- $[6] = t3;
1253
- } else t3 = $[6];
1254
- let t4;
1255
- if ($[7] !== t1 || $[8] !== t3) {
1256
- t4 = /* @__PURE__ */ jsxs(AppShell.Body, { children: [
1257
- t1,
1258
- t2,
1259
- t3
1260
- ] });
1261
- $[7] = t1;
1262
- $[8] = t3;
1263
- $[9] = t4;
1264
- } else t4 = $[9];
1265
- let t5;
1266
- if ($[10] !== footer) {
1267
- t5 = footer ? /* @__PURE__ */ jsx(AppShell.Footer, { children: /* @__PURE__ */ jsx(Slot, { slot: slots.footer }) }) : null;
1268
- $[10] = footer;
1269
- $[11] = t5;
1270
- } else t5 = $[11];
1271
- let t6;
1272
- if ($[12] !== status) {
1273
- t6 = status ? /* @__PURE__ */ jsx(AppShell.Status, { children: /* @__PURE__ */ jsx(Slot, { slot: slots.status }) }) : null;
1274
- $[12] = status;
1275
- $[13] = t6;
1276
- } else t6 = $[13];
1277
- let t7;
1278
- if ($[14] !== t0 || $[15] !== t4 || $[16] !== t5 || $[17] !== t6) {
1279
- t7 = /* @__PURE__ */ jsxs(AppShell.Root, { children: [
1280
- t0,
1281
- t4,
1282
- t5,
1283
- t6
1284
- ] });
1285
- $[14] = t0;
1286
- $[15] = t4;
1287
- $[16] = t5;
1288
- $[17] = t6;
1289
- $[18] = t7;
1290
- } else t7 = $[18];
1291
- let t8;
1292
- if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
1293
- t8 = /* @__PURE__ */ jsx(Into, {
1294
- slot: slots.overlay,
1198
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
1199
+ t1 = /* @__PURE__ */ jsxs(Fragment, { children: [t0, /* @__PURE__ */ jsx(Into, {
1200
+ slot: hostContract.slots.overlay,
1295
1201
  children: /* @__PURE__ */ jsx(StandalonePanel, {})
1296
- });
1297
- $[19] = t8;
1298
- } else t8 = $[19];
1299
- let t9;
1300
- if ($[20] !== t7) {
1301
- t9 = /* @__PURE__ */ jsxs(HostRoot, { children: [t7, t8] });
1302
- $[20] = t7;
1303
- $[21] = t9;
1304
- } else t9 = $[21];
1305
- return t9;
1306
- }
1307
- function _temp(t0) {
1308
- const { filled } = t0;
1309
- return filled;
1310
- }
1311
- //#endregion
1312
- //#region src/standalone/start.ts
1313
- /**
1314
- * Reads what the standalone page starts from: the address it opens at, and the flags the panel
1315
- * offers.
1316
- */
1317
- /**
1318
- * Returns the address the standalone page opens at: the plugin's first menu entry, else its first
1319
- * route that a sample or an empty path fills. Undefined where the plugin has neither.
1320
- *
1321
- * @param product - The standalone product, whose id is the plugin's.
1322
- * @param routes - The router's map.
1323
- */
1324
- function startOf(product, routes) {
1325
- const filled = product.routes.filter(({ plugin }) => plugin === product.productId).filter(({ path, sample }) => sample !== void 0 || !path.includes("$"));
1326
- const [start] = [...filled.filter(({ navigation }) => navigation !== void 0), ...filled];
1327
- return start === void 0 ? void 0 : routeHref(routes, { id: start.id }, start.sample);
1328
- }
1329
- /**
1330
- * Returns every flag the installed plugins' contracts declare, in install order, with the variants
1331
- * of each experiment.
1332
- */
1333
- function flagsOf(product) {
1334
- return Object.values(product.manifests).flatMap(({ contract }) => Object.values(contract.featureFlags).map(({ id, variants }) => ({
1335
- id,
1336
- variants: variants?.map(String)
1337
- })));
1202
+ })] });
1203
+ $[1] = t1;
1204
+ } else t1 = $[1];
1205
+ return t1;
1338
1206
  }
1339
1207
  //#endregion
1340
1208
  //#region src/standalone/router.ts
@@ -1387,6 +1255,9 @@ function standaloneRouter(host, product) {
1387
1255
  /**
1388
1256
  * Renders the standalone page once the host is ready.
1389
1257
  *
1258
+ * @remarks
1259
+ * The root takes the host's `onCaughtError`, so a page that throws the router's not-found writes
1260
+ * nothing to the console.
1390
1261
  * @param options - The product, the catalogues, the glyphs, the locales, the themes and the
1391
1262
  * element.
1392
1263
  * @returns The React root the page renders in, which `unmount` takes down.
@@ -1394,18 +1265,18 @@ function standaloneRouter(host, product) {
1394
1265
  */
1395
1266
  async function renderStandalone({ catalogues, element = document.querySelector("#root"), glyphs = {}, locales, product, themes }) {
1396
1267
  if (element === null) throw new Error("The standalone page renders into the element with the id root, and the document has none.");
1397
- const { host, modes, sources } = standaloneHosted(product, glyphs);
1268
+ const { flags, host, modes, sources } = standaloneHosted(product, glyphs);
1398
1269
  const { router, routes } = standaloneRouter(host, product);
1399
1270
  const state = {
1400
1271
  access: sources.access,
1401
- flags: flagsOf(product),
1272
+ flags,
1402
1273
  glyphs,
1403
1274
  modes,
1404
1275
  routes,
1405
1276
  session: sources.session
1406
1277
  };
1407
1278
  await host.ready();
1408
- const root = createRoot(element);
1279
+ const root = createRoot(element, { onCaughtError });
1409
1280
  root.render(/* @__PURE__ */ jsx(StandaloneContext, {
1410
1281
  value: state,
1411
1282
  children: /* @__PURE__ */ jsx(Shell, {