@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.
- package/README.md +274 -59
- package/dist/app.d.ts +79 -0
- package/dist/app.js +3 -0
- package/dist/{create-routes-CkgZV-dU.js → create-routes-D4iCBYZe.js} +1244 -468
- package/dist/cyclonedx/bom.json +1 -1
- package/dist/deploy.d.ts +98 -0
- package/dist/deploy.js +254 -0
- package/dist/document-CeEtZo2E.js +38 -0
- package/dist/frame-HZM1zS7g.js +2500 -0
- package/dist/index.d.ts +36 -97
- package/dist/index.js +2 -2
- package/dist/options-DBsArZKT.d.ts +91 -0
- package/dist/{page-7ScZC_LI.js → page-DzeycJUh.js} +2 -2
- package/dist/releases.d.ts +131 -0
- package/dist/releases.js +404 -0
- package/dist/render-CEa8Jhmo.js +238 -0
- package/dist/render-DRndY-TD.d.ts +54 -0
- package/dist/standalone/app.d.ts +3 -0
- package/dist/standalone/app.js +214 -343
- package/dist/standalone.js +1 -1
- package/locales/en/host.json +46 -0
- package/package.json +60 -49
|
@@ -2,16 +2,17 @@ import { n as translatorOf, r as useProductName, t as pluginWordsOf } from "./wo
|
|
|
2
2
|
import { DataProvider, createDataClient, defineMutation, defineQuery, findRecords, mutateOperation, operationQuery, resetData } from "@stealthscale/provider-data";
|
|
3
3
|
import { HOST, NOBODY, evaluateWhen, hostContract, pluginOf, subjectOf } from "@stealthscale/sdk-core";
|
|
4
4
|
import { sampledTransport } from "@stealthscale/provider-data/testing";
|
|
5
|
-
import { Boundary, HostContext, PluginProvider, RouteDecorations, Slot, changesOf, conditionContextOf, decisionKey, decisionOf, isCommandEnabled, lazyOf, useCommands, useDocumentTitle, useNavigation, usePluginStatuses, useResolvedProduct, useSettings, useToaster } from "@stealthscale/sdk-plugin";
|
|
6
|
-
import { Alert, EmptyState, Toast } from "@stealthscale/component-feedback";
|
|
5
|
+
import { Boundary, HostContext, PluginImportError, PluginProvider, RouteDecorations, Slot, asServerSnapshot, changesOf, conditionContextOf, decisionKey, decisionOf, inServerSnapshot, isCommandEnabled, lazyOf, useCommands, useDocumentTitle, useHydrating, useNavigation, usePluginStatuses, useResolvedProduct, useSettings, useToaster } from "@stealthscale/sdk-plugin";
|
|
6
|
+
import { Alert, EmptyState, Spinner, Toast } from "@stealthscale/component-feedback";
|
|
7
7
|
import { settingKey } from "@stealthscale/settings";
|
|
8
|
+
import { Navigate, Outlet, RouterContextProvider, compileRoutes, createLink, declaredOf, isNotFound, notFound, redirect, routeHref, useDeclaredRoute, useNavigate, useRouteContext, useRouteMap, useRouter } from "@stealthscale/provider-router";
|
|
8
9
|
import { c } from "react/compiler-runtime";
|
|
9
|
-
import { Navigate, Outlet, RouterContextProvider, compileRoutes, createLink, declaredOf, notFound, redirect, routeHref, useDeclaredRoute, useNavigate, useRouteContext, useRouter } from "@stealthscale/provider-router";
|
|
10
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
-
import { Component, Suspense, createElement, lazy, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
12
|
-
import { Button } from "@stealthscale/component-actions";
|
|
11
|
+
import { Component, Suspense, createContext, createElement, lazy, use, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
13
12
|
import { useTranslation } from "@stealthscale/provider-i18n";
|
|
13
|
+
import { Button } from "@stealthscale/component-actions";
|
|
14
14
|
import { normalizeHotkey, useHotkey, useHotkeys } from "@stealthscale/provider-hotkeys";
|
|
15
|
+
import { SearchIcon, XIcon } from "lucide-react";
|
|
15
16
|
import { Command, Dialog } from "@stealthscale/component-modals";
|
|
16
17
|
import { loadNeeds } from "@stealthscale/provider-data/router";
|
|
17
18
|
import { Stack } from "@stealthscale/component-layout";
|
|
@@ -59,7 +60,7 @@ function createEventBus({ events, report }) {
|
|
|
59
60
|
if (event.sticky) kept.set(eventId, payload);
|
|
60
61
|
depth += 1;
|
|
61
62
|
try {
|
|
62
|
-
target.dispatchEvent(new CustomEvent(eventId, { detail: payload }));
|
|
63
|
+
target.dispatchEvent(new CustomEvent(eventId, { detail: [payload] }));
|
|
63
64
|
} finally {
|
|
64
65
|
depth -= 1;
|
|
65
66
|
}
|
|
@@ -84,7 +85,7 @@ function createEventBus({ events, report }) {
|
|
|
84
85
|
* Receives the event on the target.
|
|
85
86
|
*/
|
|
86
87
|
const listener = (event) => {
|
|
87
|
-
deliver(event.detail);
|
|
88
|
+
deliver(event.detail[0]);
|
|
88
89
|
};
|
|
89
90
|
target.addEventListener(eventId, listener);
|
|
90
91
|
if (kept.has(eventId)) deliver(kept.get(eventId));
|
|
@@ -444,26 +445,38 @@ function createPageStore() {
|
|
|
444
445
|
//#endregion
|
|
445
446
|
//#region src/stores/quarantine.ts
|
|
446
447
|
/**
|
|
448
|
+
* Counts the renders of each route and extension that throw in a row, and quarantines a target
|
|
449
|
+
* that fails too many.
|
|
450
|
+
*/
|
|
451
|
+
/**
|
|
447
452
|
* Returns the quarantine store, with no target quarantined.
|
|
448
453
|
*
|
|
449
454
|
* @remarks
|
|
450
455
|
* The count is per render, not per second, so a target rendered sixty times a minute and one
|
|
451
456
|
* rendered once an hour follow one rule. A render that commits returns its target's count to
|
|
452
|
-
* zero.
|
|
453
|
-
*
|
|
457
|
+
* zero. The store reports and counts an error once until a render of its target commits or a
|
|
458
|
+
* retry. The router's error boundary resets on every update of its match, and a boundary that
|
|
459
|
+
* resets renders its fallback again with the error it caught. A quarantine lasts until `retry`
|
|
460
|
+
* or a reload: no store keeps it, because a reload may load a release that fixed the fault. A
|
|
461
|
+
* `PluginImportError` is reported and does not count, because a module that failed to import
|
|
462
|
+
* renders only after a reload: Chromium keeps the failed import for the document's life.
|
|
454
463
|
*/
|
|
455
464
|
function createQuarantineStore({ after, report }) {
|
|
456
465
|
const failures = /* @__PURE__ */ new Map();
|
|
466
|
+
const last = /* @__PURE__ */ new Map();
|
|
457
467
|
const state = writable(/* @__PURE__ */ new Map());
|
|
458
468
|
return {
|
|
459
469
|
failed: (target, error) => {
|
|
460
|
-
|
|
461
|
-
|
|
470
|
+
if (last.has(target) && last.get(target) === error) return;
|
|
471
|
+
last.set(target, error);
|
|
462
472
|
report({
|
|
463
473
|
error,
|
|
464
474
|
kind: "render-failed",
|
|
465
475
|
target
|
|
466
476
|
});
|
|
477
|
+
if (error instanceof PluginImportError) return;
|
|
478
|
+
const count = (failures.get(target) ?? 0) + 1;
|
|
479
|
+
failures.set(target, count);
|
|
467
480
|
if (count < after || state.get().has(target)) return;
|
|
468
481
|
state.set(new Map([...state.get(), [target, {
|
|
469
482
|
error,
|
|
@@ -478,9 +491,11 @@ function createQuarantineStore({ after, report }) {
|
|
|
478
491
|
get: state.get,
|
|
479
492
|
rendered: (target) => {
|
|
480
493
|
failures.delete(target);
|
|
494
|
+
last.delete(target);
|
|
481
495
|
},
|
|
482
496
|
retry: (target) => {
|
|
483
497
|
failures.delete(target);
|
|
498
|
+
last.delete(target);
|
|
484
499
|
if (!state.get().has(target)) return;
|
|
485
500
|
state.set(new Map([...state.get()].filter(([quarantined]) => quarantined !== target)));
|
|
486
501
|
},
|
|
@@ -505,7 +520,8 @@ function createReportStore() {
|
|
|
505
520
|
//#endregion
|
|
506
521
|
//#region src/host/page.ts
|
|
507
522
|
/**
|
|
508
|
-
* Failed renders in a row after which a target is quarantined, where the product
|
|
523
|
+
* Failed renders in a row after which a target is quarantined, where the product does not set a
|
|
524
|
+
* number.
|
|
509
525
|
*/
|
|
510
526
|
const QUARANTINE_AFTER = 3;
|
|
511
527
|
/**
|
|
@@ -520,9 +536,10 @@ function toConsole(entry) {
|
|
|
520
536
|
else console.warn(`${PREFIX} ${entry.kind}`, entry);
|
|
521
537
|
}
|
|
522
538
|
/**
|
|
523
|
-
* Returns the page's stores
|
|
539
|
+
* Returns the page's stores and the function that reports an entry into them, with each refused
|
|
540
|
+
* release reported.
|
|
524
541
|
*/
|
|
525
|
-
function createPage({ quarantineAfter = QUARANTINE_AFTER, report: receiver = toConsole }) {
|
|
542
|
+
function createPage({ quarantineAfter = QUARANTINE_AFTER, refused = [], report: receiver = toConsole }) {
|
|
526
543
|
const reports = createReportStore();
|
|
527
544
|
/**
|
|
528
545
|
* Adds an entry to the reports store and passes it to the receiver.
|
|
@@ -531,7 +548,9 @@ function createPage({ quarantineAfter = QUARANTINE_AFTER, report: receiver = toC
|
|
|
531
548
|
reports.add(entry);
|
|
532
549
|
receiver(entry);
|
|
533
550
|
};
|
|
551
|
+
for (const entry of refused) report(entry);
|
|
534
552
|
return {
|
|
553
|
+
loading: writable(false),
|
|
535
554
|
report,
|
|
536
555
|
stores: {
|
|
537
556
|
mounted: createMountedStore(),
|
|
@@ -555,6 +574,9 @@ function valuesOf(record) {
|
|
|
555
574
|
/**
|
|
556
575
|
* Returns every importer a plugin's code lists: its pages, extensions, commands and sections, and
|
|
557
576
|
* their fallbacks.
|
|
577
|
+
*
|
|
578
|
+
* @remarks
|
|
579
|
+
* The page index is left out, because the palette imports it the first time it opens.
|
|
558
580
|
*/
|
|
559
581
|
function importersOf(code) {
|
|
560
582
|
return [
|
|
@@ -756,8 +778,8 @@ function hostApiOf({ connection, data, events, product, stores, toaster }) {
|
|
|
756
778
|
return {
|
|
757
779
|
can: (permission, resourceId) => allowed(stores, product, permission.id, resourceId),
|
|
758
780
|
data: hostData,
|
|
759
|
-
emit: (event, payload) => {
|
|
760
|
-
events.emit(pluginId, event.id, payload);
|
|
781
|
+
emit: (event, ...payload) => {
|
|
782
|
+
events.emit(pluginId, event.id, payload[0]);
|
|
761
783
|
},
|
|
762
784
|
flag: (flag) => stores.flags.read(flag.id) ?? flag.default ?? false,
|
|
763
785
|
matched: connected?.matched().at(-1),
|
|
@@ -772,15 +794,15 @@ function hostApiOf({ connection, data, events, product, stores, toaster }) {
|
|
|
772
794
|
//#endregion
|
|
773
795
|
//#region src/recovery/stale.ts
|
|
774
796
|
/**
|
|
775
|
-
* Records
|
|
797
|
+
* Records a value under the key in the tab's session storage, and returns true where it did.
|
|
776
798
|
*
|
|
777
|
-
* @returns False where the key contains the
|
|
799
|
+
* @returns False where the key contains the value already, or where the browser refuses the
|
|
778
800
|
* storage.
|
|
779
801
|
*/
|
|
780
|
-
function recordReload(key,
|
|
802
|
+
function recordReload(key, value) {
|
|
781
803
|
try {
|
|
782
|
-
if (window.sessionStorage.getItem(key) ===
|
|
783
|
-
window.sessionStorage.setItem(key,
|
|
804
|
+
if (window.sessionStorage.getItem(key) === value) return false;
|
|
805
|
+
window.sessionStorage.setItem(key, value);
|
|
784
806
|
return true;
|
|
785
807
|
} catch {
|
|
786
808
|
return false;
|
|
@@ -788,21 +810,24 @@ function recordReload(key, version) {
|
|
|
788
810
|
}
|
|
789
811
|
/**
|
|
790
812
|
* Returns the function that reloads the page after a module failed to import, once per build
|
|
791
|
-
* version, and returns true while the page reloads.
|
|
813
|
+
* version and deployment, and returns true while the page reloads.
|
|
792
814
|
*
|
|
793
815
|
* @remarks
|
|
794
816
|
* The function records the product's version under `stealth.<productId>.reloaded` before it
|
|
795
|
-
* reloads
|
|
796
|
-
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
|
|
800
|
-
|
|
817
|
+
* reloads, as `<version>+<deployment>` on a page that loaded a deployment's releases. For a value
|
|
818
|
+
* recorded before, it returns false, so a chunk the new build lacks as well renders the error
|
|
819
|
+
* component instead of a second reload. A server has no page to reload, and a page whose storage
|
|
820
|
+
* the browser refuses cannot record the value, so both return false.
|
|
821
|
+
* @param product - The product: its id names the storage key, its version is the build's, and its
|
|
822
|
+
* deployment is the one whose releases the page loaded.
|
|
823
|
+
*/
|
|
824
|
+
function staleRecovery({ deployment, productId, version }) {
|
|
801
825
|
const key = `stealth.${productId}.reloaded`;
|
|
826
|
+
const recorded = deployment === void 0 ? version : `${version}+${deployment}`;
|
|
802
827
|
let reloading = false;
|
|
803
828
|
return () => {
|
|
804
829
|
if (reloading) return true;
|
|
805
|
-
if (typeof window === "undefined" || !recordReload(key,
|
|
830
|
+
if (typeof window === "undefined" || !recordReload(key, recorded)) return false;
|
|
806
831
|
reloading = true;
|
|
807
832
|
window.location.reload();
|
|
808
833
|
return true;
|
|
@@ -817,7 +842,8 @@ function staleRecovery({ productId, version }) {
|
|
|
817
842
|
* failed to preload and for a module that failed to import. A cancelled event makes the import
|
|
818
843
|
* resolve with no module, which the host's importers keep pending while the page reloads. An
|
|
819
844
|
* event the listener does not cancel rejects the import.
|
|
820
|
-
* @param recover - Reloads the page once per build version, and returns true while
|
|
845
|
+
* @param recover - Reloads the page once per build version and deployment, and returns true while
|
|
846
|
+
* it reloads.
|
|
821
847
|
*/
|
|
822
848
|
function listenForStaleChunks(recover) {
|
|
823
849
|
/**
|
|
@@ -858,8 +884,13 @@ function moduleOf(module, pluginId) {
|
|
|
858
884
|
* Imports a plugin's module through its importer, measures the plugin's first import, and reloads
|
|
859
885
|
* the page where the import fails.
|
|
860
886
|
*
|
|
887
|
+
* @remarks
|
|
888
|
+
* Where the page does not reload, the import rejects with a `PluginImportError`, whose `cause` is
|
|
889
|
+
* the import's error. TanStack's lazy route component reloads the page once more for an error
|
|
890
|
+
* whose message names a module that failed to fetch, and the host's message names none, so the
|
|
891
|
+
* route's error component renders after the one reload per build version and deployment.
|
|
861
892
|
* @returns The module, or a promise that never settles while the page reloads.
|
|
862
|
-
* @throws
|
|
893
|
+
* @throws {@link PluginImportError} Where the import fails and the page does not reload.
|
|
863
894
|
*/
|
|
864
895
|
async function imported(importer, pluginId, loading) {
|
|
865
896
|
const start = loading.startOf(pluginId);
|
|
@@ -868,7 +899,7 @@ async function imported(importer, pluginId, loading) {
|
|
|
868
899
|
module = moduleOf(await importer(), pluginId);
|
|
869
900
|
} catch (error) {
|
|
870
901
|
if (loading.recover()) return reloading();
|
|
871
|
-
throw error;
|
|
902
|
+
throw new PluginImportError(pluginId, error);
|
|
872
903
|
}
|
|
873
904
|
if (start !== void 0) performance.measure(`stealth:load:${pluginId}`, { start });
|
|
874
905
|
return module;
|
|
@@ -886,8 +917,8 @@ function mapped(record, map) {
|
|
|
886
917
|
return record === void 0 ? void 0 : Object.fromEntries(Object.entries(record).map(([name, value]) => [name, map(value)]));
|
|
887
918
|
}
|
|
888
919
|
/**
|
|
889
|
-
* Returns a plugin's code with every importer wrapped:
|
|
890
|
-
* settings sections.
|
|
920
|
+
* Returns a plugin's code with every importer wrapped: routes and their fallbacks, extensions,
|
|
921
|
+
* commands, settings sections and the page index.
|
|
891
922
|
*/
|
|
892
923
|
function codeOf(code, wrap) {
|
|
893
924
|
return {
|
|
@@ -899,6 +930,7 @@ function codeOf(code, wrap) {
|
|
|
899
930
|
component: wrap(entry.component),
|
|
900
931
|
fallback: maybe(entry.fallback, wrap)
|
|
901
932
|
})),
|
|
933
|
+
pages: maybe(code.pages, wrap),
|
|
902
934
|
routes: mapped(code.routes, (route) => typeof route === "function" ? wrap(route) : {
|
|
903
935
|
component: wrap(route.component),
|
|
904
936
|
fallback: maybe(route.fallback, wrap)
|
|
@@ -929,11 +961,11 @@ function loadingOf(product) {
|
|
|
929
961
|
* @remarks
|
|
930
962
|
* A wrapped importer measures its plugin's first import as `stealth:load:<plugin id>`, from the
|
|
931
963
|
* moment the import starts until the module resolves. Where an import rejects or resolves with no
|
|
932
|
-
* module, the page reloads once per build version, and the import never settles. A
|
|
933
|
-
* for the same version
|
|
934
|
-
* renders it. `createRuntime` and
|
|
935
|
-
*
|
|
936
|
-
* one measure per plugin.
|
|
964
|
+
* module, the page reloads once per build version and deployment, and the import never settles. A
|
|
965
|
+
* second failure for the same version and deployment rejects with a `PluginImportError`, and the
|
|
966
|
+
* route's error component or the extension's boundary renders it. `createRuntime` and
|
|
967
|
+
* `createHostRoutes` both wrap the product they receive, so a product that passes one object to
|
|
968
|
+
* `createHost` and `createHostRoutes` shares one recovery and one measure per plugin.
|
|
937
969
|
* @param product - The product, with each installed plugin's manifest.
|
|
938
970
|
*/
|
|
939
971
|
function instrumented(product) {
|
|
@@ -1221,14 +1253,27 @@ function createAccessStore({ product, report, source }) {
|
|
|
1221
1253
|
*/
|
|
1222
1254
|
const ON = { on: true };
|
|
1223
1255
|
/**
|
|
1256
|
+
* The availability of a plugin that the computation withholds.
|
|
1257
|
+
*/
|
|
1258
|
+
const WITHHELD = {
|
|
1259
|
+
on: false,
|
|
1260
|
+
reason: "unavailable"
|
|
1261
|
+
};
|
|
1262
|
+
/**
|
|
1263
|
+
* The empty set of withheld plugins.
|
|
1264
|
+
*/
|
|
1265
|
+
const NONE$1 = /* @__PURE__ */ new Set();
|
|
1266
|
+
/**
|
|
1224
1267
|
* Returns every installed plugin's availability, in install order.
|
|
1225
1268
|
*
|
|
1226
1269
|
* @remarks
|
|
1227
|
-
* A plugin is computed on first need, so
|
|
1228
|
-
*
|
|
1229
|
-
* requirements
|
|
1270
|
+
* A plugin is computed on first need, so every plugin in its condition and every plugin it
|
|
1271
|
+
* requires is computed before it, whatever the install order. The build refuses a ring of
|
|
1272
|
+
* conditions and requirements. On such a ring, `on` returns false for a plugin that is still
|
|
1273
|
+
* being computed. A withheld plugin is off before any test runs, so a plugin that requires it is
|
|
1274
|
+
* off too.
|
|
1230
1275
|
*/
|
|
1231
|
-
function availabilityOf({ availability, flags, plugins, session, switches }) {
|
|
1276
|
+
function availabilityOf({ availability, flags, plugins, session, switches, withheld }) {
|
|
1232
1277
|
const installed = new Map(plugins.map((plugin) => [plugin.id, plugin]));
|
|
1233
1278
|
const known = /* @__PURE__ */ new Map();
|
|
1234
1279
|
const computing = /* @__PURE__ */ new Set();
|
|
@@ -1251,6 +1296,7 @@ function availabilityOf({ availability, flags, plugins, session, switches }) {
|
|
|
1251
1296
|
* Returns a plugin's availability: the reason of the first test it fails, else on.
|
|
1252
1297
|
*/
|
|
1253
1298
|
const tested = (plugin) => {
|
|
1299
|
+
if (withheld.has(plugin.id)) return WITHHELD;
|
|
1254
1300
|
if (flags.read(plugin.killSwitch) === false) return {
|
|
1255
1301
|
on: false,
|
|
1256
1302
|
reason: "unavailable"
|
|
@@ -1284,6 +1330,24 @@ function availabilityOf({ availability, flags, plugins, session, switches }) {
|
|
|
1284
1330
|
return plugins.map((plugin) => [plugin.id, computed(plugin)]);
|
|
1285
1331
|
}
|
|
1286
1332
|
/**
|
|
1333
|
+
* Returns the reader of the availability inside a server snapshot: computed from the flags the
|
|
1334
|
+
* flag store returns there, and computed again only after the flag state it returns changed.
|
|
1335
|
+
*
|
|
1336
|
+
* @param flags - The flag store, whose state the computation is kept for.
|
|
1337
|
+
* @param compute - Computes every plugin's availability from the stores as they read.
|
|
1338
|
+
*/
|
|
1339
|
+
function servedOf(flags, compute) {
|
|
1340
|
+
let served;
|
|
1341
|
+
return () => {
|
|
1342
|
+
const state = flags.get();
|
|
1343
|
+
if (served === void 0 || served.flags !== state) served = {
|
|
1344
|
+
flags: state,
|
|
1345
|
+
value: Object.fromEntries(compute())
|
|
1346
|
+
};
|
|
1347
|
+
return served.value;
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1287
1351
|
* Returns the availability store, computed now and again whenever the session, a flag or a switch
|
|
1288
1352
|
* changes.
|
|
1289
1353
|
*
|
|
@@ -1292,19 +1356,24 @@ function availabilityOf({ availability, flags, plugins, session, switches }) {
|
|
|
1292
1356
|
* on (`unavailable`), it is locked or switched on (`off`), its condition is true (`condition`),
|
|
1293
1357
|
* and every plugin it requires without `optional` is on (`requirement`). A computation that
|
|
1294
1358
|
* changes no plugin's state publishes nothing. A plugin whose reason changes while it remains
|
|
1295
|
-
* off is published without a call to `changed`.
|
|
1359
|
+
* off is published without a call to `changed`. Inside a server snapshot the store returns the
|
|
1360
|
+
* availability that it computes from the server render's flags, so a boundary that hydrates late
|
|
1361
|
+
* renders what the server rendered. The server renders a page without the plugins of its
|
|
1362
|
+
* releases, so inside a server snapshot each release plugin is off with the reason `unavailable`.
|
|
1296
1363
|
*/
|
|
1297
1364
|
function createAvailabilityStore({ changed, flags, product, session, switches }) {
|
|
1298
1365
|
const availability = writable({});
|
|
1299
1366
|
/**
|
|
1300
|
-
* Returns every plugin's availability as the stores are now
|
|
1367
|
+
* Returns every plugin's availability as the stores are now, with every plugin in `withheld`
|
|
1368
|
+
* off.
|
|
1301
1369
|
*/
|
|
1302
|
-
const compute = () => availabilityOf({
|
|
1370
|
+
const compute = (withheld = NONE$1) => availabilityOf({
|
|
1303
1371
|
availability,
|
|
1304
1372
|
flags,
|
|
1305
1373
|
plugins: product.plugins,
|
|
1306
1374
|
session,
|
|
1307
|
-
switches: switches.get()
|
|
1375
|
+
switches: switches.get(),
|
|
1376
|
+
withheld
|
|
1308
1377
|
});
|
|
1309
1378
|
const first = compute();
|
|
1310
1379
|
let states = new Map(first.map(([pluginId, state]) => [pluginId, state.on]));
|
|
@@ -1330,15 +1399,92 @@ function createAvailabilityStore({ changed, flags, product, session, switches })
|
|
|
1330
1399
|
flags.subscribe(recompute),
|
|
1331
1400
|
switches.subscribe(recompute)
|
|
1332
1401
|
];
|
|
1402
|
+
const releases = new Set(product.releases);
|
|
1403
|
+
const served = servedOf(flags, () => compute(releases));
|
|
1333
1404
|
return {
|
|
1334
1405
|
dispose: () => {
|
|
1335
1406
|
for (const stop of stops) stop();
|
|
1336
1407
|
},
|
|
1337
|
-
get: availability.get,
|
|
1408
|
+
get: () => inServerSnapshot() ? served() : availability.get(),
|
|
1338
1409
|
subscribe: availability.subscribe
|
|
1339
1410
|
};
|
|
1340
1411
|
}
|
|
1341
1412
|
//#endregion
|
|
1413
|
+
//#region src/stores/flag-overrides.ts
|
|
1414
|
+
/**
|
|
1415
|
+
* Returns true where a value fits a flag: a boolean for a release or an ops flag, one of its
|
|
1416
|
+
* variants for an experiment.
|
|
1417
|
+
*/
|
|
1418
|
+
function fits(flag, value) {
|
|
1419
|
+
return flag.type === "boolean" ? typeof value === "boolean" : typeof value === "string" && flag.variants?.includes(value) === true;
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Returns true where a parsed value is an object whose members can be read by name.
|
|
1423
|
+
*/
|
|
1424
|
+
function isRecord$1(value) {
|
|
1425
|
+
return typeof value === "object" && value !== null;
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Returns the object stored text contains, or an empty object for no text, for text that is not
|
|
1429
|
+
* JSON, and for JSON that is not an object.
|
|
1430
|
+
*/
|
|
1431
|
+
function parsed(text) {
|
|
1432
|
+
if (text === null) return {};
|
|
1433
|
+
try {
|
|
1434
|
+
const value = JSON.parse(text);
|
|
1435
|
+
return isRecord$1(value) ? value : {};
|
|
1436
|
+
} catch {
|
|
1437
|
+
return {};
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Returns the overrides the tab's storage contains that fit a declared flag, and queues
|
|
1442
|
+
* `flag-ignored` for each that does not fit. An override for a flag no installed plugin declares is
|
|
1443
|
+
* left out without a report.
|
|
1444
|
+
*
|
|
1445
|
+
* @param storage - The tab's session storage. No override is read without it.
|
|
1446
|
+
* @param key - The key the overrides are kept under.
|
|
1447
|
+
* @param declared - The declared flags, by qualified id.
|
|
1448
|
+
* @param queue - Receives `flag-ignored` for an override that does not fit its flag.
|
|
1449
|
+
*/
|
|
1450
|
+
function storedOverrides(storage, key, declared, queue) {
|
|
1451
|
+
const kept = [];
|
|
1452
|
+
for (const [id, value] of Object.entries(parsed(storage?.getItem(key) ?? null))) {
|
|
1453
|
+
const flag = declared.get(id);
|
|
1454
|
+
if (flag === void 0) continue;
|
|
1455
|
+
if (fits(flag, value)) kept.push([id, value]);
|
|
1456
|
+
else queue({
|
|
1457
|
+
flag: id,
|
|
1458
|
+
kind: "flag-ignored",
|
|
1459
|
+
source: "override",
|
|
1460
|
+
value
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
return Object.fromEntries(kept);
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Writes the overrides to the tab's storage, or removes the key where none is left.
|
|
1467
|
+
*
|
|
1468
|
+
* @returns False where the storage refused the write. The overrides then last until the page
|
|
1469
|
+
* reloads.
|
|
1470
|
+
*/
|
|
1471
|
+
function written(storage, key, overrides) {
|
|
1472
|
+
try {
|
|
1473
|
+
if (Object.keys(overrides).length === 0) storage.removeItem(key);
|
|
1474
|
+
else storage.setItem(key, JSON.stringify(overrides));
|
|
1475
|
+
return true;
|
|
1476
|
+
} catch {
|
|
1477
|
+
return false;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Returns the overrides with a flag's override replaced, or removed where no value is given.
|
|
1482
|
+
*/
|
|
1483
|
+
function withOverride(overrides, id, value) {
|
|
1484
|
+
const kept = Object.entries(overrides).filter(([flag]) => flag !== id);
|
|
1485
|
+
return Object.fromEntries(value === void 0 ? kept : [...kept, [id, value]]);
|
|
1486
|
+
}
|
|
1487
|
+
//#endregion
|
|
1342
1488
|
//#region src/stores/flag-session.ts
|
|
1343
1489
|
/**
|
|
1344
1490
|
* Returns the record of the variants served, for no subject yet.
|
|
@@ -1416,13 +1562,6 @@ function identityOf(source, failed) {
|
|
|
1416
1562
|
* variant the person saw.
|
|
1417
1563
|
*/
|
|
1418
1564
|
/**
|
|
1419
|
-
* Returns true where a value fits a flag: a boolean for a release or an ops flag, one of its
|
|
1420
|
-
* variants for an experiment.
|
|
1421
|
-
*/
|
|
1422
|
-
function fits(flag, value) {
|
|
1423
|
-
return flag.type === "boolean" ? typeof value === "boolean" : typeof value === "string" && flag.variants?.includes(value) === true;
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
1565
|
* Returns the flag source's value for a flag, or undefined where the source states none or throws.
|
|
1427
1566
|
* A throw queues `flags-failed`.
|
|
1428
1567
|
*/
|
|
@@ -1525,67 +1664,6 @@ function publishedOf(report) {
|
|
|
1525
1664
|
};
|
|
1526
1665
|
}
|
|
1527
1666
|
/**
|
|
1528
|
-
* Returns true where a parsed value is an object whose members can be read by name.
|
|
1529
|
-
*/
|
|
1530
|
-
function isRecord$1(value) {
|
|
1531
|
-
return typeof value === "object" && value !== null;
|
|
1532
|
-
}
|
|
1533
|
-
/**
|
|
1534
|
-
* Returns the object stored text contains, or an empty object for no text, for text that is not
|
|
1535
|
-
* JSON, and for JSON that is not an object.
|
|
1536
|
-
*/
|
|
1537
|
-
function parsed(text) {
|
|
1538
|
-
if (text === null) return {};
|
|
1539
|
-
try {
|
|
1540
|
-
const value = JSON.parse(text);
|
|
1541
|
-
return isRecord$1(value) ? value : {};
|
|
1542
|
-
} catch {
|
|
1543
|
-
return {};
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
/**
|
|
1547
|
-
* Returns the overrides the tab's storage contains that fit a declared flag, and queues
|
|
1548
|
-
* `flag-ignored` for each that does not fit. An override for a flag no installed plugin declares is
|
|
1549
|
-
* left out without a report.
|
|
1550
|
-
*/
|
|
1551
|
-
function storedOverrides(storage, key, declared, queue) {
|
|
1552
|
-
const kept = [];
|
|
1553
|
-
for (const [id, value] of Object.entries(parsed(storage?.getItem(key) ?? null))) {
|
|
1554
|
-
const flag = declared.get(id);
|
|
1555
|
-
if (flag === void 0) continue;
|
|
1556
|
-
if (fits(flag, value)) kept.push([id, value]);
|
|
1557
|
-
else queue({
|
|
1558
|
-
flag: id,
|
|
1559
|
-
kind: "flag-ignored",
|
|
1560
|
-
source: "override",
|
|
1561
|
-
value
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
return Object.fromEntries(kept);
|
|
1565
|
-
}
|
|
1566
|
-
/**
|
|
1567
|
-
* Writes the overrides to the tab's storage, or removes the key where none is left.
|
|
1568
|
-
*
|
|
1569
|
-
* @returns False where the storage refused the write. The overrides then last until the page
|
|
1570
|
-
* reloads.
|
|
1571
|
-
*/
|
|
1572
|
-
function written(storage, key, overrides) {
|
|
1573
|
-
try {
|
|
1574
|
-
if (Object.keys(overrides).length === 0) storage.removeItem(key);
|
|
1575
|
-
else storage.setItem(key, JSON.stringify(overrides));
|
|
1576
|
-
return true;
|
|
1577
|
-
} catch {
|
|
1578
|
-
return false;
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
/**
|
|
1582
|
-
* Returns the overrides with a flag's override replaced, or removed where the value is undefined.
|
|
1583
|
-
*/
|
|
1584
|
-
function withOverride(overrides, id, value) {
|
|
1585
|
-
const kept = Object.entries(overrides).filter(([flag]) => flag !== id);
|
|
1586
|
-
return Object.fromEntries(value === void 0 ? kept : [...kept, [id, value]]);
|
|
1587
|
-
}
|
|
1588
|
-
/**
|
|
1589
1667
|
* Returns the store's `read`: the tab's override, else the reading the page took, else a first
|
|
1590
1668
|
* evaluation, published at the end of the task.
|
|
1591
1669
|
*/
|
|
@@ -1664,20 +1742,28 @@ function hydrationOf(shared, reevaluate, waiting) {
|
|
|
1664
1742
|
const { declared, exposures, published } = shared;
|
|
1665
1743
|
let active = false;
|
|
1666
1744
|
let aside = {};
|
|
1745
|
+
let served;
|
|
1667
1746
|
return {
|
|
1668
1747
|
active: () => active,
|
|
1669
1748
|
hydrate: (readings, subject) => {
|
|
1749
|
+
const kept = /* @__PURE__ */ new Map();
|
|
1670
1750
|
active = true;
|
|
1671
1751
|
aside = published.state.get().overrides;
|
|
1672
1752
|
published.setOverrides({});
|
|
1673
1753
|
for (const [id, reading] of readings) {
|
|
1674
1754
|
const flag = declared.get(id);
|
|
1675
1755
|
if (flag === void 0 || !fits(flag, reading.value)) continue;
|
|
1756
|
+
kept.set(id, reading);
|
|
1676
1757
|
published.readings.set(id, reading);
|
|
1677
1758
|
exposures.serve(subject, flag, reading.value);
|
|
1678
1759
|
}
|
|
1760
|
+
served = {
|
|
1761
|
+
overrides: {},
|
|
1762
|
+
readings: kept
|
|
1763
|
+
};
|
|
1679
1764
|
published.flush();
|
|
1680
1765
|
},
|
|
1766
|
+
served: () => served,
|
|
1681
1767
|
settle: () => {
|
|
1682
1768
|
if (!active) return;
|
|
1683
1769
|
active = false;
|
|
@@ -1688,6 +1774,28 @@ function hydrationOf(shared, reevaluate, waiting) {
|
|
|
1688
1774
|
};
|
|
1689
1775
|
}
|
|
1690
1776
|
/**
|
|
1777
|
+
* Returns the store's `get` and `read`, which return what the server render read inside a server
|
|
1778
|
+
* snapshot after a hydration, and the published state outside one.
|
|
1779
|
+
*
|
|
1780
|
+
* @remarks
|
|
1781
|
+
* A boundary can hydrate after `settle`, once the store took the browser's values. React reads
|
|
1782
|
+
* its components' flags as a server snapshot, so the boundary hydrates against the server's HTML
|
|
1783
|
+
* and renders the browser's values after it. A flag the server render did not read is evaluated
|
|
1784
|
+
* as outside a server snapshot.
|
|
1785
|
+
* @param hydration - The store's hydration, which keeps the server render's state.
|
|
1786
|
+
* @param published - The store's published state.
|
|
1787
|
+
* @param read - The store's reader outside a server snapshot.
|
|
1788
|
+
*/
|
|
1789
|
+
function servedReadersOf(hydration, published, read) {
|
|
1790
|
+
return {
|
|
1791
|
+
get: () => (inServerSnapshot() ? hydration.served() : void 0) ?? published.state.get(),
|
|
1792
|
+
read: (id) => {
|
|
1793
|
+
const served = inServerSnapshot() ? hydration.served()?.readings.get(id) : void 0;
|
|
1794
|
+
return served === void 0 ? read(id) : served.value;
|
|
1795
|
+
}
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1691
1799
|
* Returns the flag store over the product's flags, its flag source and the tab's storage.
|
|
1692
1800
|
*
|
|
1693
1801
|
* @remarks
|
|
@@ -1698,7 +1806,8 @@ function hydrationOf(shared, reevaluate, waiting) {
|
|
|
1698
1806
|
* the flags the page has read, and the previous values remain while `identify` is pending.
|
|
1699
1807
|
* `flag-exposed` is reported the first time the session's subject is served each variant of an
|
|
1700
1808
|
* experiment. An override counts no exposure. A store a server render hydrated reads the
|
|
1701
|
-
* server's readings until `settle`, and evaluates again only then.
|
|
1809
|
+
* server's readings until `settle`, and evaluates again only then. Inside a server snapshot it
|
|
1810
|
+
* returns the server's readings after `settle` too.
|
|
1702
1811
|
*/
|
|
1703
1812
|
function createFlagStore({ overrides: storage, product, report, source }) {
|
|
1704
1813
|
const published = publishedOf(report);
|
|
@@ -1723,19 +1832,21 @@ function createFlagStore({ overrides: storage, product, report, source }) {
|
|
|
1723
1832
|
const stop = source?.subscribe(() => {
|
|
1724
1833
|
if (shared.identity.trusted() !== void 0 && !hydration.active()) reevaluate();
|
|
1725
1834
|
});
|
|
1835
|
+
const readers = servedReadersOf(hydration, published, readerOf(shared));
|
|
1726
1836
|
return {
|
|
1727
1837
|
dispose: () => {
|
|
1728
1838
|
stop?.();
|
|
1729
1839
|
shared.identity.stop();
|
|
1730
1840
|
},
|
|
1731
|
-
get:
|
|
1841
|
+
get: readers.get,
|
|
1732
1842
|
hydrate: hydration.hydrate,
|
|
1733
1843
|
identify: async (session) => {
|
|
1734
1844
|
shared.exposures.reset(subjectOf(session));
|
|
1735
1845
|
if (await shared.identity.identify(session) && !hydration.active()) reevaluate();
|
|
1736
1846
|
},
|
|
1847
|
+
overridable: storage !== void 0,
|
|
1737
1848
|
override: overriderOf(shared, storage, key),
|
|
1738
|
-
read:
|
|
1849
|
+
read: readers.read,
|
|
1739
1850
|
settle: hydration.settle,
|
|
1740
1851
|
snapshot: () => [...published.readings],
|
|
1741
1852
|
subscribe: published.state.subscribe
|
|
@@ -2249,12 +2360,33 @@ function createHost(options) {
|
|
|
2249
2360
|
connect: connection.connect,
|
|
2250
2361
|
flags,
|
|
2251
2362
|
glyphs: options.glyphs,
|
|
2363
|
+
loading: page.loading,
|
|
2252
2364
|
runtime,
|
|
2253
2365
|
switches: state.switches
|
|
2254
2366
|
});
|
|
2255
2367
|
return host;
|
|
2256
2368
|
}
|
|
2257
2369
|
//#endregion
|
|
2370
|
+
//#region src/parts/caught.ts
|
|
2371
|
+
/**
|
|
2372
|
+
* Writes an error that a React error boundary caught to the console, and leaves the router's
|
|
2373
|
+
* not-found out.
|
|
2374
|
+
*/
|
|
2375
|
+
/**
|
|
2376
|
+
* Writes an error that an error boundary caught to the console, as React's default does in a
|
|
2377
|
+
* production build, and returns without writing for the router's not-found.
|
|
2378
|
+
*
|
|
2379
|
+
* @remarks
|
|
2380
|
+
* A page throws the router's not-found for parameters it cannot resolve, and the router's
|
|
2381
|
+
* boundary renders the product's not-found page in the page's place. React reports every error a
|
|
2382
|
+
* boundary catches, so a root without this handler writes each such not-found to the console. A
|
|
2383
|
+
* product passes the handler to `createRoot` and `hydrateRoot` as `onCaughtError`.
|
|
2384
|
+
*/
|
|
2385
|
+
function onCaughtError(error) {
|
|
2386
|
+
if (isNotFound(error)) return;
|
|
2387
|
+
console.error(error);
|
|
2388
|
+
}
|
|
2389
|
+
//#endregion
|
|
2258
2390
|
//#region src/parts/content.tsx
|
|
2259
2391
|
/**
|
|
2260
2392
|
* Renders the root route's outlet inside the `content` region, so the region's extensions decorate
|
|
@@ -2277,89 +2409,107 @@ function HostContent() {
|
|
|
2277
2409
|
return t0;
|
|
2278
2410
|
}
|
|
2279
2411
|
//#endregion
|
|
2280
|
-
//#region src/
|
|
2281
|
-
/**
|
|
2282
|
-
* Returns true for the data of a page whose plugin a switch or a kill switch stopped.
|
|
2283
|
-
*/
|
|
2284
|
-
function isStopped(data) {
|
|
2285
|
-
return "plugin" in data && typeof data.plugin === "string" && "reason" in data && (data.reason === "off" || data.reason === "unavailable");
|
|
2286
|
-
}
|
|
2412
|
+
//#region src/host/use-internals.ts
|
|
2287
2413
|
/**
|
|
2288
|
-
*
|
|
2414
|
+
* Reads the internals of the host that the router's context contains, for the host's own parts.
|
|
2289
2415
|
*/
|
|
2290
|
-
function isQuarantined(data) {
|
|
2291
|
-
return "target" in data && typeof data.target === "string" && data.target.startsWith("route:") && "reason" in data && data.reason === "quarantined";
|
|
2292
|
-
}
|
|
2293
2416
|
/**
|
|
2294
|
-
*
|
|
2417
|
+
* Returns the internals of the host that the router's context contains.
|
|
2295
2418
|
*
|
|
2296
|
-
* @
|
|
2297
|
-
* Every route of the tree can throw a not-found error, so data of another shape, from a route the
|
|
2298
|
-
* product wrote, reads as none.
|
|
2299
|
-
* @param data - The data the router passes the not-found component.
|
|
2300
|
-
* @returns The data the route evaluator threw, or nothing.
|
|
2419
|
+
* @throws {@link Error} Where `createHost` did not create the host.
|
|
2301
2420
|
*/
|
|
2302
|
-
function
|
|
2303
|
-
|
|
2304
|
-
|
|
2421
|
+
function useInternals() {
|
|
2422
|
+
const $ = c(3);
|
|
2423
|
+
let t0;
|
|
2424
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2425
|
+
t0 = { strict: false };
|
|
2426
|
+
$[0] = t0;
|
|
2427
|
+
} else t0 = $[0];
|
|
2428
|
+
const { host } = useRouteContext(t0);
|
|
2429
|
+
let t1;
|
|
2430
|
+
if ($[1] !== host) {
|
|
2431
|
+
t1 = internalsOf(host);
|
|
2432
|
+
$[1] = host;
|
|
2433
|
+
$[2] = t1;
|
|
2434
|
+
} else t1 = $[2];
|
|
2435
|
+
return t1;
|
|
2305
2436
|
}
|
|
2306
2437
|
//#endregion
|
|
2307
|
-
//#region src/parts/
|
|
2438
|
+
//#region src/parts/loading.tsx
|
|
2308
2439
|
/**
|
|
2309
|
-
* Renders the
|
|
2310
|
-
*
|
|
2440
|
+
* Renders the host's loading page. The loading page takes the place of a page while the router
|
|
2441
|
+
* loads that page.
|
|
2311
2442
|
*/
|
|
2312
2443
|
/**
|
|
2313
|
-
*
|
|
2314
|
-
* person was on without a navigation.
|
|
2444
|
+
* Renders a busy panel with a spinner above a level-one heading.
|
|
2315
2445
|
*
|
|
2316
2446
|
* @remarks
|
|
2317
|
-
* The
|
|
2318
|
-
*
|
|
2319
|
-
*
|
|
2447
|
+
* The spinner does not have a role, so a screen reader announces the state from the heading. The
|
|
2448
|
+
* panel sets `aria-busy` while it renders. The host records the panel as on screen from its
|
|
2449
|
+
* commit until it unmounts, so the page that replaces it renders as the first page of the load.
|
|
2320
2450
|
*/
|
|
2321
|
-
function
|
|
2322
|
-
const $ = c(
|
|
2323
|
-
const
|
|
2324
|
-
const
|
|
2451
|
+
function HostLoading() {
|
|
2452
|
+
const $ = c(8);
|
|
2453
|
+
const { t } = useTranslation("host");
|
|
2454
|
+
const { loading } = useInternals();
|
|
2325
2455
|
let t0;
|
|
2326
|
-
if ($[0] !== router.state.location.href || $[1] !== router.state.resolvedLocation?.href) {
|
|
2327
|
-
t0 = () => router.state.resolvedLocation?.href === router.state.location.href;
|
|
2328
|
-
$[0] = router.state.location.href;
|
|
2329
|
-
$[1] = router.state.resolvedLocation?.href;
|
|
2330
|
-
$[2] = t0;
|
|
2331
|
-
} else t0 = $[2];
|
|
2332
|
-
const [replaced] = useState(t0);
|
|
2333
2456
|
let t1;
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2457
|
+
if ($[0] !== loading) {
|
|
2458
|
+
t0 = () => {
|
|
2459
|
+
loading.set(true);
|
|
2460
|
+
return () => {
|
|
2461
|
+
loading.set(false);
|
|
2462
|
+
};
|
|
2338
2463
|
};
|
|
2339
|
-
|
|
2340
|
-
$[
|
|
2341
|
-
$[
|
|
2342
|
-
$[
|
|
2464
|
+
t1 = [loading];
|
|
2465
|
+
$[0] = loading;
|
|
2466
|
+
$[1] = t0;
|
|
2467
|
+
$[2] = t1;
|
|
2343
2468
|
} else {
|
|
2344
|
-
|
|
2345
|
-
|
|
2469
|
+
t0 = $[1];
|
|
2470
|
+
t1 = $[2];
|
|
2346
2471
|
}
|
|
2347
|
-
useEffect(
|
|
2348
|
-
|
|
2472
|
+
useEffect(t0, t1);
|
|
2473
|
+
let t2;
|
|
2474
|
+
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2475
|
+
t2 = /* @__PURE__ */ jsx(EmptyState.Indicator, { children: /* @__PURE__ */ jsx(Spinner, { size: "2xl" }) });
|
|
2476
|
+
$[3] = t2;
|
|
2477
|
+
} else t2 = $[3];
|
|
2478
|
+
let t3;
|
|
2479
|
+
if ($[4] !== t) {
|
|
2480
|
+
t3 = t("page.loading.title");
|
|
2481
|
+
$[4] = t;
|
|
2482
|
+
$[5] = t3;
|
|
2483
|
+
} else t3 = $[5];
|
|
2484
|
+
let t4;
|
|
2485
|
+
if ($[6] !== t3) {
|
|
2486
|
+
t4 = /* @__PURE__ */ jsx(EmptyState.Root, {
|
|
2487
|
+
"aria-busy": "true",
|
|
2488
|
+
children: /* @__PURE__ */ jsxs(EmptyState.Content, { children: [t2, /* @__PURE__ */ jsx(EmptyState.Title, {
|
|
2489
|
+
as: "h1",
|
|
2490
|
+
children: t3
|
|
2491
|
+
})] })
|
|
2492
|
+
});
|
|
2493
|
+
$[6] = t3;
|
|
2494
|
+
$[7] = t4;
|
|
2495
|
+
} else t4 = $[7];
|
|
2496
|
+
return t4;
|
|
2349
2497
|
}
|
|
2498
|
+
//#endregion
|
|
2499
|
+
//#region src/parts/not-found-page.tsx
|
|
2350
2500
|
/**
|
|
2351
|
-
* Renders the not-found page the
|
|
2501
|
+
* Renders the not-found page that the data describes.
|
|
2352
2502
|
*
|
|
2353
2503
|
* @remarks
|
|
2354
|
-
* A page whose plugin a switch turned off
|
|
2355
|
-
* A page whose plugin a kill switch stopped
|
|
2356
|
-
* page
|
|
2357
|
-
*
|
|
2358
|
-
*
|
|
2504
|
+
* A page whose plugin a switch turned off shows the plugin's name and a button that turns the
|
|
2505
|
+
* plugin on. A page whose plugin a kill switch stopped shows the plugin's name alone. A
|
|
2506
|
+
* quarantined page shows a retry that lifts the quarantine. A plain not-found page does not
|
|
2507
|
+
* describe the address. The host runs the router's `beforeLoad` again after a switch or a retry,
|
|
2508
|
+
* and the page renders again in place of this one.
|
|
2359
2509
|
*/
|
|
2360
|
-
function
|
|
2510
|
+
function NotFoundPage(t0) {
|
|
2361
2511
|
const $ = c(25);
|
|
2362
|
-
const {
|
|
2512
|
+
const { found, heading } = t0;
|
|
2363
2513
|
let t1;
|
|
2364
2514
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2365
2515
|
t1 = { strict: false };
|
|
@@ -2367,16 +2517,13 @@ function HostNotFound(t0) {
|
|
|
2367
2517
|
} else t1 = $[0];
|
|
2368
2518
|
const { host } = useRouteContext(t1);
|
|
2369
2519
|
const { i18n, t } = useTranslation("host");
|
|
2370
|
-
const
|
|
2520
|
+
const reason = found?.reason ?? "plain";
|
|
2371
2521
|
let T0;
|
|
2372
2522
|
let T1;
|
|
2373
|
-
let found;
|
|
2374
2523
|
let t2;
|
|
2375
2524
|
let t3;
|
|
2376
2525
|
let t4;
|
|
2377
|
-
if ($[1] !==
|
|
2378
|
-
found = notFoundDataOf(data);
|
|
2379
|
-
const reason = found?.reason ?? "plain";
|
|
2526
|
+
if ($[1] !== found || $[2] !== heading || $[3] !== host || $[4] !== i18n || $[5] !== reason || $[6] !== t) {
|
|
2380
2527
|
const plugin = found === void 0 || found.reason === "quarantined" ? "" : pluginWordsOf(i18n).t(found.plugin, "plugin.name");
|
|
2381
2528
|
T1 = EmptyState.Root;
|
|
2382
2529
|
T0 = EmptyState.Content;
|
|
@@ -2393,21 +2540,20 @@ function HostNotFound(t0) {
|
|
|
2393
2540
|
},
|
|
2394
2541
|
children: t("notFound.off.action", { plugin })
|
|
2395
2542
|
}) : null;
|
|
2396
|
-
$[1] =
|
|
2543
|
+
$[1] = found;
|
|
2397
2544
|
$[2] = heading;
|
|
2398
2545
|
$[3] = host;
|
|
2399
2546
|
$[4] = i18n;
|
|
2400
|
-
$[5] =
|
|
2401
|
-
$[6] =
|
|
2402
|
-
$[7] =
|
|
2403
|
-
$[8] =
|
|
2547
|
+
$[5] = reason;
|
|
2548
|
+
$[6] = t;
|
|
2549
|
+
$[7] = T0;
|
|
2550
|
+
$[8] = T1;
|
|
2404
2551
|
$[9] = t2;
|
|
2405
2552
|
$[10] = t3;
|
|
2406
2553
|
$[11] = t4;
|
|
2407
2554
|
} else {
|
|
2408
|
-
T0 = $[
|
|
2409
|
-
T1 = $[
|
|
2410
|
-
found = $[8];
|
|
2555
|
+
T0 = $[7];
|
|
2556
|
+
T1 = $[8];
|
|
2411
2557
|
t2 = $[9];
|
|
2412
2558
|
t3 = $[10];
|
|
2413
2559
|
t4 = $[11];
|
|
@@ -2450,16 +2596,206 @@ function HostNotFound(t0) {
|
|
|
2450
2596
|
return t7;
|
|
2451
2597
|
}
|
|
2452
2598
|
//#endregion
|
|
2453
|
-
//#region src/
|
|
2599
|
+
//#region src/routes/not-found.ts
|
|
2454
2600
|
/**
|
|
2455
|
-
* Returns the
|
|
2456
|
-
*
|
|
2457
|
-
* @remarks
|
|
2458
|
-
* The root and every route the product wrote without a name state no id, so the list leaves them
|
|
2459
|
-
* out.
|
|
2601
|
+
* Returns true for the data of a page whose plugin a switch or a kill switch stopped.
|
|
2460
2602
|
*/
|
|
2461
|
-
function
|
|
2462
|
-
return
|
|
2603
|
+
function isStopped(data) {
|
|
2604
|
+
return "plugin" in data && typeof data.plugin === "string" && "reason" in data && (data.reason === "off" || data.reason === "unavailable");
|
|
2605
|
+
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Returns true for the data of a page the host quarantined.
|
|
2608
|
+
*/
|
|
2609
|
+
function isQuarantined(data) {
|
|
2610
|
+
return "target" in data && typeof data.target === "string" && data.target.startsWith("route:") && "reason" in data && data.reason === "quarantined";
|
|
2611
|
+
}
|
|
2612
|
+
/**
|
|
2613
|
+
* Reads the data of a not-found error as the host's not-found data.
|
|
2614
|
+
*
|
|
2615
|
+
* @remarks
|
|
2616
|
+
* Every route of the tree can throw a not-found error, so data of another shape, from a route the
|
|
2617
|
+
* product wrote, reads as none.
|
|
2618
|
+
* @param data - The data the router passes the not-found component.
|
|
2619
|
+
* @returns The data the route evaluator threw, or nothing.
|
|
2620
|
+
*/
|
|
2621
|
+
function notFoundDataOf(data) {
|
|
2622
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
2623
|
+
return isStopped(data) || isQuarantined(data) ? data : void 0;
|
|
2624
|
+
}
|
|
2625
|
+
//#endregion
|
|
2626
|
+
//#region src/routes/release-reload.ts
|
|
2627
|
+
/**
|
|
2628
|
+
* Loads the address again after a hydration where the page's document contains a deployment.
|
|
2629
|
+
*
|
|
2630
|
+
* @remarks
|
|
2631
|
+
* A server renders a page without the routes of its releases. Where only a route of a release
|
|
2632
|
+
* matches the address, the server renders the not-found page. The router's hydration commits that
|
|
2633
|
+
* page as final and does not load the address. The page loads the address again once it hydrated.
|
|
2634
|
+
*/
|
|
2635
|
+
/**
|
|
2636
|
+
* Returns true while a plain not-found page waits for the router to load the address again with
|
|
2637
|
+
* the routes of the page's releases.
|
|
2638
|
+
*
|
|
2639
|
+
* @remarks
|
|
2640
|
+
* The wait starts where the page renders on a server or hydrates, and the page's document
|
|
2641
|
+
* contains a deployment. After the page hydrated, the router loads the address again. The wait
|
|
2642
|
+
* ends when that load ends. A route of a release that matches the address then renders in place
|
|
2643
|
+
* of the not-found page.
|
|
2644
|
+
* @param plain - True where the not-found page does not have data of the host's own.
|
|
2645
|
+
*/
|
|
2646
|
+
function useReleaseReload(plain) {
|
|
2647
|
+
const $ = c(8);
|
|
2648
|
+
const { product } = useInternals().runtime;
|
|
2649
|
+
const router = useRouter();
|
|
2650
|
+
const hydrating = useHydrating();
|
|
2651
|
+
let t0;
|
|
2652
|
+
if ($[0] !== hydrating || $[1] !== plain || $[2] !== product) {
|
|
2653
|
+
t0 = () => plain && hydrating && product.releases !== void 0;
|
|
2654
|
+
$[0] = hydrating;
|
|
2655
|
+
$[1] = plain;
|
|
2656
|
+
$[2] = product;
|
|
2657
|
+
$[3] = t0;
|
|
2658
|
+
} else t0 = $[3];
|
|
2659
|
+
const [reloading, setReloading] = useState(t0);
|
|
2660
|
+
let t1;
|
|
2661
|
+
let t2;
|
|
2662
|
+
if ($[4] !== reloading || $[5] !== router) {
|
|
2663
|
+
t1 = () => {
|
|
2664
|
+
if (!reloading) return;
|
|
2665
|
+
const ended = () => {
|
|
2666
|
+
setReloading(false);
|
|
2667
|
+
};
|
|
2668
|
+
router.invalidate().then(ended, ended);
|
|
2669
|
+
};
|
|
2670
|
+
t2 = [reloading, router];
|
|
2671
|
+
$[4] = reloading;
|
|
2672
|
+
$[5] = router;
|
|
2673
|
+
$[6] = t1;
|
|
2674
|
+
$[7] = t2;
|
|
2675
|
+
} else {
|
|
2676
|
+
t1 = $[6];
|
|
2677
|
+
t2 = $[7];
|
|
2678
|
+
}
|
|
2679
|
+
useEffect(t1, t2);
|
|
2680
|
+
return reloading;
|
|
2681
|
+
}
|
|
2682
|
+
//#endregion
|
|
2683
|
+
//#region src/routes/replacement.ts
|
|
2684
|
+
/**
|
|
2685
|
+
* Moves focus to a page that renders in place of the page a person is on, where focus moved to the
|
|
2686
|
+
* document's body.
|
|
2687
|
+
*/
|
|
2688
|
+
/**
|
|
2689
|
+
* Moves focus to the page when it renders in place of the page a person is on without a
|
|
2690
|
+
* navigation, and focus is on the document's body.
|
|
2691
|
+
*
|
|
2692
|
+
* @remarks
|
|
2693
|
+
* The router records an address as resolved only after the pages of its navigation rendered.
|
|
2694
|
+
* While the page first renders, the resolved address equals the address only where a switch, a
|
|
2695
|
+
* kill switch, a condition, a quarantine or a retry changed the page without a navigation. The
|
|
2696
|
+
* page takes focus only after the content region showed a page, so the first page of a load does
|
|
2697
|
+
* not take focus. A page that replaces the host's loading page is the first page of its load too.
|
|
2698
|
+
* A page does not take focus while it hydrates. Focus moves to the document's body when the
|
|
2699
|
+
* element that has it unmounts. The page takes focus from the body alone, so a control outside
|
|
2700
|
+
* the page keeps it. The element that takes focus gets a tab index of `-1` where it has none, so
|
|
2701
|
+
* it takes focus without a tab stop.
|
|
2702
|
+
* @param heading - The page's own heading. Without it, the first level-one heading of the main
|
|
2703
|
+
* region takes focus, else the main region.
|
|
2704
|
+
*/
|
|
2705
|
+
function useReplacementFocus(heading) {
|
|
2706
|
+
const $ = c(11);
|
|
2707
|
+
let t0;
|
|
2708
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2709
|
+
t0 = { strict: false };
|
|
2710
|
+
$[0] = t0;
|
|
2711
|
+
} else t0 = $[0];
|
|
2712
|
+
const { host } = useRouteContext(t0);
|
|
2713
|
+
const router = useRouter();
|
|
2714
|
+
const hydrating = useHydrating();
|
|
2715
|
+
let t1;
|
|
2716
|
+
if ($[1] !== host || $[2] !== hydrating || $[3] !== router) {
|
|
2717
|
+
t1 = () => !hydrating && !internalsOf(host).loading.get() && host.stores.mounted.get().has(hostContract.slots.content.id) && router.state.resolvedLocation?.href === router.state.location.href;
|
|
2718
|
+
$[1] = host;
|
|
2719
|
+
$[2] = hydrating;
|
|
2720
|
+
$[3] = router;
|
|
2721
|
+
$[4] = t1;
|
|
2722
|
+
} else t1 = $[4];
|
|
2723
|
+
const [replaced] = useState(t1);
|
|
2724
|
+
let t2;
|
|
2725
|
+
if ($[5] !== heading?.current || $[6] !== replaced) {
|
|
2726
|
+
t2 = () => {
|
|
2727
|
+
if (!replaced || document.activeElement !== document.body) return;
|
|
2728
|
+
const main = document.querySelector("main");
|
|
2729
|
+
const target = heading?.current ?? main?.querySelector("h1") ?? main;
|
|
2730
|
+
target?.setAttribute("tabindex", target.getAttribute("tabindex") ?? "-1");
|
|
2731
|
+
target?.focus();
|
|
2732
|
+
};
|
|
2733
|
+
$[5] = heading?.current;
|
|
2734
|
+
$[6] = replaced;
|
|
2735
|
+
$[7] = t2;
|
|
2736
|
+
} else t2 = $[7];
|
|
2737
|
+
let t3;
|
|
2738
|
+
if ($[8] !== heading || $[9] !== replaced) {
|
|
2739
|
+
t3 = [heading, replaced];
|
|
2740
|
+
$[8] = heading;
|
|
2741
|
+
$[9] = replaced;
|
|
2742
|
+
$[10] = t3;
|
|
2743
|
+
} else t3 = $[10];
|
|
2744
|
+
useEffect(t2, t3);
|
|
2745
|
+
}
|
|
2746
|
+
//#endregion
|
|
2747
|
+
//#region src/parts/not-found.tsx
|
|
2748
|
+
/**
|
|
2749
|
+
* Renders the root's not-found page: for a page whose plugin a switch or a kill switch stopped, for
|
|
2750
|
+
* a page the host quarantined, and for an address no route matches.
|
|
2751
|
+
*/
|
|
2752
|
+
/**
|
|
2753
|
+
* Renders the not-found page that the router's data describes, or the loading page while the
|
|
2754
|
+
* router loads the address again with the routes of the page's releases.
|
|
2755
|
+
*
|
|
2756
|
+
* @remarks
|
|
2757
|
+
* The page treats the router's data as the host's own where the route evaluator threw it, and
|
|
2758
|
+
* ignores any other data. Where the page's document contains a deployment, a plain
|
|
2759
|
+
* not-found page renders the loading page on a server and while it hydrates. It renders the
|
|
2760
|
+
* loading page until the router loaded the address again. The heading takes focus where the page
|
|
2761
|
+
* renders in place of the page a person is on.
|
|
2762
|
+
*/
|
|
2763
|
+
function HostNotFound(t0) {
|
|
2764
|
+
const $ = c(5);
|
|
2765
|
+
const { data } = t0;
|
|
2766
|
+
const heading = useRef(null);
|
|
2767
|
+
let t1;
|
|
2768
|
+
if ($[0] !== data) {
|
|
2769
|
+
t1 = notFoundDataOf(data);
|
|
2770
|
+
$[0] = data;
|
|
2771
|
+
$[1] = t1;
|
|
2772
|
+
} else t1 = $[1];
|
|
2773
|
+
const found = t1;
|
|
2774
|
+
const reloading = useReleaseReload(found === void 0);
|
|
2775
|
+
useReplacementFocus(heading);
|
|
2776
|
+
let t2;
|
|
2777
|
+
if ($[2] !== found || $[3] !== reloading) {
|
|
2778
|
+
t2 = reloading ? /* @__PURE__ */ jsx(HostLoading, {}) : /* @__PURE__ */ jsx(NotFoundPage, {
|
|
2779
|
+
found,
|
|
2780
|
+
heading
|
|
2781
|
+
});
|
|
2782
|
+
$[2] = found;
|
|
2783
|
+
$[3] = reloading;
|
|
2784
|
+
$[4] = t2;
|
|
2785
|
+
} else t2 = $[4];
|
|
2786
|
+
return t2;
|
|
2787
|
+
}
|
|
2788
|
+
//#endregion
|
|
2789
|
+
//#region src/parts/connection.ts
|
|
2790
|
+
/**
|
|
2791
|
+
* Returns the qualified ids of the declared routes the router matched, outermost first.
|
|
2792
|
+
*
|
|
2793
|
+
* @remarks
|
|
2794
|
+
* The root and every route the product wrote without a name state no id, so the list leaves them
|
|
2795
|
+
* out.
|
|
2796
|
+
*/
|
|
2797
|
+
function matchedOf(router) {
|
|
2798
|
+
return router.state.matches.flatMap((match) => declaredOf(match)?.id ?? []);
|
|
2463
2799
|
}
|
|
2464
2800
|
/**
|
|
2465
2801
|
* Returns the connection of a router and the catalogues' instance.
|
|
@@ -2709,7 +3045,7 @@ function HostFailed(t0) {
|
|
|
2709
3045
|
let t6;
|
|
2710
3046
|
if ($[11] !== t5) {
|
|
2711
3047
|
t6 = /* @__PURE__ */ jsx(Button, {
|
|
2712
|
-
onClick: _temp$
|
|
3048
|
+
onClick: _temp$6,
|
|
2713
3049
|
children: t5
|
|
2714
3050
|
});
|
|
2715
3051
|
$[11] = t5;
|
|
@@ -2732,7 +3068,7 @@ function HostFailed(t0) {
|
|
|
2732
3068
|
} else t7 = $[16];
|
|
2733
3069
|
return t7;
|
|
2734
3070
|
}
|
|
2735
|
-
function _temp$
|
|
3071
|
+
function _temp$6() {
|
|
2736
3072
|
window.location.reload();
|
|
2737
3073
|
}
|
|
2738
3074
|
//#endregion
|
|
@@ -2938,7 +3274,7 @@ function CommandKeys() {
|
|
|
2938
3274
|
const [hotkey, bound] = t1;
|
|
2939
3275
|
return {
|
|
2940
3276
|
callback: () => {
|
|
2941
|
-
const first = bound.find(_temp$
|
|
3277
|
+
const first = bound.find(_temp$5);
|
|
2942
3278
|
if (first !== void 0) runReporting(first, runtime, t("commands.failed", { label: first.label }));
|
|
2943
3279
|
},
|
|
2944
3280
|
hotkey
|
|
@@ -2946,20 +3282,305 @@ function CommandKeys() {
|
|
|
2946
3282
|
}));
|
|
2947
3283
|
return null;
|
|
2948
3284
|
}
|
|
2949
|
-
function _temp$
|
|
3285
|
+
function _temp$5(t0) {
|
|
2950
3286
|
const { enabled } = t0;
|
|
2951
3287
|
return enabled;
|
|
2952
3288
|
}
|
|
2953
3289
|
//#endregion
|
|
2954
|
-
//#region src/commands/palette.
|
|
3290
|
+
//#region src/commands/palette-state.ts
|
|
2955
3291
|
/**
|
|
2956
|
-
*
|
|
2957
|
-
* `Mod+K` opens.
|
|
3292
|
+
* Shares the command palette's open state between the palette and the controls of the frame.
|
|
2958
3293
|
*/
|
|
2959
3294
|
/**
|
|
2960
|
-
*
|
|
3295
|
+
* Provides the palette's open state to the palette and the frame. `HostRoot` provides it, and the
|
|
3296
|
+
* value is undefined outside `HostRoot`.
|
|
2961
3297
|
*/
|
|
2962
|
-
const
|
|
3298
|
+
const PaletteContext = createContext(void 0);
|
|
3299
|
+
/**
|
|
3300
|
+
* Returns the palette's open state, so a control in the frame opens the palette that `Mod+K` opens.
|
|
3301
|
+
*
|
|
3302
|
+
* @throws {@link Error} Outside `HostRoot`.
|
|
3303
|
+
*/
|
|
3304
|
+
function usePalette() {
|
|
3305
|
+
const state = use(PaletteContext);
|
|
3306
|
+
if (state === void 0) throw new Error("usePalette() found no HostRoot. The frame and the palette render inside HostRoot, which provides the palette's state.");
|
|
3307
|
+
return state;
|
|
3308
|
+
}
|
|
3309
|
+
//#endregion
|
|
3310
|
+
//#region src/app/listed.ts
|
|
3311
|
+
/**
|
|
3312
|
+
* Reads which routes the person may open, with or without a menu entry, and the menus and settings
|
|
3313
|
+
* pages a sidebar lists from them.
|
|
3314
|
+
*/
|
|
3315
|
+
/**
|
|
3316
|
+
* Returns the routes whose plugin is on and whose condition is true, with or without a menu entry.
|
|
3317
|
+
*
|
|
3318
|
+
* @param routes - The product's routes.
|
|
3319
|
+
* @param stores - The stores that a condition depends on.
|
|
3320
|
+
*/
|
|
3321
|
+
function openableRoutesOf(routes, stores) {
|
|
3322
|
+
const context = conditionContextOf(stores);
|
|
3323
|
+
return routes.filter(({ plugin, when }) => context.on(plugin) && evaluateWhen(when, context));
|
|
3324
|
+
}
|
|
3325
|
+
/**
|
|
3326
|
+
* Returns the routes with a menu entry whose plugin is on and whose condition is true.
|
|
3327
|
+
*
|
|
3328
|
+
* @param routes - The product's routes.
|
|
3329
|
+
* @param stores - The stores that a condition depends on.
|
|
3330
|
+
*/
|
|
3331
|
+
function listedRoutesOf(routes, stores) {
|
|
3332
|
+
return openableRoutesOf(routes, stores).filter(({ navigation }) => navigation !== void 0);
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Returns the ids of the routes a selection returns, and renders again when a plugin turns on or
|
|
3336
|
+
* off, a flag changes or the session changes.
|
|
3337
|
+
*
|
|
3338
|
+
* @remarks
|
|
3339
|
+
* The snapshot is the ids joined by spaces. A qualified id does not contain a space, so a
|
|
3340
|
+
* component does not render again after a change that selects the same routes. The server
|
|
3341
|
+
* snapshot runs the selection inside `asServerSnapshot`, so a component hydrates with the routes
|
|
3342
|
+
* that the server render selected.
|
|
3343
|
+
*/
|
|
3344
|
+
function useSelected(select) {
|
|
3345
|
+
const $ = c(14);
|
|
3346
|
+
const { product, stores } = useInternals().runtime;
|
|
3347
|
+
const { availability, flags, session } = stores;
|
|
3348
|
+
let t0;
|
|
3349
|
+
if ($[0] !== availability || $[1] !== flags || $[2] !== session) {
|
|
3350
|
+
t0 = (changed) => {
|
|
3351
|
+
const stops = [
|
|
3352
|
+
availability,
|
|
3353
|
+
flags,
|
|
3354
|
+
session
|
|
3355
|
+
].map((store) => store.subscribe(changed));
|
|
3356
|
+
return () => {
|
|
3357
|
+
for (const stop of stops) stop();
|
|
3358
|
+
};
|
|
3359
|
+
};
|
|
3360
|
+
$[0] = availability;
|
|
3361
|
+
$[1] = flags;
|
|
3362
|
+
$[2] = session;
|
|
3363
|
+
$[3] = t0;
|
|
3364
|
+
} else t0 = $[3];
|
|
3365
|
+
const subscribe = t0;
|
|
3366
|
+
let t1;
|
|
3367
|
+
if ($[4] !== availability || $[5] !== flags || $[6] !== product.routes || $[7] !== select || $[8] !== session) {
|
|
3368
|
+
t1 = () => select(product.routes, {
|
|
3369
|
+
availability,
|
|
3370
|
+
flags,
|
|
3371
|
+
session
|
|
3372
|
+
}).map(_temp$4).join(" ");
|
|
3373
|
+
$[4] = availability;
|
|
3374
|
+
$[5] = flags;
|
|
3375
|
+
$[6] = product.routes;
|
|
3376
|
+
$[7] = select;
|
|
3377
|
+
$[8] = session;
|
|
3378
|
+
$[9] = t1;
|
|
3379
|
+
} else t1 = $[9];
|
|
3380
|
+
const read = t1;
|
|
3381
|
+
let t2;
|
|
3382
|
+
if ($[10] !== read) {
|
|
3383
|
+
t2 = () => asServerSnapshot(read);
|
|
3384
|
+
$[10] = read;
|
|
3385
|
+
$[11] = t2;
|
|
3386
|
+
} else t2 = $[11];
|
|
3387
|
+
const t3 = useSyncExternalStore(subscribe, read, t2);
|
|
3388
|
+
let t4;
|
|
3389
|
+
if ($[12] !== t3) {
|
|
3390
|
+
t4 = new Set(t3.split(" "));
|
|
3391
|
+
$[12] = t3;
|
|
3392
|
+
$[13] = t4;
|
|
3393
|
+
} else t4 = $[13];
|
|
3394
|
+
return t4;
|
|
3395
|
+
}
|
|
3396
|
+
function _temp$4(t0) {
|
|
3397
|
+
const { id } = t0;
|
|
3398
|
+
return id;
|
|
3399
|
+
}
|
|
3400
|
+
/**
|
|
3401
|
+
* Returns the ids of the routes a menu may list, and renders again when a plugin turns on or off,
|
|
3402
|
+
* a flag changes or the session changes.
|
|
3403
|
+
*/
|
|
3404
|
+
function useListed() {
|
|
3405
|
+
return useSelected(listedRoutesOf);
|
|
3406
|
+
}
|
|
3407
|
+
/**
|
|
3408
|
+
* Returns the ids of the routes the person may open, with or without a menu entry, and renders
|
|
3409
|
+
* again when a plugin turns on or off, a flag changes or the session changes.
|
|
3410
|
+
*/
|
|
3411
|
+
function useOpenable() {
|
|
3412
|
+
return useSelected(openableRoutesOf);
|
|
3413
|
+
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Returns true where a menu lists a route the person may open.
|
|
3416
|
+
*/
|
|
3417
|
+
function lists(menu, product, listed) {
|
|
3418
|
+
return product.routes.some(({ id, navigation }) => {
|
|
3419
|
+
return navigation?.menu === menu && listed.has(id);
|
|
3420
|
+
});
|
|
3421
|
+
}
|
|
3422
|
+
/**
|
|
3423
|
+
* Returns the menus plugins declare that list a route the person may open, in install order.
|
|
3424
|
+
*
|
|
3425
|
+
* @param product - The resolved product.
|
|
3426
|
+
* @param listed - The ids of the routes a menu may list.
|
|
3427
|
+
* @param plugin - The plugin whose menus count. Without it, the menus of every plugin but the host
|
|
3428
|
+
* count.
|
|
3429
|
+
*/
|
|
3430
|
+
function menusOf(product, listed, plugin) {
|
|
3431
|
+
return product.menus.filter((menu) => {
|
|
3432
|
+
return (plugin === void 0 ? menu.plugin !== hostContract.pluginId : menu.plugin === plugin) && lists(menu.id, product, listed);
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
/**
|
|
3436
|
+
* Returns true where the settings menu lists a page the person may open.
|
|
3437
|
+
*
|
|
3438
|
+
* @param product - The resolved product.
|
|
3439
|
+
* @param listed - The ids of the routes a menu may list.
|
|
3440
|
+
* @param plugin - The plugin whose settings pages count. Without it, the pages of every plugin
|
|
3441
|
+
* count.
|
|
3442
|
+
*/
|
|
3443
|
+
function settingsListed(product, listed, plugin) {
|
|
3444
|
+
return product.routes.some(({ id, navigation, plugin: owner }) => {
|
|
3445
|
+
return navigation?.menu === hostContract.menus.settings.id && listed.has(id) && (plugin === void 0 || owner === plugin);
|
|
3446
|
+
});
|
|
3447
|
+
}
|
|
3448
|
+
//#endregion
|
|
3449
|
+
//#region src/commands/pages.ts
|
|
3450
|
+
/**
|
|
3451
|
+
* Imports the page index of every installed plugin the first time the palette opens.
|
|
3452
|
+
*
|
|
3453
|
+
* @remarks
|
|
3454
|
+
* The catalogue's index lists 183 pages, so the palette imports the indexes only when a person
|
|
3455
|
+
* opens it. The host imports each index once per product. The host reports an index as
|
|
3456
|
+
* `index-failed` under its plugin when its import fails or its module does not export exactly one
|
|
3457
|
+
* function. The palette then lists none of that plugin's indexed pages.
|
|
3458
|
+
*/
|
|
3459
|
+
/**
|
|
3460
|
+
* The separator i18next reads between a namespace and a key.
|
|
3461
|
+
*/
|
|
3462
|
+
const SEPARATOR = ":";
|
|
3463
|
+
/**
|
|
3464
|
+
* The pages of each product's indexes, imported once per product.
|
|
3465
|
+
*/
|
|
3466
|
+
const INDEXES = /* @__PURE__ */ new WeakMap();
|
|
3467
|
+
/**
|
|
3468
|
+
* Returns a key without its namespace prefix.
|
|
3469
|
+
*
|
|
3470
|
+
* @param key - A key of a plugin's catalogue, or `<namespace>:<key>` of another catalogue.
|
|
3471
|
+
*/
|
|
3472
|
+
function bareKeyOf(key) {
|
|
3473
|
+
return key.slice(key.indexOf(SEPARATOR) + 1);
|
|
3474
|
+
}
|
|
3475
|
+
/**
|
|
3476
|
+
* Returns the namespaces that prefix the keys of the pages, without repeats.
|
|
3477
|
+
*/
|
|
3478
|
+
function namespacesOf(pages) {
|
|
3479
|
+
const named = pages.flatMap(({ entry }) => [entry.label, entry.group ?? ""]).filter((key) => key.includes(SEPARATOR)).map((key) => key.slice(0, key.indexOf(SEPARATOR)));
|
|
3480
|
+
return [...new Set(named)];
|
|
3481
|
+
}
|
|
3482
|
+
/**
|
|
3483
|
+
* Returns the pages one plugin's index lists.
|
|
3484
|
+
*
|
|
3485
|
+
* @throws {@link Error} When the index's module does not export exactly one function.
|
|
3486
|
+
*/
|
|
3487
|
+
async function indexOf(pluginId, load) {
|
|
3488
|
+
const functions = Object.values(await load()).filter((value) => typeof value === "function");
|
|
3489
|
+
const [list] = functions;
|
|
3490
|
+
if (functions.length !== 1 || list === void 0) throw new Error(`The page index of ${pluginId} exports ${String(functions.length)} functions. A page index must export exactly one function.`);
|
|
3491
|
+
return list().map((entry) => ({
|
|
3492
|
+
entry,
|
|
3493
|
+
plugin: pluginId
|
|
3494
|
+
}));
|
|
3495
|
+
}
|
|
3496
|
+
/**
|
|
3497
|
+
* Imports every installed plugin's index and loads the namespaces that prefix its pages' keys.
|
|
3498
|
+
*
|
|
3499
|
+
* @remarks
|
|
3500
|
+
* The function reports each index that fails, and lists the pages of the others.
|
|
3501
|
+
* @param product - The product, with each plugin's manifest.
|
|
3502
|
+
* @param report - The host's report.
|
|
3503
|
+
* @param load - Loads the catalogues of the namespaces that prefix the keys.
|
|
3504
|
+
* @returns The pages of every index that loaded, in install order.
|
|
3505
|
+
*/
|
|
3506
|
+
async function indexedPagesOf(product, report, load) {
|
|
3507
|
+
const lists = Object.entries(product.manifests).map(async ([pluginId, { code }]) => {
|
|
3508
|
+
if (code.pages === void 0) return [];
|
|
3509
|
+
try {
|
|
3510
|
+
return await indexOf(pluginId, code.pages);
|
|
3511
|
+
} catch (error) {
|
|
3512
|
+
report({
|
|
3513
|
+
error,
|
|
3514
|
+
kind: "index-failed",
|
|
3515
|
+
plugin: pluginId
|
|
3516
|
+
});
|
|
3517
|
+
return [];
|
|
3518
|
+
}
|
|
3519
|
+
});
|
|
3520
|
+
const pages = (await Promise.all(lists)).flat();
|
|
3521
|
+
await load(namespacesOf(pages));
|
|
3522
|
+
return pages;
|
|
3523
|
+
}
|
|
3524
|
+
/**
|
|
3525
|
+
* Returns the pages of a product's indexes, imported on the first call for the product.
|
|
3526
|
+
*/
|
|
3527
|
+
function cachedPagesOf(product, report, load) {
|
|
3528
|
+
const known = INDEXES.get(product) ?? indexedPagesOf(product, report, load);
|
|
3529
|
+
INDEXES.set(product, known);
|
|
3530
|
+
return known;
|
|
3531
|
+
}
|
|
3532
|
+
/**
|
|
3533
|
+
* Returns the pages of every installed plugin's index once the palette has opened, and none
|
|
3534
|
+
* before.
|
|
3535
|
+
*
|
|
3536
|
+
* @param open - True while the palette is open.
|
|
3537
|
+
*/
|
|
3538
|
+
function usePageIndex(open) {
|
|
3539
|
+
const $ = c(10);
|
|
3540
|
+
const { runtime } = useInternals();
|
|
3541
|
+
const { i18n } = useTranslation("host");
|
|
3542
|
+
let t0;
|
|
3543
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3544
|
+
t0 = [];
|
|
3545
|
+
$[0] = t0;
|
|
3546
|
+
} else t0 = $[0];
|
|
3547
|
+
const [pages, setPages] = useState(t0);
|
|
3548
|
+
let t1;
|
|
3549
|
+
if ($[1] !== i18n || $[2] !== open || $[3] !== runtime.product || $[4] !== runtime.report) {
|
|
3550
|
+
t1 = () => {
|
|
3551
|
+
let current = true;
|
|
3552
|
+
const listed = async () => {
|
|
3553
|
+
const loaded = await cachedPagesOf(runtime.product, runtime.report, (namespaces) => i18n.loadNamespaces([...namespaces]));
|
|
3554
|
+
if (current) setPages(loaded);
|
|
3555
|
+
};
|
|
3556
|
+
if (open) listed();
|
|
3557
|
+
return () => {
|
|
3558
|
+
current = false;
|
|
3559
|
+
};
|
|
3560
|
+
};
|
|
3561
|
+
$[1] = i18n;
|
|
3562
|
+
$[2] = open;
|
|
3563
|
+
$[3] = runtime.product;
|
|
3564
|
+
$[4] = runtime.report;
|
|
3565
|
+
$[5] = t1;
|
|
3566
|
+
} else t1 = $[5];
|
|
3567
|
+
let t2;
|
|
3568
|
+
if ($[6] !== i18n || $[7] !== open || $[8] !== runtime) {
|
|
3569
|
+
t2 = [
|
|
3570
|
+
i18n,
|
|
3571
|
+
open,
|
|
3572
|
+
runtime
|
|
3573
|
+
];
|
|
3574
|
+
$[6] = i18n;
|
|
3575
|
+
$[7] = open;
|
|
3576
|
+
$[8] = runtime;
|
|
3577
|
+
$[9] = t2;
|
|
3578
|
+
} else t2 = $[9];
|
|
3579
|
+
useEffect(t1, t2);
|
|
3580
|
+
return pages;
|
|
3581
|
+
}
|
|
3582
|
+
//#endregion
|
|
3583
|
+
//#region src/commands/choices.ts
|
|
2963
3584
|
/**
|
|
2964
3585
|
* Returns true for a command the palette lists: it is enabled, takes no arguments and resolves
|
|
2965
3586
|
* with no result.
|
|
@@ -2968,224 +3589,342 @@ function isListed({ command, enabled }) {
|
|
|
2968
3589
|
return enabled && !command.takesArguments && !command.returnsResult;
|
|
2969
3590
|
}
|
|
2970
3591
|
/**
|
|
2971
|
-
* Returns
|
|
2972
|
-
*
|
|
3592
|
+
* Returns a word of a plugin's catalogue, or of the catalogue its key names, and the key without a
|
|
3593
|
+
* namespace where no catalogue states the word.
|
|
3594
|
+
*/
|
|
3595
|
+
function worded(words, plugin, key) {
|
|
3596
|
+
return words.t(plugin, key, { defaultValue: bareKeyOf(key) });
|
|
3597
|
+
}
|
|
3598
|
+
/**
|
|
3599
|
+
* Returns a row per entry of every menu but the main one that the person may open: under its
|
|
3600
|
+
* plugin's name, or under the settings menu's heading for a settings page.
|
|
3601
|
+
*
|
|
3602
|
+
* @remarks
|
|
3603
|
+
* A row matches its menu's heading beside its text, and the palette lists it while a query
|
|
3604
|
+
* matches it.
|
|
3605
|
+
*/
|
|
3606
|
+
function menuRows({ go, hrefOf, openable, product, words }) {
|
|
3607
|
+
return product.menus.filter(({ id }) => id !== hostContract.menus.main.id).flatMap((menu) => {
|
|
3608
|
+
const heading = words.t(menu.plugin, menu.label);
|
|
3609
|
+
return product.routes.flatMap((route) => {
|
|
3610
|
+
const { navigation } = route;
|
|
3611
|
+
if (navigation?.menu !== menu.id || !openable.has(route.id)) return [];
|
|
3612
|
+
const href = hrefOf(route.id);
|
|
3613
|
+
return [{
|
|
3614
|
+
action: {
|
|
3615
|
+
group: menu.plugin === HOST ? heading : words.t(route.plugin, "plugin.name"),
|
|
3616
|
+
keywords: heading,
|
|
3617
|
+
label: words.t(route.plugin, navigation.label),
|
|
3618
|
+
searchOnly: true,
|
|
3619
|
+
value: `route:${route.id}`
|
|
3620
|
+
},
|
|
3621
|
+
run: () => {
|
|
3622
|
+
go(href);
|
|
3623
|
+
}
|
|
3624
|
+
}];
|
|
3625
|
+
});
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
/**
|
|
3629
|
+
* Returns a row per page of every plugin's index that the person may open, under its plugin's name.
|
|
3630
|
+
*
|
|
3631
|
+
* @remarks
|
|
3632
|
+
* A row matches its group's heading beside its title, and the palette lists it while a query
|
|
3633
|
+
* matches it.
|
|
3634
|
+
*/
|
|
3635
|
+
function pageRows({ go, hrefOf, openable, words }, pages) {
|
|
3636
|
+
return pages.flatMap(({ entry, plugin }) => {
|
|
3637
|
+
if (!openable.has(entry.route.id)) return [];
|
|
3638
|
+
const href = hrefOf(entry.route.id, entry.params);
|
|
3639
|
+
return [{
|
|
3640
|
+
action: {
|
|
3641
|
+
group: words.t(plugin, "plugin.name"),
|
|
3642
|
+
keywords: entry.group === void 0 ? void 0 : worded(words, plugin, entry.group),
|
|
3643
|
+
label: worded(words, plugin, entry.label),
|
|
3644
|
+
searchOnly: true,
|
|
3645
|
+
value: `page:${href}`
|
|
3646
|
+
},
|
|
3647
|
+
run: () => {
|
|
3648
|
+
go(href);
|
|
3649
|
+
}
|
|
3650
|
+
}];
|
|
3651
|
+
});
|
|
3652
|
+
}
|
|
3653
|
+
/**
|
|
3654
|
+
* Returns the rows the palette lists: each command a key could run under its plugin's name, each
|
|
3655
|
+
* page of the main menu under `Go to`, then the entries of the other menus and the pages of every
|
|
3656
|
+
* plugin's index.
|
|
2973
3657
|
*
|
|
2974
3658
|
* @remarks
|
|
2975
3659
|
* A command matches its catalogue's `keywords.<label>` beside its label, where the catalogue
|
|
2976
|
-
* states it.
|
|
3660
|
+
* states it. The indexes are imported the first time the palette opens.
|
|
3661
|
+
* @param runtime - The host's runtime, which reports a command that fails.
|
|
3662
|
+
* @param open - True while the palette is open.
|
|
2977
3663
|
*/
|
|
2978
|
-
function useChoices(runtime) {
|
|
2979
|
-
const $ = c(
|
|
3664
|
+
function useChoices(runtime, open) {
|
|
3665
|
+
const $ = c(14);
|
|
2980
3666
|
const commands = useCommands();
|
|
2981
3667
|
const entries = useNavigation();
|
|
2982
3668
|
const navigate = useNavigate();
|
|
3669
|
+
const map = useRouteMap();
|
|
3670
|
+
const openable = useOpenable();
|
|
3671
|
+
const pages = usePageIndex(open);
|
|
2983
3672
|
const { i18n, t } = useTranslation("host");
|
|
2984
3673
|
let t0;
|
|
2985
|
-
if ($[0] !== commands || $[1] !== entries || $[2] !== i18n || $[3] !==
|
|
3674
|
+
if ($[0] !== commands || $[1] !== entries || $[2] !== i18n || $[3] !== map || $[4] !== navigate || $[5] !== openable || $[6] !== pages || $[7] !== runtime || $[8] !== t) {
|
|
2986
3675
|
const words = pluginWordsOf(i18n);
|
|
2987
3676
|
let t1;
|
|
2988
|
-
if ($[
|
|
2989
|
-
t1 = (
|
|
2990
|
-
|
|
3677
|
+
if ($[10] !== navigate) {
|
|
3678
|
+
t1 = (href) => {
|
|
3679
|
+
navigate({ to: href });
|
|
3680
|
+
};
|
|
3681
|
+
$[10] = navigate;
|
|
3682
|
+
$[11] = t1;
|
|
3683
|
+
} else t1 = $[11];
|
|
3684
|
+
let t2;
|
|
3685
|
+
if ($[12] !== map) {
|
|
3686
|
+
t2 = (routeId, params) => routeHref(map, routeId, params);
|
|
3687
|
+
$[12] = map;
|
|
3688
|
+
$[13] = t2;
|
|
3689
|
+
} else t2 = $[13];
|
|
3690
|
+
const rows = {
|
|
3691
|
+
go: t1,
|
|
3692
|
+
hrefOf: t2,
|
|
3693
|
+
openable,
|
|
3694
|
+
product: runtime.product,
|
|
3695
|
+
words
|
|
3696
|
+
};
|
|
3697
|
+
t0 = [
|
|
3698
|
+
...commands.filter(_temp$3).map((status_0) => {
|
|
3699
|
+
const { id, label, plugin } = status_0.command;
|
|
3700
|
+
const keywords = `keywords.${label}`;
|
|
3701
|
+
return {
|
|
3702
|
+
action: {
|
|
3703
|
+
group: words.t(plugin, "plugin.name"),
|
|
3704
|
+
keywords: words.exists(plugin, keywords) ? words.t(plugin, keywords) : void 0,
|
|
3705
|
+
label: status_0.label,
|
|
3706
|
+
shortcut: status_0.keys,
|
|
3707
|
+
value: id
|
|
3708
|
+
},
|
|
3709
|
+
run: () => {
|
|
3710
|
+
runReporting(status_0, runtime, t("commands.failed", { label: status_0.label }));
|
|
3711
|
+
}
|
|
3712
|
+
};
|
|
3713
|
+
}),
|
|
3714
|
+
...entries.map((t3) => {
|
|
3715
|
+
const { href: href_0, label: label_0, routeId: routeId_0 } = t3;
|
|
2991
3716
|
return {
|
|
2992
3717
|
action: {
|
|
2993
3718
|
group: t("palette.goTo"),
|
|
2994
3719
|
label: label_0,
|
|
2995
|
-
value: `route:${
|
|
3720
|
+
value: `route:${routeId_0}`
|
|
2996
3721
|
},
|
|
2997
3722
|
run: () => {
|
|
2998
|
-
|
|
3723
|
+
rows.go(href_0);
|
|
2999
3724
|
}
|
|
3000
3725
|
};
|
|
3001
|
-
}
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
} else t1 = $[9];
|
|
3006
|
-
t0 = [...commands.filter(_temp$2).map((status_0) => {
|
|
3007
|
-
const { id, label, plugin } = status_0.command;
|
|
3008
|
-
const keywords = `keywords.${label}`;
|
|
3009
|
-
return {
|
|
3010
|
-
action: {
|
|
3011
|
-
group: words.t(plugin, "plugin.name"),
|
|
3012
|
-
keywords: words.exists(plugin, keywords) ? words.t(plugin, keywords) : void 0,
|
|
3013
|
-
label: status_0.label,
|
|
3014
|
-
shortcut: status_0.keys,
|
|
3015
|
-
value: id
|
|
3016
|
-
},
|
|
3017
|
-
run: () => {
|
|
3018
|
-
runReporting(status_0, runtime, t("commands.failed", { label: status_0.label }));
|
|
3019
|
-
}
|
|
3020
|
-
};
|
|
3021
|
-
}), ...entries.map(t1)];
|
|
3726
|
+
}),
|
|
3727
|
+
...menuRows(rows),
|
|
3728
|
+
...pageRows(rows, pages)
|
|
3729
|
+
];
|
|
3022
3730
|
$[0] = commands;
|
|
3023
3731
|
$[1] = entries;
|
|
3024
3732
|
$[2] = i18n;
|
|
3025
|
-
$[3] =
|
|
3026
|
-
$[4] =
|
|
3027
|
-
$[5] =
|
|
3028
|
-
$[6] =
|
|
3029
|
-
|
|
3733
|
+
$[3] = map;
|
|
3734
|
+
$[4] = navigate;
|
|
3735
|
+
$[5] = openable;
|
|
3736
|
+
$[6] = pages;
|
|
3737
|
+
$[7] = runtime;
|
|
3738
|
+
$[8] = t;
|
|
3739
|
+
$[9] = t0;
|
|
3740
|
+
} else t0 = $[9];
|
|
3030
3741
|
return t0;
|
|
3031
3742
|
}
|
|
3032
|
-
function _temp$
|
|
3743
|
+
function _temp$3(status) {
|
|
3033
3744
|
return isListed(status);
|
|
3034
3745
|
}
|
|
3746
|
+
//#endregion
|
|
3747
|
+
//#region src/commands/palette.tsx
|
|
3748
|
+
/**
|
|
3749
|
+
* The chord that opens the palette. The build refuses a command that binds it.
|
|
3750
|
+
*/
|
|
3751
|
+
const PALETTE = "Mod+K";
|
|
3035
3752
|
/**
|
|
3036
3753
|
* Renders the palette in a dialog that `Mod+K` opens and closes.
|
|
3037
3754
|
*
|
|
3038
3755
|
* @remarks
|
|
3039
|
-
*
|
|
3040
|
-
*
|
|
3041
|
-
*
|
|
3756
|
+
* The query bar renders a search glyph before the field, and a clear control after it while the
|
|
3757
|
+
* field has a query. A chosen row closes the dialog, then runs its command or navigates to its
|
|
3758
|
+
* page. The palette renders in the `overlay` region at the end of the frame, so the dialog needs
|
|
3759
|
+
* no portal. A server renders the dialog while it is closed. `HostRoot` provides the open state,
|
|
3760
|
+
* so a control in the frame opens the same dialog.
|
|
3042
3761
|
*/
|
|
3043
3762
|
function CommandPalette() {
|
|
3044
|
-
const $ = c(
|
|
3045
|
-
const
|
|
3763
|
+
const $ = c(46);
|
|
3764
|
+
const { open, setOpen } = usePalette();
|
|
3765
|
+
const choices = useChoices(useInternals().runtime, open);
|
|
3766
|
+
const { t } = useTranslation("host");
|
|
3046
3767
|
let t0;
|
|
3047
|
-
if ($[0]
|
|
3048
|
-
t0 =
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3768
|
+
if ($[0] !== setOpen) {
|
|
3769
|
+
t0 = () => {
|
|
3770
|
+
setOpen(_temp$2);
|
|
3771
|
+
};
|
|
3772
|
+
$[0] = setOpen;
|
|
3773
|
+
$[1] = t0;
|
|
3774
|
+
} else t0 = $[1];
|
|
3775
|
+
useHotkey(PALETTE, t0);
|
|
3052
3776
|
let t1;
|
|
3053
|
-
if ($[
|
|
3054
|
-
t1 =
|
|
3055
|
-
$[
|
|
3056
|
-
$[
|
|
3057
|
-
} else t1 = $[
|
|
3058
|
-
const choices = useChoices(t1.runtime);
|
|
3059
|
-
const { t } = useTranslation("host");
|
|
3777
|
+
if ($[2] !== t) {
|
|
3778
|
+
t1 = t("palette.label");
|
|
3779
|
+
$[2] = t;
|
|
3780
|
+
$[3] = t1;
|
|
3781
|
+
} else t1 = $[3];
|
|
3060
3782
|
let t2;
|
|
3061
|
-
if ($[
|
|
3062
|
-
t2 = () => {
|
|
3063
|
-
|
|
3783
|
+
if ($[4] !== setOpen) {
|
|
3784
|
+
t2 = (t3) => {
|
|
3785
|
+
const { open: next } = t3;
|
|
3786
|
+
setOpen(next);
|
|
3064
3787
|
};
|
|
3065
|
-
$[
|
|
3066
|
-
|
|
3067
|
-
|
|
3788
|
+
$[4] = setOpen;
|
|
3789
|
+
$[5] = t2;
|
|
3790
|
+
} else t2 = $[5];
|
|
3068
3791
|
let t3;
|
|
3069
|
-
if ($[4] !== t) {
|
|
3070
|
-
t3 = t("palette.label");
|
|
3071
|
-
$[4] = t;
|
|
3072
|
-
$[5] = t3;
|
|
3073
|
-
} else t3 = $[5];
|
|
3074
|
-
let t4;
|
|
3075
3792
|
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3793
|
+
t3 = /* @__PURE__ */ jsx(Dialog.Backdrop, {});
|
|
3794
|
+
$[6] = t3;
|
|
3795
|
+
} else t3 = $[6];
|
|
3796
|
+
let t4;
|
|
3797
|
+
if ($[7] !== choices) {
|
|
3798
|
+
t4 = choices.map(_temp2);
|
|
3799
|
+
$[7] = choices;
|
|
3800
|
+
$[8] = t4;
|
|
3801
|
+
} else t4 = $[8];
|
|
3082
3802
|
let t5;
|
|
3083
|
-
if ($[
|
|
3084
|
-
t5 =
|
|
3085
|
-
$[
|
|
3086
|
-
|
|
3803
|
+
if ($[9] !== t) {
|
|
3804
|
+
t5 = t("palette.commands");
|
|
3805
|
+
$[9] = t;
|
|
3806
|
+
$[10] = t5;
|
|
3807
|
+
} else t5 = $[10];
|
|
3087
3808
|
let t6;
|
|
3088
|
-
if ($[
|
|
3089
|
-
t6 =
|
|
3090
|
-
$[
|
|
3091
|
-
$[
|
|
3092
|
-
} else t6 = $[
|
|
3809
|
+
if ($[11] !== t) {
|
|
3810
|
+
t6 = (matches) => t("palette.count", { count: matches });
|
|
3811
|
+
$[11] = t;
|
|
3812
|
+
$[12] = t6;
|
|
3813
|
+
} else t6 = $[12];
|
|
3093
3814
|
let t7;
|
|
3094
|
-
if ($[
|
|
3095
|
-
t7 =
|
|
3096
|
-
$[10] = t;
|
|
3097
|
-
$[11] = t7;
|
|
3098
|
-
} else t7 = $[11];
|
|
3099
|
-
let t8;
|
|
3100
|
-
if ($[12] !== t) {
|
|
3101
|
-
t8 = (matches) => t("palette.count", { count: matches });
|
|
3102
|
-
$[12] = t;
|
|
3103
|
-
$[13] = t8;
|
|
3104
|
-
} else t8 = $[13];
|
|
3105
|
-
let t9;
|
|
3106
|
-
if ($[14] !== choices) {
|
|
3107
|
-
t9 = (value) => {
|
|
3815
|
+
if ($[13] !== choices || $[14] !== setOpen) {
|
|
3816
|
+
t7 = (value) => {
|
|
3108
3817
|
setOpen(false);
|
|
3109
3818
|
for (const choice of choices) if (choice.action.value === value) choice.run();
|
|
3110
3819
|
};
|
|
3111
|
-
$[
|
|
3112
|
-
$[
|
|
3113
|
-
|
|
3114
|
-
|
|
3820
|
+
$[13] = choices;
|
|
3821
|
+
$[14] = setOpen;
|
|
3822
|
+
$[15] = t7;
|
|
3823
|
+
} else t7 = $[15];
|
|
3824
|
+
let t8;
|
|
3115
3825
|
if ($[16] !== t) {
|
|
3116
|
-
|
|
3826
|
+
t8 = t("palette.search");
|
|
3117
3827
|
$[16] = t;
|
|
3118
|
-
$[17] =
|
|
3119
|
-
} else
|
|
3828
|
+
$[17] = t8;
|
|
3829
|
+
} else t8 = $[17];
|
|
3830
|
+
let t9;
|
|
3831
|
+
if ($[18] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3832
|
+
t9 = /* @__PURE__ */ jsx(SearchIcon, { size: "100%" });
|
|
3833
|
+
$[18] = t9;
|
|
3834
|
+
} else t9 = $[18];
|
|
3835
|
+
let t10;
|
|
3836
|
+
if ($[19] !== t) {
|
|
3837
|
+
t10 = t("palette.search");
|
|
3838
|
+
$[19] = t;
|
|
3839
|
+
$[20] = t10;
|
|
3840
|
+
} else t10 = $[20];
|
|
3120
3841
|
let t11;
|
|
3121
|
-
if ($[
|
|
3122
|
-
t11 = t("
|
|
3123
|
-
$[
|
|
3124
|
-
$[
|
|
3125
|
-
} else t11 = $[
|
|
3842
|
+
if ($[21] !== t) {
|
|
3843
|
+
t11 = t("frame.clear");
|
|
3844
|
+
$[21] = t;
|
|
3845
|
+
$[22] = t11;
|
|
3846
|
+
} else t11 = $[22];
|
|
3126
3847
|
let t12;
|
|
3127
|
-
if ($[
|
|
3128
|
-
t12 = /* @__PURE__ */ jsx(
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
});
|
|
3132
|
-
$[20] = t10;
|
|
3133
|
-
$[21] = t11;
|
|
3134
|
-
$[22] = t12;
|
|
3135
|
-
} else t12 = $[22];
|
|
3848
|
+
if ($[23] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3849
|
+
t12 = /* @__PURE__ */ jsx(XIcon, { size: "100%" });
|
|
3850
|
+
$[23] = t12;
|
|
3851
|
+
} else t12 = $[23];
|
|
3136
3852
|
let t13;
|
|
3137
|
-
if ($[
|
|
3138
|
-
t13 =
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
if ($[25] !== t13) {
|
|
3144
|
-
t14 = /* @__PURE__ */ jsx(Command.List, { children: /* @__PURE__ */ jsx(Command.Empty, { children: t13 }) });
|
|
3853
|
+
if ($[24] !== t11) {
|
|
3854
|
+
t13 = /* @__PURE__ */ jsx(Command.Clear, {
|
|
3855
|
+
"aria-label": t11,
|
|
3856
|
+
children: t12
|
|
3857
|
+
});
|
|
3858
|
+
$[24] = t11;
|
|
3145
3859
|
$[25] = t13;
|
|
3146
|
-
|
|
3147
|
-
|
|
3860
|
+
} else t13 = $[25];
|
|
3861
|
+
let t14;
|
|
3862
|
+
if ($[26] !== t10 || $[27] !== t13 || $[28] !== t8) {
|
|
3863
|
+
t14 = /* @__PURE__ */ jsx(Command.Input, {
|
|
3864
|
+
"aria-label": t8,
|
|
3865
|
+
indicator: t9,
|
|
3866
|
+
placeholder: t10,
|
|
3867
|
+
children: t13
|
|
3868
|
+
});
|
|
3869
|
+
$[26] = t10;
|
|
3870
|
+
$[27] = t13;
|
|
3871
|
+
$[28] = t8;
|
|
3872
|
+
$[29] = t14;
|
|
3873
|
+
} else t14 = $[29];
|
|
3148
3874
|
let t15;
|
|
3149
|
-
if ($[
|
|
3150
|
-
t15 =
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
onRun: t9,
|
|
3155
|
-
children: [t12, t14]
|
|
3156
|
-
}) }) });
|
|
3157
|
-
$[27] = t12;
|
|
3158
|
-
$[28] = t14;
|
|
3159
|
-
$[29] = t6;
|
|
3160
|
-
$[30] = t7;
|
|
3161
|
-
$[31] = t8;
|
|
3162
|
-
$[32] = t9;
|
|
3163
|
-
$[33] = t15;
|
|
3164
|
-
} else t15 = $[33];
|
|
3875
|
+
if ($[30] !== t) {
|
|
3876
|
+
t15 = t("palette.none");
|
|
3877
|
+
$[30] = t;
|
|
3878
|
+
$[31] = t15;
|
|
3879
|
+
} else t15 = $[31];
|
|
3165
3880
|
let t16;
|
|
3166
|
-
if ($[
|
|
3167
|
-
t16 = /* @__PURE__ */
|
|
3168
|
-
|
|
3169
|
-
|
|
3881
|
+
if ($[32] !== t15) {
|
|
3882
|
+
t16 = /* @__PURE__ */ jsx(Command.List, { children: /* @__PURE__ */ jsx(Command.Empty, { children: t15 }) });
|
|
3883
|
+
$[32] = t15;
|
|
3884
|
+
$[33] = t16;
|
|
3885
|
+
} else t16 = $[33];
|
|
3886
|
+
let t17;
|
|
3887
|
+
if ($[34] !== t14 || $[35] !== t16 || $[36] !== t4 || $[37] !== t5 || $[38] !== t6 || $[39] !== t7) {
|
|
3888
|
+
t17 = /* @__PURE__ */ jsx(Dialog.Positioner, { children: /* @__PURE__ */ jsx(Dialog.Content, { children: /* @__PURE__ */ jsxs(Command.Root, {
|
|
3889
|
+
actions: t4,
|
|
3890
|
+
"aria-label": t5,
|
|
3891
|
+
count: t6,
|
|
3892
|
+
onRun: t7,
|
|
3893
|
+
children: [t14, t16]
|
|
3894
|
+
}) }) });
|
|
3895
|
+
$[34] = t14;
|
|
3896
|
+
$[35] = t16;
|
|
3897
|
+
$[36] = t4;
|
|
3898
|
+
$[37] = t5;
|
|
3899
|
+
$[38] = t6;
|
|
3900
|
+
$[39] = t7;
|
|
3901
|
+
$[40] = t17;
|
|
3902
|
+
} else t17 = $[40];
|
|
3903
|
+
let t18;
|
|
3904
|
+
if ($[41] !== open || $[42] !== t1 || $[43] !== t17 || $[44] !== t2) {
|
|
3905
|
+
t18 = /* @__PURE__ */ jsxs(Dialog.Root, {
|
|
3906
|
+
"aria-label": t1,
|
|
3907
|
+
onOpenChange: t2,
|
|
3170
3908
|
open,
|
|
3171
3909
|
placement: "top",
|
|
3172
3910
|
scrollBehavior: "inside",
|
|
3173
3911
|
size: "lg",
|
|
3174
3912
|
variant: "plain",
|
|
3175
|
-
children: [
|
|
3913
|
+
children: [t3, t17]
|
|
3176
3914
|
});
|
|
3177
|
-
$[
|
|
3178
|
-
$[
|
|
3179
|
-
$[
|
|
3180
|
-
$[
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3915
|
+
$[41] = open;
|
|
3916
|
+
$[42] = t1;
|
|
3917
|
+
$[43] = t17;
|
|
3918
|
+
$[44] = t2;
|
|
3919
|
+
$[45] = t18;
|
|
3920
|
+
} else t18 = $[45];
|
|
3921
|
+
return t18;
|
|
3922
|
+
}
|
|
3923
|
+
function _temp2(t0) {
|
|
3185
3924
|
const { action } = t0;
|
|
3186
3925
|
return action;
|
|
3187
3926
|
}
|
|
3188
|
-
function
|
|
3927
|
+
function _temp$2(shown) {
|
|
3189
3928
|
return !shown;
|
|
3190
3929
|
}
|
|
3191
3930
|
//#endregion
|
|
@@ -3238,66 +3977,80 @@ function _temp$1(toast) {
|
|
|
3238
3977
|
//#endregion
|
|
3239
3978
|
//#region src/parts/root.tsx
|
|
3240
3979
|
/**
|
|
3980
|
+
* Renders the root route's component: the `layout` region around the product's frame, and the
|
|
3981
|
+
* `overlay` region after it with the palette and the toast region.
|
|
3982
|
+
*/
|
|
3983
|
+
/**
|
|
3241
3984
|
* Renders the product's frame inside the `layout` region, the `overlay` region after it, and the
|
|
3242
3985
|
* keys of the product's commands.
|
|
3243
3986
|
*
|
|
3244
3987
|
* @remarks
|
|
3245
3988
|
* The router renders the root route's component inside a catch boundary of its own, so the
|
|
3246
3989
|
* component renders the host's last boundary around the frame, and an error in the frame
|
|
3247
|
-
* renders the failure page and is reported with the target `host`.
|
|
3990
|
+
* renders the failure page and is reported with the target `host`. The component provides the
|
|
3991
|
+
* palette's open state, so a control in the frame opens the palette that `Mod+K` opens.
|
|
3248
3992
|
*/
|
|
3249
3993
|
function HostRoot(t0) {
|
|
3250
|
-
const $ = c(
|
|
3994
|
+
const $ = c(12);
|
|
3251
3995
|
const { children } = t0;
|
|
3996
|
+
const { runtime } = useInternals();
|
|
3997
|
+
const [open, setOpen] = useState(false);
|
|
3252
3998
|
let t1;
|
|
3253
|
-
if ($[0]
|
|
3254
|
-
t1 = {
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3999
|
+
if ($[0] !== open) {
|
|
4000
|
+
t1 = {
|
|
4001
|
+
open,
|
|
4002
|
+
setOpen
|
|
4003
|
+
};
|
|
4004
|
+
$[0] = open;
|
|
4005
|
+
$[1] = t1;
|
|
4006
|
+
} else t1 = $[1];
|
|
3258
4007
|
let t2;
|
|
3259
|
-
if ($[
|
|
3260
|
-
t2 =
|
|
3261
|
-
$[1] = host;
|
|
3262
|
-
$[2] = t2;
|
|
3263
|
-
} else t2 = $[2];
|
|
3264
|
-
let t3;
|
|
3265
|
-
if ($[3] !== children) {
|
|
3266
|
-
t3 = /* @__PURE__ */ jsx(Slot, {
|
|
4008
|
+
if ($[2] !== children) {
|
|
4009
|
+
t2 = /* @__PURE__ */ jsx(Slot, {
|
|
3267
4010
|
slot: hostContract.slots.layout,
|
|
3268
4011
|
children
|
|
3269
4012
|
});
|
|
3270
|
-
$[
|
|
3271
|
-
$[
|
|
3272
|
-
} else
|
|
4013
|
+
$[2] = children;
|
|
4014
|
+
$[3] = t2;
|
|
4015
|
+
} else t2 = $[3];
|
|
4016
|
+
let t3;
|
|
3273
4017
|
let t4;
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
t4 = /* @__PURE__ */ jsxs(Slot, {
|
|
4018
|
+
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
|
4019
|
+
t3 = /* @__PURE__ */ jsxs(Slot, {
|
|
3277
4020
|
slot: hostContract.slots.overlay,
|
|
3278
4021
|
children: [/* @__PURE__ */ jsx(CommandPalette, {}), /* @__PURE__ */ jsx(HostToasts, {})]
|
|
3279
4022
|
});
|
|
3280
|
-
|
|
4023
|
+
t4 = /* @__PURE__ */ jsx(CommandKeys, {});
|
|
4024
|
+
$[4] = t3;
|
|
3281
4025
|
$[5] = t4;
|
|
3282
|
-
$[6] = t5;
|
|
3283
4026
|
} else {
|
|
4027
|
+
t3 = $[4];
|
|
3284
4028
|
t4 = $[5];
|
|
3285
|
-
t5 = $[6];
|
|
3286
4029
|
}
|
|
3287
|
-
let
|
|
3288
|
-
if ($[
|
|
3289
|
-
|
|
3290
|
-
runtime
|
|
4030
|
+
let t5;
|
|
4031
|
+
if ($[6] !== runtime || $[7] !== t2) {
|
|
4032
|
+
t5 = /* @__PURE__ */ jsxs(LastBoundary, {
|
|
4033
|
+
runtime,
|
|
3291
4034
|
children: [
|
|
4035
|
+
t2,
|
|
3292
4036
|
t3,
|
|
3293
|
-
t4
|
|
3294
|
-
t5
|
|
4037
|
+
t4
|
|
3295
4038
|
]
|
|
3296
4039
|
});
|
|
3297
|
-
$[
|
|
3298
|
-
$[
|
|
3299
|
-
$[
|
|
3300
|
-
} else
|
|
4040
|
+
$[6] = runtime;
|
|
4041
|
+
$[7] = t2;
|
|
4042
|
+
$[8] = t5;
|
|
4043
|
+
} else t5 = $[8];
|
|
4044
|
+
let t6;
|
|
4045
|
+
if ($[9] !== t1 || $[10] !== t5) {
|
|
4046
|
+
t6 = /* @__PURE__ */ jsx(PaletteContext, {
|
|
4047
|
+
value: t1,
|
|
4048
|
+
children: t5
|
|
4049
|
+
});
|
|
4050
|
+
$[9] = t1;
|
|
4051
|
+
$[10] = t5;
|
|
4052
|
+
$[11] = t6;
|
|
4053
|
+
} else t6 = $[11];
|
|
3301
4054
|
return t6;
|
|
3302
4055
|
}
|
|
3303
4056
|
//#endregion
|
|
@@ -3343,16 +4096,16 @@ function componentOf(module, routeId) {
|
|
|
3343
4096
|
* error page, and counts each error towards the page's quarantine.
|
|
3344
4097
|
*/
|
|
3345
4098
|
/**
|
|
3346
|
-
* Marks a route error component that has counted no error yet.
|
|
3347
|
-
*/
|
|
3348
|
-
const UNCOUNTED = Symbol("uncounted");
|
|
3349
|
-
/**
|
|
3350
4099
|
* Returns the error component of a plugin's route.
|
|
3351
4100
|
*
|
|
3352
4101
|
* @remarks
|
|
3353
|
-
* The router renders the component for an error that the page or its loader throws.
|
|
3354
|
-
*
|
|
3355
|
-
* it. The fallback renders in its plugin's scope, without props.
|
|
4102
|
+
* The router renders the component for an error that the page or its loader throws. The component
|
|
4103
|
+
* passes each error to the route's quarantine, which counts the error once however often the
|
|
4104
|
+
* component mounts with it. The fallback renders in its plugin's scope, without props. A page
|
|
4105
|
+
* whose module failed to import renders the host's error page with a button that reloads the
|
|
4106
|
+
* page, in place of the fallback, because the fallback is a module of the same plugin and a
|
|
4107
|
+
* reload is the one way to import it again. The error page takes focus where it renders in place
|
|
4108
|
+
* of the page a person is on.
|
|
3356
4109
|
* @param routeId - Qualified id of the route.
|
|
3357
4110
|
* @param fallback - Imports the component the manifest renders in the page's place, where it
|
|
3358
4111
|
* states one.
|
|
@@ -3367,23 +4120,31 @@ function routeErrorOf(routeId, fallback) {
|
|
|
3367
4120
|
const { host } = useRouteContext({ strict: false });
|
|
3368
4121
|
const { quarantine } = host.stores;
|
|
3369
4122
|
const { t } = useTranslation("host");
|
|
3370
|
-
const
|
|
4123
|
+
const unloaded = error instanceof PluginImportError;
|
|
4124
|
+
useReplacementFocus();
|
|
3371
4125
|
useEffect(() => {
|
|
3372
|
-
if (counted.current === error) return;
|
|
3373
|
-
counted.current = error;
|
|
3374
4126
|
quarantine.failed(target, error);
|
|
3375
4127
|
}, [error, quarantine]);
|
|
3376
|
-
if (Fallback !== void 0) return /* @__PURE__ */ jsx(PluginProvider, {
|
|
4128
|
+
if (Fallback !== void 0 && !unloaded) return /* @__PURE__ */ jsx(PluginProvider, {
|
|
3377
4129
|
pluginId: pluginOf(routeId),
|
|
3378
4130
|
children: /* @__PURE__ */ jsx(Suspense, {
|
|
3379
4131
|
fallback: null,
|
|
3380
4132
|
children: /* @__PURE__ */ jsx(Fallback, {})
|
|
3381
4133
|
})
|
|
3382
4134
|
});
|
|
3383
|
-
return /* @__PURE__ */ jsx(EmptyState.Root, { children: /* @__PURE__ */ jsxs(EmptyState.Content, { children: [
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
4135
|
+
return /* @__PURE__ */ jsx(EmptyState.Root, { children: /* @__PURE__ */ jsxs(EmptyState.Content, { children: [
|
|
4136
|
+
/* @__PURE__ */ jsx(EmptyState.Title, {
|
|
4137
|
+
as: "h1",
|
|
4138
|
+
children: t("page.failed.title")
|
|
4139
|
+
}),
|
|
4140
|
+
/* @__PURE__ */ jsx(EmptyState.Description, { children: t("page.failed.description") }),
|
|
4141
|
+
unloaded ? /* @__PURE__ */ jsx(Button, {
|
|
4142
|
+
onClick: () => {
|
|
4143
|
+
window.location.reload();
|
|
4144
|
+
},
|
|
4145
|
+
children: t("failed.reload")
|
|
4146
|
+
}) : null
|
|
4147
|
+
] }) });
|
|
3387
4148
|
};
|
|
3388
4149
|
}
|
|
3389
4150
|
//#endregion
|
|
@@ -3427,18 +4188,24 @@ function requiresSignIn(when) {
|
|
|
3427
4188
|
* context.
|
|
3428
4189
|
*
|
|
3429
4190
|
* @remarks
|
|
3430
|
-
*
|
|
3431
|
-
*
|
|
3432
|
-
*
|
|
3433
|
-
*
|
|
3434
|
-
*
|
|
3435
|
-
*
|
|
4191
|
+
* For a person who is not signed in, where the product states a sign-in route and the condition
|
|
4192
|
+
* requires sign-in at its top level or in a top-level `allOf`, the evaluator throws the router's
|
|
4193
|
+
* redirect to the sign-in route, with the address the navigation enters as the `redirect` search.
|
|
4194
|
+
* The redirect comes before the plugin's availability, because an entitlement or a condition on
|
|
4195
|
+
* the plugin reads a session that does not exist yet. Otherwise the evaluator throws the router's
|
|
4196
|
+
* not-found error where the route's plugin is not on, with the plugin and its reason as data
|
|
4197
|
+
* where a switch or a kill switch stopped it, and where the route is quarantined, with the target
|
|
4198
|
+
* as data. It then returns the condition.
|
|
3436
4199
|
* @param product - The installed plugins and the sign-in route.
|
|
3437
4200
|
*/
|
|
3438
4201
|
function evaluatorOf(product) {
|
|
3439
4202
|
return ({ pluginId, routeId, when }, { host, routes }, location) => {
|
|
3440
4203
|
const context = conditionContextOf(host.stores);
|
|
3441
4204
|
const target = `route:${routeId}`;
|
|
4205
|
+
if (product.signIn !== void 0 && !context.authenticated && when !== void 0 && requiresSignIn(when)) throw redirect({
|
|
4206
|
+
search: { redirect: location?.href },
|
|
4207
|
+
to: routeHref(routes, product.signIn)
|
|
4208
|
+
});
|
|
3442
4209
|
if (!context.on(pluginId)) {
|
|
3443
4210
|
const data = stoppedBy(pluginId, product.plugins, host.stores.availability.get());
|
|
3444
4211
|
throw data === void 0 ? notFound() : notFound({ data });
|
|
@@ -3447,12 +4214,7 @@ function evaluatorOf(product) {
|
|
|
3447
4214
|
reason: "quarantined",
|
|
3448
4215
|
target
|
|
3449
4216
|
} });
|
|
3450
|
-
|
|
3451
|
-
if (product.signIn !== void 0 && !context.authenticated && requiresSignIn(when)) throw redirect({
|
|
3452
|
-
search: { redirect: location?.href },
|
|
3453
|
-
to: routeHref(routes, product.signIn)
|
|
3454
|
-
});
|
|
3455
|
-
return false;
|
|
4217
|
+
return when === void 0 || evaluateWhen(when, context);
|
|
3456
4218
|
};
|
|
3457
4219
|
}
|
|
3458
4220
|
//#endregion
|
|
@@ -3495,7 +4257,7 @@ function loaderOf(product, route) {
|
|
|
3495
4257
|
* @remarks
|
|
3496
4258
|
* The component reads the host from the router's context. A render of the page that throws
|
|
3497
4259
|
* commits nothing, so the route's count of failed renders returns to zero only after a render
|
|
3498
|
-
* that commits.
|
|
4260
|
+
* that commits. The page takes focus where it renders in place of the page a person is on.
|
|
3499
4261
|
* @param pluginId - Id of the plugin that declares the route, or the settings page, whose scope
|
|
3500
4262
|
* the page renders in.
|
|
3501
4263
|
* @param routeId - The route's qualified id, under which its failed renders and the extensions
|
|
@@ -3511,6 +4273,7 @@ function hostPageOf(pluginId, routeId, page) {
|
|
|
3511
4273
|
return function HostPage() {
|
|
3512
4274
|
const { host } = useRouteContext({ strict: false });
|
|
3513
4275
|
const { quarantine } = host.stores;
|
|
4276
|
+
useReplacementFocus();
|
|
3514
4277
|
useEffect(() => {
|
|
3515
4278
|
quarantine.rendered(target);
|
|
3516
4279
|
});
|
|
@@ -3600,8 +4363,8 @@ function unplacedOf(sections, listed, pages) {
|
|
|
3600
4363
|
* part under the settings route.
|
|
3601
4364
|
*/
|
|
3602
4365
|
/**
|
|
3603
|
-
* Returns the ids of the sections whose plugin is on and whose condition is true, joined by spaces
|
|
3604
|
-
*
|
|
4366
|
+
* Returns the ids of the sections whose plugin is on and whose condition is true, joined by spaces.
|
|
4367
|
+
* A qualified id does not contain a space.
|
|
3605
4368
|
*/
|
|
3606
4369
|
function shownOf(sections, stores) {
|
|
3607
4370
|
const context = conditionContextOf(stores);
|
|
@@ -3610,9 +4373,13 @@ function shownOf(sections, stores) {
|
|
|
3610
4373
|
/**
|
|
3611
4374
|
* Returns the sections that render and the pages the settings menu lists, and renders again when a
|
|
3612
4375
|
* plugin turns on or off, a flag changes or the session changes.
|
|
4376
|
+
*
|
|
4377
|
+
* @remarks
|
|
4378
|
+
* The server snapshot computes the sections inside `asServerSnapshot`, so a settings page
|
|
4379
|
+
* hydrates with the sections that the server render showed.
|
|
3613
4380
|
*/
|
|
3614
4381
|
function useSections() {
|
|
3615
|
-
const $ = c(
|
|
4382
|
+
const $ = c(24);
|
|
3616
4383
|
let t0;
|
|
3617
4384
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
3618
4385
|
t0 = { strict: false };
|
|
@@ -3654,47 +4421,53 @@ function useSections() {
|
|
|
3654
4421
|
$[9] = t2;
|
|
3655
4422
|
} else t2 = $[9];
|
|
3656
4423
|
const read = t2;
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
$[
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
const shown = t4;
|
|
4424
|
+
let t3;
|
|
4425
|
+
if ($[10] !== read) {
|
|
4426
|
+
t3 = () => asServerSnapshot(read);
|
|
4427
|
+
$[10] = read;
|
|
4428
|
+
$[11] = t3;
|
|
4429
|
+
} else t3 = $[11];
|
|
4430
|
+
const t4 = useSyncExternalStore(subscribe, read, t3);
|
|
3665
4431
|
let t5;
|
|
3666
|
-
if ($[12] !==
|
|
3667
|
-
t5 =
|
|
3668
|
-
$[12] =
|
|
4432
|
+
if ($[12] !== t4) {
|
|
4433
|
+
t5 = new Set(t4.split(" "));
|
|
4434
|
+
$[12] = t4;
|
|
3669
4435
|
$[13] = t5;
|
|
3670
4436
|
} else t5 = $[13];
|
|
4437
|
+
const shown = t5;
|
|
3671
4438
|
let t6;
|
|
3672
|
-
if ($[14] !==
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
4439
|
+
if ($[14] !== entries) {
|
|
4440
|
+
t6 = entries.map(_temp);
|
|
4441
|
+
$[14] = entries;
|
|
4442
|
+
$[15] = t6;
|
|
4443
|
+
} else t6 = $[15];
|
|
4444
|
+
let t7;
|
|
4445
|
+
if ($[16] !== settings.sections || $[17] !== shown) {
|
|
4446
|
+
let t8;
|
|
4447
|
+
if ($[19] !== shown) {
|
|
4448
|
+
t8 = (t9) => {
|
|
4449
|
+
const { id } = t9;
|
|
3677
4450
|
return shown.has(id);
|
|
3678
4451
|
};
|
|
3679
|
-
$[
|
|
3680
|
-
$[
|
|
3681
|
-
} else
|
|
3682
|
-
|
|
3683
|
-
$[
|
|
3684
|
-
$[
|
|
3685
|
-
$[
|
|
3686
|
-
} else
|
|
3687
|
-
let
|
|
3688
|
-
if ($[
|
|
3689
|
-
|
|
3690
|
-
listed:
|
|
3691
|
-
shown:
|
|
4452
|
+
$[19] = shown;
|
|
4453
|
+
$[20] = t8;
|
|
4454
|
+
} else t8 = $[20];
|
|
4455
|
+
t7 = settings.sections.filter(t8);
|
|
4456
|
+
$[16] = settings.sections;
|
|
4457
|
+
$[17] = shown;
|
|
4458
|
+
$[18] = t7;
|
|
4459
|
+
} else t7 = $[18];
|
|
4460
|
+
let t8;
|
|
4461
|
+
if ($[21] !== t6 || $[22] !== t7) {
|
|
4462
|
+
t8 = {
|
|
4463
|
+
listed: t6,
|
|
4464
|
+
shown: t7
|
|
3692
4465
|
};
|
|
3693
|
-
$[
|
|
3694
|
-
$[
|
|
3695
|
-
$[
|
|
3696
|
-
} else
|
|
3697
|
-
return
|
|
4466
|
+
$[21] = t6;
|
|
4467
|
+
$[22] = t7;
|
|
4468
|
+
$[23] = t8;
|
|
4469
|
+
} else t8 = $[23];
|
|
4470
|
+
return t8;
|
|
3698
4471
|
}
|
|
3699
4472
|
function _temp(t0) {
|
|
3700
4473
|
const { routeId } = t0;
|
|
@@ -4710,9 +5483,11 @@ function declarationOf(product, route) {
|
|
|
4710
5483
|
* The routes compile in one call, so the compiler sees every path at once. Each route loads its
|
|
4711
5484
|
* page with the modules of the plugins in its `loads`, loads its `data` through the data client
|
|
4712
5485
|
* in the router's context, validates its search with its contract's validator, and evaluates its
|
|
4713
|
-
* condition against the host in the router's context.
|
|
4714
|
-
*
|
|
4715
|
-
* The
|
|
5486
|
+
* condition against the host in the router's context. Each route renders the root's not-found
|
|
5487
|
+
* page in its own place, so a page that throws the router's not-found shows it inside the frame
|
|
5488
|
+
* and counts no failure. The settings route renders the settings frame, its index opens the first
|
|
5489
|
+
* settings page, and each settings page renders its sections. The pages import through the
|
|
5490
|
+
* manifests' importers as `instrumented` wraps them.
|
|
4716
5491
|
* @param product - The product, with each installed plugin's manifest.
|
|
4717
5492
|
* @returns The function that compiles the routes under the root, for the root's `addChildren`.
|
|
4718
5493
|
*/
|
|
@@ -4723,8 +5498,9 @@ function createHostRoutes(product) {
|
|
|
4723
5498
|
return (root) => compileRoutes(declarations, {
|
|
4724
5499
|
errorComponent: ({ id }) => routeErrorOf(id, mappingOf(loaded, id).fallback),
|
|
4725
5500
|
evaluate,
|
|
5501
|
+
notFoundComponent: root.options.notFoundComponent,
|
|
4726
5502
|
parent: root
|
|
4727
5503
|
});
|
|
4728
5504
|
}
|
|
4729
5505
|
//#endregion
|
|
4730
|
-
export {
|
|
5506
|
+
export { settingsListed as a, HostProvider as c, HostContent as d, onCaughtError as f, sampledFrom as h, menusOf as i, HostNotFound as l, internalsOf as m, HostRoot as n, useListed as o, createHost as p, listedRoutesOf as r, usePalette as s, createHostRoutes as t, useInternals as u };
|