@askrjs/askr 0.3.1 → 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/CHANGELOG.md +855 -0
- package/README.md +61 -29
- package/capabilities.json +19 -9
- package/dist/actions/index.js +56 -12
- package/dist/benchmark.js +10 -15
- package/dist/boot/index.d.ts +3 -2
- package/dist/boot/index.js +1 -1
- package/dist/boot-DaoTYoCw.js +3989 -0
- package/dist/browser-activity-CB8e-V4f.js +220 -0
- package/dist/components/index.d.ts +8 -1
- package/dist/components/index.js +1 -43
- package/dist/{aria-hKjThHcp.js → compose-ref-DWzdbxsD.js} +41 -4
- package/dist/control/index.d.ts +2 -0
- package/dist/control/index.js +1 -1
- package/dist/control-nfpT2fbK.js +209 -0
- package/dist/core.d.ts +5 -9
- package/dist/csp-nonce-DwGw1A6Q.js +35 -0
- package/dist/data/index.js +3 -3
- package/dist/{data-2edW0jhF.js → data-BCwClJiF.js} +283 -94
- package/dist/data-runtime-C1bFAlba.js +242 -0
- package/dist/{deferred-CbgNhf67.js → deferred-bEEHHvic.js} +42 -20
- package/dist/domains/actions.d.ts +8 -3
- package/dist/domains/component.d.ts +1 -107
- package/dist/domains/context.d.ts +0 -15
- package/dist/domains/control.d.ts +36 -249
- package/dist/domains/data.d.ts +31 -13
- package/dist/domains/diagnostics.d.ts +1 -2
- package/dist/domains/lifecycle.d.ts +2 -5
- package/dist/domains/refs.d.ts +0 -2
- package/dist/domains/routing.d.ts +43 -8
- package/dist/domains/scheduler.d.ts +1 -76
- package/dist/domains/server.d.ts +5 -2
- package/dist/domains/state.d.ts +2 -5
- package/dist/domains/telemetry.d.ts +0 -2
- package/dist/elements.d.ts +214 -51
- package/dist/error-boundary-Bpwofwps.js +293 -0
- package/dist/foundations/icon/index.d.ts +0 -58
- package/dist/foundations/icon/index.js +2 -2
- package/dist/foundations/index.js +2 -1
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +3 -3
- package/dist/foundations/state/index.js +5 -3
- package/dist/foundations/structures/index.js +2 -1
- package/dist/foundations/utilities/index.d.ts +2 -0
- package/dist/foundations/utilities/index.js +1 -2
- package/dist/fx/index.d.ts +31 -58
- package/dist/fx/index.js +313 -195
- package/dist/hooks-BimKStr4.js +202 -0
- package/dist/index.d.ts +11 -74
- package/dist/index.js +8 -447
- package/dist/instance-BAnfmXOR.js +988 -0
- package/dist/{interactions-CTeACdT_.js → interactions-D2BoREb-.js} +3 -2
- package/dist/jsx-dev-runtime.d.ts +41 -18
- package/dist/jsx-dev-runtime.js +1 -6
- package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
- package/dist/jsx-runtime.js +1 -1
- package/dist/jsx.d.ts +61 -37
- package/dist/{manifest-D1P3UNWV.js → manifest-CW9gyNBY.js} +3 -3
- package/dist/{navigate-prC5V8XV.js → navigate-CP_0Zy5U.js} +454 -87
- package/dist/navigation.d.ts +12 -3
- package/dist/peer-types.d.ts +8 -0
- package/dist/portal-DuOKfnLa.js +349 -0
- package/dist/promise-Dz8iIHAm.js +16 -0
- package/dist/query-registry-WMCN0BJL.js +320 -0
- package/dist/readable-DFWOsTac.js +21 -0
- package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
- package/dist/render-sync-IC5lMFpq.js +1240 -0
- package/dist/resolution-Cx82-xjW.js +1244 -0
- package/dist/resource-CLjB7RBQ.js +377 -0
- package/dist/resources/index.d.ts +21 -11
- package/dist/resources/index.js +45 -244
- package/dist/router/index.d.ts +14 -5
- package/dist/router/index.js +22 -53
- package/dist/scope-CwpVSHVf.js +55 -0
- package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
- package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
- package/dist/ssg/index.d.ts +1 -2
- package/dist/ssg/index.js +5 -13
- package/dist/ssr/index.d.ts +26 -28
- package/dist/ssr/index.js +4 -4
- package/dist/{ssr-CiNm6Vzl.js → ssr-BJ8ug7zH.js} +133 -122
- package/dist/ssr-DoV-vJ-5.js +229 -0
- package/dist/state-DWO1KzkX.js +232 -0
- package/dist/structures-D4dDMqHX.js +231 -0
- package/dist/testing/index.d.ts +8 -4
- package/dist/testing/index.js +29 -34
- package/dist/url-CnbfpX0K.js +185 -0
- package/dist/utilities.d.ts +48 -2
- package/dist/verify-hydration-DpVaDnqz.js +152 -0
- package/package.json +19 -9
- package/dist/access-CEA2pOry.js +0 -1046
- package/dist/activity-oy2YFTFF.js +0 -201
- package/dist/boot-DLplW9h4.js +0 -1104
- package/dist/capabilities-Bbf0mlp9.js +0 -42
- package/dist/compose-ref-v0qGlHoN.js +0 -26
- package/dist/control-BR_mrX__.js +0 -173
- package/dist/control-C-ApnFs1.js +0 -20
- package/dist/csp-nonce-BfFWcd0w.js +0 -173
- package/dist/data-runtime-CxoY98lX.js +0 -149
- package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
- package/dist/dom-renderer.d.ts +0 -77
- package/dist/domains/renderer.d.ts +0 -127
- package/dist/eager-control.d.ts +0 -8
- package/dist/effect-C3Zsc1k6.js +0 -292
- package/dist/for-state-BDykeOJL.js +0 -1753
- package/dist/instance-VAFYVJOa.js +0 -1027
- package/dist/notify-DxxuDPeY.js +0 -266
- package/dist/portal-w3oQ6oQU.js +0 -29
- package/dist/query-registry-LrYpQsDJ.js +0 -83
- package/dist/render-resolved-BC0W7_5b.js +0 -1123
- package/dist/renderer-CT0HeY58.js +0 -8623
- package/dist/resolution-3DskUkjG.js +0 -577
- package/dist/resource-18-sFRl9.js +0 -344
- package/dist/route-matching-CUzZ31gT.js +0 -551
- package/dist/router-internal-7nuNEHfG.js +0 -36
- package/dist/scope-Cq_iYbTR.js +0 -403
- package/dist/selector-BSPVkjKP.js +0 -311
- package/dist/ssr-EPjwJH5R.js +0 -29
- package/dist/state-BXJfudq1.js +0 -91
- package/dist/structures-CjEzfj4h.js +0 -654
- package/dist/timer-C7uqnJVt.js +0 -85
- package/dist/verify-hydration-ikaxotf7.js +0 -27
- package/dist/vnode-D24p6IW2.js +0 -7
|
@@ -1,9 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { a as resolveRouteRequest, i as hasRouteRenderData, n as getRouteRenderContext, r as getRouteRenderData } from "./resolution-
|
|
6
|
-
import {
|
|
1
|
+
import { F as logger, I as isDevelopmentEnvironment, L as isProductionEnvironment, x as isFlushing, y as flushSync } from "./instance-BAnfmXOR.js";
|
|
2
|
+
import { b as reportUncaughtErrorLater, c as onCommit, i as currentComponent, n as createSource, o as hookSlot, r as currentAppRuntime, s as notify, u as readSource } from "./hooks-BimKStr4.js";
|
|
3
|
+
import { n as getActiveRenderContext } from "./render-context-DGHnEveC.js";
|
|
4
|
+
import { n as isPromiseLike } from "./promise-Dz8iIHAm.js";
|
|
5
|
+
import { C as removeRouteBasePath, E as parseLocation, M as getActiveRouteBasePath, N as getActiveRoutes, T as makeQuery, Y as lockRouteRegistration, a as resolveRouteRequest, b as addLogicalRouteBasePath, g as computeRouteActivityMatches, h as computeMatchesFromRoutes, i as hasRouteRenderData, n as getRouteRenderContext, r as getRouteRenderData, w as deepFreeze } from "./resolution-Cx82-xjW.js";
|
|
6
|
+
import { n as syncRouteActivitySnapshot } from "./route-activity-CNJfpwx-.js";
|
|
7
|
+
import { c as resolveNavigationUrl } from "./url-CnbfpX0K.js";
|
|
8
|
+
//#region src/common/app-render-runtime.ts
|
|
9
|
+
const stagedRouteLocations = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
function createAppRenderRuntime(input = {}) {
|
|
11
|
+
return {
|
|
12
|
+
hydrationResources: input.hydrationResources,
|
|
13
|
+
framework: Object.freeze({ ...input.framework }),
|
|
14
|
+
route: input.route,
|
|
15
|
+
hasRoute: input.hasRoute ?? false,
|
|
16
|
+
dataRuntime: input.dataRuntime,
|
|
17
|
+
routeRegistry: input.routeRegistry,
|
|
18
|
+
routeAuth: input.routeAuth,
|
|
19
|
+
lifetime: input.lifetime
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function stageAppRenderRouteLocation(runtime, href) {
|
|
23
|
+
stagedRouteLocations.set(runtime, href);
|
|
24
|
+
}
|
|
25
|
+
function getStagedAppRenderRouteLocation(runtime) {
|
|
26
|
+
return runtime ? stagedRouteLocations.get(runtime) : void 0;
|
|
27
|
+
}
|
|
28
|
+
function clearStagedAppRenderRouteLocation(runtime) {
|
|
29
|
+
if (runtime) stagedRouteLocations.delete(runtime);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
7
32
|
//#region src/common/root-update.ts
|
|
8
33
|
let rootUpdateHost;
|
|
9
34
|
function configureRootUpdateHost(host) {
|
|
@@ -14,6 +39,174 @@ function prepareRootUpdate(root, input) {
|
|
|
14
39
|
return rootUpdateHost.prepare(root, input);
|
|
15
40
|
}
|
|
16
41
|
//#endregion
|
|
42
|
+
//#region src/router/activity.ts
|
|
43
|
+
function routeSignature(route) {
|
|
44
|
+
return `${route.path}\u0000${JSON.stringify(route.query.toJSON())}\u0000${route.hash ?? ""}`;
|
|
45
|
+
}
|
|
46
|
+
let currentRouteSnapshot = buildRouteSnapshot("/", "", "");
|
|
47
|
+
const currentRouteSource = createSource();
|
|
48
|
+
/** Register a callback to run whenever the active route changes, with optional cleanup. */
|
|
49
|
+
function onRouteChange(fn, options = {}) {
|
|
50
|
+
const instance = currentComponent();
|
|
51
|
+
if (!instance) return;
|
|
52
|
+
const route = currentRoute();
|
|
53
|
+
const slot = hookSlot(instance, "onRouteChange", () => ({
|
|
54
|
+
kind: "route-change",
|
|
55
|
+
previous: null,
|
|
56
|
+
pending: null,
|
|
57
|
+
cleanup: null,
|
|
58
|
+
cleanupRegistered: false,
|
|
59
|
+
callback: fn,
|
|
60
|
+
immediate: options.immediate === true
|
|
61
|
+
}));
|
|
62
|
+
slot.pending = route;
|
|
63
|
+
slot.callback = fn;
|
|
64
|
+
slot.immediate = options.immediate === true;
|
|
65
|
+
if (!slot.cleanupRegistered) {
|
|
66
|
+
instance.onCleanup(() => {
|
|
67
|
+
slot.cleanup?.();
|
|
68
|
+
slot.cleanup = null;
|
|
69
|
+
});
|
|
70
|
+
slot.cleanupRegistered = true;
|
|
71
|
+
}
|
|
72
|
+
if (!slot.previous) {
|
|
73
|
+
onCommit(instance, () => {
|
|
74
|
+
const committed = slot.pending;
|
|
75
|
+
if (!committed) return;
|
|
76
|
+
if (slot.immediate) slot.cleanup = slot.callback(committed, null) ?? null;
|
|
77
|
+
slot.previous = committed;
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (routeSignature(slot.previous) === routeSignature(route)) return;
|
|
82
|
+
onCommit(instance, () => {
|
|
83
|
+
const previous = slot.previous;
|
|
84
|
+
const committed = slot.pending;
|
|
85
|
+
if (!previous || !committed || routeSignature(previous) === routeSignature(committed)) return;
|
|
86
|
+
slot.cleanup?.();
|
|
87
|
+
slot.cleanup = slot.callback(committed, previous) ?? null;
|
|
88
|
+
slot.previous = committed;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
let serverLocation = null;
|
|
92
|
+
function readLocationState() {
|
|
93
|
+
const historyState = typeof window !== "undefined" && window.history?.state ? window.history.state : null;
|
|
94
|
+
const hasState = historyState?.askrHasState === true;
|
|
95
|
+
return {
|
|
96
|
+
hasState,
|
|
97
|
+
state: hasState ? historyState.askrState : void 0
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function logicalRoutePathname(pathname) {
|
|
101
|
+
const logical = removeRouteBasePath(pathname, getActiveRouteBasePath());
|
|
102
|
+
return logical === void 0 ? void 0 : parseLocation(logical).pathname;
|
|
103
|
+
}
|
|
104
|
+
function routePathname(pathname) {
|
|
105
|
+
const logical = logicalRoutePathname(pathname);
|
|
106
|
+
return logical === void 0 ? {
|
|
107
|
+
pathname: parseLocation(pathname).pathname,
|
|
108
|
+
withinBasePath: false
|
|
109
|
+
} : {
|
|
110
|
+
pathname: logical,
|
|
111
|
+
withinBasePath: true
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function setServerLocation(url) {
|
|
115
|
+
serverLocation = url;
|
|
116
|
+
if (url) {
|
|
117
|
+
const location = routePathname(parseLocation(url).pathname);
|
|
118
|
+
syncRouteActivitySnapshot(location.pathname, location.withinBasePath ? computeMatchesFromRoutes(location.pathname, getActiveRoutes()) : []);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
syncRouteActivitySnapshot(currentRouteSnapshot.path, currentRouteSnapshot.matches);
|
|
122
|
+
}
|
|
123
|
+
function buildRouteSnapshot(pathname, search, hash) {
|
|
124
|
+
const location = routePathname(pathname);
|
|
125
|
+
pathname = location.pathname;
|
|
126
|
+
const query = makeQuery(search);
|
|
127
|
+
const matches = location.withinBasePath ? computeMatchesFromRoutes(pathname, getActiveRoutes()) : [];
|
|
128
|
+
const locationState = readLocationState();
|
|
129
|
+
return Object.freeze({
|
|
130
|
+
path: pathname,
|
|
131
|
+
params: deepFreeze({ ...matches[0]?.params }),
|
|
132
|
+
query,
|
|
133
|
+
hash: hash || null,
|
|
134
|
+
...locationState,
|
|
135
|
+
matches: Object.freeze(matches)
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function setCurrentRouteSnapshot(pathname, search, hash, activityMatches) {
|
|
139
|
+
currentRouteSnapshot = buildRouteSnapshot(pathname, search, hash);
|
|
140
|
+
syncRouteActivitySnapshot(currentRouteSnapshot.path, activityMatches ?? currentRouteSnapshot.matches);
|
|
141
|
+
notify(currentRouteSource);
|
|
142
|
+
}
|
|
143
|
+
function readCurrentRouteLocation() {
|
|
144
|
+
const renderContext = getActiveRenderContext();
|
|
145
|
+
if (renderContext?.url) {
|
|
146
|
+
const parsed = parseLocation(renderContext.url);
|
|
147
|
+
const location = routePathname(parsed.pathname);
|
|
148
|
+
return {
|
|
149
|
+
...parsed,
|
|
150
|
+
...location
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const stagedRouteLocation = getStagedAppRenderRouteLocation(currentAppRuntime());
|
|
154
|
+
if (stagedRouteLocation) {
|
|
155
|
+
const parsed = parseLocation(stagedRouteLocation);
|
|
156
|
+
const location = routePathname(parsed.pathname);
|
|
157
|
+
return {
|
|
158
|
+
...parsed,
|
|
159
|
+
...location
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (typeof window !== "undefined" && window.location) return {
|
|
163
|
+
...routePathname(window.location.pathname || "/"),
|
|
164
|
+
search: window.location.search || "",
|
|
165
|
+
hash: window.location.hash || ""
|
|
166
|
+
};
|
|
167
|
+
if (serverLocation) {
|
|
168
|
+
const parsed = parseLocation(serverLocation);
|
|
169
|
+
const location = routePathname(parsed.pathname);
|
|
170
|
+
return {
|
|
171
|
+
...parsed,
|
|
172
|
+
...location
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
...routePathname(currentRouteSnapshot.path),
|
|
177
|
+
search: "",
|
|
178
|
+
hash: currentRouteSnapshot.hash ?? ""
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function readCurrentRouteSnapshot() {
|
|
182
|
+
if (!currentComponent()) throw new Error("currentRoute() can only be called during component render execution. Call currentRoute() from inside your component function.");
|
|
183
|
+
const { pathname, search, hash, withinBasePath } = readCurrentRouteLocation();
|
|
184
|
+
const query = makeQuery(search);
|
|
185
|
+
const matches = withinBasePath ? computeMatchesFromRoutes(pathname, getActiveRoutes()) : [];
|
|
186
|
+
const routeParams = (withinBasePath ? getActiveRenderContext()?.params : void 0) ?? matches[0]?.params ?? {};
|
|
187
|
+
const params = deepFreeze({ ...routeParams });
|
|
188
|
+
const locationState = readLocationState();
|
|
189
|
+
return Object.freeze({
|
|
190
|
+
path: pathname,
|
|
191
|
+
params,
|
|
192
|
+
query,
|
|
193
|
+
hash: hash || null,
|
|
194
|
+
...locationState,
|
|
195
|
+
matches: Object.freeze(matches)
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** Read the currently active route's {@link RouteSnapshot}; reactive during component render. */
|
|
199
|
+
function currentRoute() {
|
|
200
|
+
const instance = currentComponent();
|
|
201
|
+
if (!instance) throw new Error("currentRoute() can only be called during component render execution. Call currentRoute() from inside your component function.");
|
|
202
|
+
if (typeof window === "undefined" || instance.server) return readCurrentRouteSnapshot();
|
|
203
|
+
readSource(currentRouteSource);
|
|
204
|
+
return readCurrentRouteSnapshot();
|
|
205
|
+
}
|
|
206
|
+
function syncCurrentRouteSnapshot(pathname, search, hash, activityMatches) {
|
|
207
|
+
setCurrentRouteSnapshot(pathname, search, hash, activityMatches);
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
17
210
|
//#region src/router/navigation-registry.ts
|
|
18
211
|
let currentInstance = null;
|
|
19
212
|
let currentPathname = "/";
|
|
@@ -45,6 +238,29 @@ function parseTargetUrl(path) {
|
|
|
45
238
|
const base = href && href !== "about:blank" && !href.startsWith("about:") && !href.startsWith("data:") ? href : `http://localhost${pathname}${search}${hash}`;
|
|
46
239
|
return new URL(path, base);
|
|
47
240
|
}
|
|
241
|
+
/** Whether a parsed target shares the origin of the current document. */
|
|
242
|
+
function isCurrentOrigin(target) {
|
|
243
|
+
return target.origin === parseTargetUrl("/").origin;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The absolute URL of a root-relative href on the current origin. Browsers
|
|
247
|
+
* read a root-relative `//host/x` as another host, so history writes and
|
|
248
|
+
* document loads never receive the root-relative form.
|
|
249
|
+
*/
|
|
250
|
+
function toDocumentUrl(href) {
|
|
251
|
+
const origin = typeof window === "undefined" ? void 0 : window.location.origin;
|
|
252
|
+
return origin && origin !== "null" ? `${origin}${href}` : href;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Parse a navigation or redirect target. Only a target written with an
|
|
256
|
+
* explicit `http:`/`https:` scheme may leave the current origin: a path-like
|
|
257
|
+
* string that the URL parser resolves elsewhere (`/\\evil.example`,
|
|
258
|
+
* `//evil.example`, `/.//evil.example`) would otherwise turn an app path into
|
|
259
|
+
* an open redirect.
|
|
260
|
+
*/
|
|
261
|
+
function parseNavigationTarget(path) {
|
|
262
|
+
return resolveNavigationUrl(path, parseTargetUrl("").href);
|
|
263
|
+
}
|
|
48
264
|
function getRegisteredAppsSnapshot() {
|
|
49
265
|
return [...registeredApps];
|
|
50
266
|
}
|
|
@@ -217,13 +433,29 @@ function scrollToPosition(position) {
|
|
|
217
433
|
if (typeof window === "undefined" || typeof window.scrollTo !== "function") return;
|
|
218
434
|
window.scrollTo(position.x, position.y);
|
|
219
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* Find the element a URL fragment indicates. Like the browser, try the raw
|
|
438
|
+
* fragment first, then its percent-decoded form. A fragment that is not valid
|
|
439
|
+
* percent-encoding (`#%E0`) has no decoded form, so only the raw id applies.
|
|
440
|
+
*/
|
|
441
|
+
function findFragmentTarget(fragment) {
|
|
442
|
+
const raw = document.getElementById(fragment);
|
|
443
|
+
if (raw) return raw;
|
|
444
|
+
let decoded;
|
|
445
|
+
try {
|
|
446
|
+
decoded = decodeURIComponent(fragment);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (error instanceof URIError) return null;
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
return decoded === fragment ? null : document.getElementById(decoded);
|
|
452
|
+
}
|
|
220
453
|
function applyNavigationScroll(behavior) {
|
|
221
454
|
const nextBehavior = behavior ?? scrollRestorationOptions.navigation;
|
|
222
455
|
if (!scrollRestorationOptions.enabled || nextBehavior === "preserve") return;
|
|
223
456
|
const hash = typeof window !== "undefined" ? window.location.hash : "";
|
|
224
457
|
if (hash && typeof document !== "undefined") {
|
|
225
|
-
const
|
|
226
|
-
const target = document.getElementById(id);
|
|
458
|
+
const target = findFragmentTarget(hash.slice(1));
|
|
227
459
|
if (target && typeof target.scrollIntoView === "function") {
|
|
228
460
|
target.scrollIntoView({ behavior: "auto" });
|
|
229
461
|
return;
|
|
@@ -410,6 +642,118 @@ function serializeRouteMeta(meta) {
|
|
|
410
642
|
return output.join("");
|
|
411
643
|
}
|
|
412
644
|
//#endregion
|
|
645
|
+
//#region src/router/document-navigation.ts
|
|
646
|
+
/**
|
|
647
|
+
* Hand a location no registered app can render to the browser as a document
|
|
648
|
+
* load, keeping the requested history mode.
|
|
649
|
+
*/
|
|
650
|
+
function loadDocument(href, history) {
|
|
651
|
+
if (history === "replace") window.location.replace(href);
|
|
652
|
+
else window.location.assign(href);
|
|
653
|
+
}
|
|
654
|
+
/** Reload the document at the current history entry. */
|
|
655
|
+
function reloadDocument() {
|
|
656
|
+
window.location.reload();
|
|
657
|
+
}
|
|
658
|
+
//#endregion
|
|
659
|
+
//#region src/router/history-index.ts
|
|
660
|
+
/**
|
|
661
|
+
* Positions in this document's session history.
|
|
662
|
+
*
|
|
663
|
+
* Askr stamps an index into every entry it writes. A failed back/forward
|
|
664
|
+
* traversal then returns with `history.go()` to the entry whose page is still
|
|
665
|
+
* rendered, instead of rewriting the entry the browser landed on. A position
|
|
666
|
+
* is `undefined` once it cannot be known, for example after other code pushed
|
|
667
|
+
* an entry; rollback then reloads rather than guess.
|
|
668
|
+
*/
|
|
669
|
+
let landedIndex;
|
|
670
|
+
let renderedIndex;
|
|
671
|
+
let pendingReturnIndex;
|
|
672
|
+
let historyIndexInitialized = false;
|
|
673
|
+
function readHistoryIndex(state) {
|
|
674
|
+
if (!state || typeof state !== "object") return void 0;
|
|
675
|
+
const index = state.askrIndex;
|
|
676
|
+
return typeof index === "number" ? index : void 0;
|
|
677
|
+
}
|
|
678
|
+
function stampActiveEntry(state, index) {
|
|
679
|
+
window.history.replaceState({
|
|
680
|
+
...state && typeof state === "object" ? state : {},
|
|
681
|
+
askrIndex: index
|
|
682
|
+
}, "", window.location.href);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* The index an entry written with `mode` occupies. The active entry must still
|
|
686
|
+
* carry the index Askr last tracked; otherwise other code wrote an entry and
|
|
687
|
+
* positions are unknown from here on.
|
|
688
|
+
*/
|
|
689
|
+
function nextHistoryIndex(mode) {
|
|
690
|
+
if (readHistoryIndex(window.history.state) !== landedIndex) landedIndex = renderedIndex = void 0;
|
|
691
|
+
if (landedIndex === void 0) return void 0;
|
|
692
|
+
return mode === "push" ? landedIndex + 1 : landedIndex;
|
|
693
|
+
}
|
|
694
|
+
/** Record that the entry at `index` is active and its page rendered. */
|
|
695
|
+
function commitHistoryIndex(index) {
|
|
696
|
+
landedIndex = renderedIndex = index;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Record the entry a popstate landed on, before its page renders. An entry
|
|
700
|
+
* without an index was added by a fragment navigation from the entry the user
|
|
701
|
+
* was on, so it is stamped as the next position.
|
|
702
|
+
*/
|
|
703
|
+
function landOnHistoryEntry(state) {
|
|
704
|
+
let index = readHistoryIndex(state);
|
|
705
|
+
if (index === void 0 && landedIndex !== void 0) {
|
|
706
|
+
index = landedIndex + 1;
|
|
707
|
+
stampActiveEntry(state, index);
|
|
708
|
+
}
|
|
709
|
+
landedIndex = index;
|
|
710
|
+
return index;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Adopt the active entry's index. Only the document's first entry is stamped
|
|
714
|
+
* as index 0: stamping later would guess a position the user may not be at.
|
|
715
|
+
*/
|
|
716
|
+
function initializeHistoryIndex() {
|
|
717
|
+
const state = window.history?.state;
|
|
718
|
+
const index = readHistoryIndex(state);
|
|
719
|
+
if (index !== void 0) {
|
|
720
|
+
historyIndexInitialized = true;
|
|
721
|
+
commitHistoryIndex(index);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (historyIndexInitialized || typeof window.history?.replaceState !== "function") return;
|
|
725
|
+
historyIndexInitialized = true;
|
|
726
|
+
commitHistoryIndex(0);
|
|
727
|
+
stampActiveEntry(state, 0);
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Traverse back to the entry whose page is rendered. Returns false when the
|
|
731
|
+
* positions are unknown, leaving history untouched.
|
|
732
|
+
*/
|
|
733
|
+
function returnToRenderedHistoryEntry() {
|
|
734
|
+
if (landedIndex === void 0 || renderedIndex === void 0) return false;
|
|
735
|
+
const delta = renderedIndex - landedIndex;
|
|
736
|
+
if (delta === 0) return false;
|
|
737
|
+
pendingReturnIndex = renderedIndex;
|
|
738
|
+
window.history.go(delta);
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
/** A newer navigation owns history, so a pending return must not skip it. */
|
|
742
|
+
function cancelHistoryReturn() {
|
|
743
|
+
pendingReturnIndex = void 0;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Whether this popstate completes a {@link returnToRenderedHistoryEntry}
|
|
747
|
+
* traversal, whose destination is already rendered.
|
|
748
|
+
*/
|
|
749
|
+
function consumeHistoryReturn(state) {
|
|
750
|
+
const expected = pendingReturnIndex;
|
|
751
|
+
pendingReturnIndex = void 0;
|
|
752
|
+
if (expected === void 0 || readHistoryIndex(state) !== expected) return false;
|
|
753
|
+
commitHistoryIndex(expected);
|
|
754
|
+
return true;
|
|
755
|
+
}
|
|
756
|
+
//#endregion
|
|
413
757
|
//#region src/router/navigation-targets.ts
|
|
414
758
|
const MAX_NAVIGATION_REDIRECTS = 20;
|
|
415
759
|
let activeRouteRequestId = 0;
|
|
@@ -506,7 +850,8 @@ function resolveAppRouteRequest(app, pathname, href, signal) {
|
|
|
506
850
|
return resolveRouteRequest(href, {
|
|
507
851
|
registry: app.registry,
|
|
508
852
|
auth: app.auth,
|
|
509
|
-
signal
|
|
853
|
+
signal,
|
|
854
|
+
dataRuntime: app.instance.appRuntime?.dataRuntime
|
|
510
855
|
});
|
|
511
856
|
}
|
|
512
857
|
function getResolvedRouteHandler(resolved) {
|
|
@@ -562,8 +907,8 @@ function applyNavigationTargets(requestId, path, options, redirectState, pathnam
|
|
|
562
907
|
for (const target of targets) {
|
|
563
908
|
const resolved = target.resolved;
|
|
564
909
|
if (!resolved || resolved.kind !== "redirect") continue;
|
|
565
|
-
const redirectTarget =
|
|
566
|
-
const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}
|
|
910
|
+
const redirectTarget = parseNavigationTarget(resolved.to);
|
|
911
|
+
const redirectHref = isCurrentOrigin(redirectTarget) ? `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}` : redirectTarget.href;
|
|
567
912
|
if (redirectHref === href) {
|
|
568
913
|
if (isDevelopmentEnvironment()) logger.warn(`Navigation guard redirected to the same path: ${redirectHref}`);
|
|
569
914
|
return;
|
|
@@ -578,26 +923,35 @@ function applyNavigationTargets(requestId, path, options, redirectState, pathnam
|
|
|
578
923
|
const matchedTargets = targets.filter((target) => target.resolved !== null);
|
|
579
924
|
if (matchedTargets.length === 0) {
|
|
580
925
|
if (isDevelopmentEnvironment()) logger.warn(`No route found for path: ${path}`);
|
|
926
|
+
if (href !== getWindowHref$1()) loadDocument(toDocumentUrl(href), getNavigationHistoryMode(options));
|
|
581
927
|
return;
|
|
582
928
|
}
|
|
583
929
|
if (isStaleRouteRequest(requestId)) return;
|
|
584
930
|
commitNavigationRoots(requestId, pathname, href, matchedTargets, () => {
|
|
585
931
|
saveScrollPosition(previousHref);
|
|
586
|
-
const
|
|
587
|
-
|
|
932
|
+
const historyMode = getNavigationHistoryMode(options);
|
|
933
|
+
const historyIndex = nextHistoryIndex(historyMode);
|
|
934
|
+
window.history[historyMode === "replace" ? "replaceState" : "pushState"]({
|
|
588
935
|
path: href,
|
|
589
936
|
askrHasState: Object.prototype.hasOwnProperty.call(options, "state"),
|
|
590
|
-
askrState: options.state
|
|
591
|
-
|
|
937
|
+
askrState: options.state,
|
|
938
|
+
askrIndex: historyIndex
|
|
939
|
+
}, "", toDocumentUrl(href));
|
|
940
|
+
commitHistoryIndex(historyIndex);
|
|
592
941
|
}, () => {
|
|
593
942
|
if (pathname !== previousPathname || parseTargetUrl(href).hash) applyNavigationScroll(options.scroll);
|
|
594
943
|
});
|
|
595
944
|
}
|
|
596
|
-
function applyPopStateNavigationTargets(requestId, previousHref,
|
|
945
|
+
function applyPopStateNavigationTargets(requestId, previousHref, historyIndex, pathname, href, state, targets, navigate) {
|
|
597
946
|
if (isStaleRouteRequest(requestId)) return;
|
|
598
947
|
const matchedTargets = targets.filter((target) => target.resolved !== null);
|
|
599
948
|
if (matchedTargets.length === 0) {
|
|
949
|
+
if (href.split("#")[0] === previousHref.split("#")[0]) {
|
|
950
|
+
commitHistoryIndex(historyIndex);
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
600
953
|
if (isDevelopmentEnvironment()) logger.warn(`No route found for path: ${pathname}`);
|
|
954
|
+
reloadDocument();
|
|
601
955
|
return;
|
|
602
956
|
}
|
|
603
957
|
for (const target of matchedTargets) {
|
|
@@ -607,13 +961,16 @@ function applyPopStateNavigationTargets(requestId, previousHref, previousState,
|
|
|
607
961
|
return;
|
|
608
962
|
}
|
|
609
963
|
saveScrollPosition(previousHref);
|
|
610
|
-
commitNavigationRoots(requestId, pathname, href, matchedTargets, () =>
|
|
611
|
-
|
|
612
|
-
|
|
964
|
+
commitNavigationRoots(requestId, pathname, href, matchedTargets, () => commitHistoryIndex(historyIndex), () => applyHistoryScroll(href, state), () => {
|
|
965
|
+
if (isStaleRouteRequest(requestId)) return;
|
|
966
|
+
if (!returnToRenderedHistoryEntry()) reloadDocument();
|
|
613
967
|
});
|
|
614
968
|
}
|
|
615
|
-
/**
|
|
616
|
-
*
|
|
969
|
+
/**
|
|
970
|
+
* Render every destination root, then commit all of them or none. Lifecycle
|
|
971
|
+
* work of the committed destination runs before history is updated, so a
|
|
972
|
+
* navigation it starts supersedes this one.
|
|
973
|
+
*/
|
|
617
974
|
function commitNavigationRoots(requestId, pathname, href, targets, updateHistory, updateScroll, restoreHistory) {
|
|
618
975
|
const previousPathname = getCurrentPathname();
|
|
619
976
|
const previousHref = getCurrentHref();
|
|
@@ -622,70 +979,58 @@ function commitNavigationRoots(requestId, pathname, href, targets, updateHistory
|
|
|
622
979
|
return {
|
|
623
980
|
target,
|
|
624
981
|
replaceLifetime,
|
|
625
|
-
previousPathname: target.app.pathname,
|
|
626
|
-
previousHref: target.app.href,
|
|
627
982
|
prepared: prepareNavigationRoot(target, href, replaceLifetime)
|
|
628
983
|
};
|
|
629
984
|
});
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
root.target.app.href = root.previousHref;
|
|
639
|
-
}
|
|
640
|
-
setCurrentRouteLocation(previousPathname, previousHref);
|
|
641
|
-
try {
|
|
642
|
-
restoreHistory?.();
|
|
643
|
-
} catch (error) {
|
|
644
|
-
errors.push(error);
|
|
645
|
-
}
|
|
646
|
-
reportRouteCleanupErrors(errors);
|
|
647
|
-
},
|
|
648
|
-
settle() {
|
|
649
|
-
const errors = [];
|
|
650
|
-
for (const root of roots) errors.push(...root.prepared.retire());
|
|
651
|
-
reportRouteCleanupErrors(errors);
|
|
652
|
-
},
|
|
653
|
-
complete() {
|
|
654
|
-
if (isStaleRouteRequest(requestId)) return;
|
|
655
|
-
try {
|
|
656
|
-
updateHistory();
|
|
657
|
-
setCurrentRouteLocation(pathname, href);
|
|
658
|
-
syncRegisteredRouteSnapshot();
|
|
659
|
-
reconcileNavigationMetadata(targets);
|
|
660
|
-
updateScroll();
|
|
661
|
-
} catch (error) {
|
|
662
|
-
completionFailure = { error };
|
|
663
|
-
throw error;
|
|
664
|
-
} finally {
|
|
665
|
-
for (const root of roots) root.prepared.publish();
|
|
666
|
-
}
|
|
985
|
+
const rollback = () => {
|
|
986
|
+
const errors = [];
|
|
987
|
+
for (let index = roots.length - 1; index >= 0; index--) errors.push(...roots[index].prepared.rollback());
|
|
988
|
+
setCurrentRouteLocation(previousPathname, previousHref);
|
|
989
|
+
try {
|
|
990
|
+
restoreHistory?.();
|
|
991
|
+
} catch (error) {
|
|
992
|
+
errors.push(error);
|
|
667
993
|
}
|
|
668
|
-
|
|
994
|
+
reportRouteCleanupErrors(errors);
|
|
995
|
+
};
|
|
669
996
|
try {
|
|
670
|
-
for (const replaceLifetime of [true, false])
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
997
|
+
for (const replaceLifetime of [true, false]) for (const root of roots) if (root.replaceLifetime === replaceLifetime) root.prepared.apply();
|
|
998
|
+
} catch (error) {
|
|
999
|
+
rollback();
|
|
1000
|
+
logger.error("[Askr] navigation failed:", error);
|
|
1001
|
+
throw error;
|
|
1002
|
+
}
|
|
1003
|
+
if (isStaleRouteRequest(requestId)) {
|
|
1004
|
+
rollback();
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
const committedFailures = [];
|
|
1008
|
+
for (const root of roots) {
|
|
1009
|
+
const result = root.prepared.publish();
|
|
1010
|
+
if (result.aborted) {
|
|
1011
|
+
rollback();
|
|
1012
|
+
const failure = result.errors[0];
|
|
1013
|
+
logger.error("[Askr] navigation failed:", failure);
|
|
1014
|
+
throw failure;
|
|
678
1015
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1016
|
+
committedFailures.push(...result.errors);
|
|
1017
|
+
syncAppRegistrationLocation(root.target.app, pathname, href);
|
|
1018
|
+
}
|
|
1019
|
+
for (const failure of committedFailures) reportUncaughtErrorLater(failure);
|
|
1020
|
+
const retired = [];
|
|
1021
|
+
for (const root of roots) retired.push(...root.prepared.retire());
|
|
1022
|
+
if (retired.length) reportUncaughtErrorLater(retired.length === 1 ? retired[0] : new AggregateError(retired, "Route cleanup failed"));
|
|
1023
|
+
flushSync();
|
|
1024
|
+
if (isStaleRouteRequest(requestId)) return;
|
|
1025
|
+
try {
|
|
1026
|
+
updateHistory();
|
|
1027
|
+
setCurrentRouteLocation(pathname, href);
|
|
1028
|
+
syncRegisteredRouteSnapshot();
|
|
1029
|
+
reconcileNavigationMetadata(targets);
|
|
1030
|
+
updateScroll();
|
|
684
1031
|
} catch (error) {
|
|
685
1032
|
logger.error("[Askr] navigation failed:", error);
|
|
686
1033
|
throw error;
|
|
687
|
-
} finally {
|
|
688
|
-
discardTransaction(transaction);
|
|
689
1034
|
}
|
|
690
1035
|
}
|
|
691
1036
|
//#endregion
|
|
@@ -708,29 +1053,43 @@ function ensureNavigationRegistryHost() {
|
|
|
708
1053
|
navigationRegistryHosted = true;
|
|
709
1054
|
configureNavigationRegistryHost({ cancelRouteRequests });
|
|
710
1055
|
}
|
|
711
|
-
/**
|
|
712
|
-
|
|
1056
|
+
/**
|
|
1057
|
+
* Navigate the client-side router using the History API.
|
|
1058
|
+
*
|
|
1059
|
+
* A string is a logical path: a root-relative path gains the registry
|
|
1060
|
+
* `basePath`. A typed destination from `to()` already carries its public href.
|
|
1061
|
+
* A target on another origin is handed to the browser.
|
|
1062
|
+
*/
|
|
1063
|
+
function navigate(target, options = {}) {
|
|
1064
|
+
navigateToPublicHref(typeof target === "string" ? addLogicalRouteBasePath(target, getActiveRouteBasePath()) : target.href, options);
|
|
1065
|
+
}
|
|
1066
|
+
/** Navigate to a public (already mounted) href without adding the `basePath`. */
|
|
1067
|
+
function navigateToPublicHref(href, options = {}) {
|
|
713
1068
|
ensureNavigationRegistryHost();
|
|
714
1069
|
if (typeof window === "undefined") return;
|
|
715
1070
|
prepareNavigationFocus();
|
|
716
|
-
|
|
717
|
-
const initialTarget = parseTargetUrl(
|
|
1071
|
+
cancelHistoryReturn();
|
|
1072
|
+
const initialTarget = parseTargetUrl(href);
|
|
718
1073
|
const redirectState = {
|
|
719
1074
|
redirects: 0,
|
|
720
1075
|
visited: /* @__PURE__ */ new Set([`${initialTarget.pathname}${initialTarget.search}${initialTarget.hash}`])
|
|
721
1076
|
};
|
|
722
|
-
if (
|
|
1077
|
+
if (isFlushing()) {
|
|
723
1078
|
queueMicrotask(() => {
|
|
724
|
-
navigateWithRedirectState(
|
|
1079
|
+
navigateWithRedirectState(href, options, redirectState);
|
|
725
1080
|
});
|
|
726
1081
|
return;
|
|
727
1082
|
}
|
|
728
|
-
navigateWithRedirectState(
|
|
1083
|
+
navigateWithRedirectState(href, options, redirectState);
|
|
729
1084
|
}
|
|
730
1085
|
function navigateWithRedirectState(path, options, redirectState) {
|
|
731
1086
|
if (typeof window === "undefined") return;
|
|
732
1087
|
const request = beginRouteRequest();
|
|
733
|
-
const target =
|
|
1088
|
+
const target = parseNavigationTarget(path);
|
|
1089
|
+
if (!isCurrentOrigin(target)) {
|
|
1090
|
+
loadDocument(target.href, getNavigationHistoryMode(options));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
734
1093
|
const pathname = target.pathname;
|
|
735
1094
|
const href = `${target.pathname}${target.search}${target.hash}`;
|
|
736
1095
|
const resolvedTargets = resolveNavigationTargetsForApps(pathname, href, request.signal);
|
|
@@ -751,15 +1110,20 @@ function navigateWithRedirectState(path, options, redirectState) {
|
|
|
751
1110
|
applyNavigationTargets(request.id, path, options, redirectState, pathname, href, resolvedTargets, navigateWithRedirectState);
|
|
752
1111
|
}
|
|
753
1112
|
function handlePopState(event) {
|
|
1113
|
+
if (consumeHistoryReturn(event.state)) {
|
|
1114
|
+
syncRegisteredRouteSnapshot();
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
754
1117
|
beginHistoryFocusRestoration();
|
|
755
1118
|
const request = beginRouteRequest();
|
|
756
1119
|
const previousHref = getCurrentHref();
|
|
1120
|
+
const historyIndex = landOnHistoryEntry(event.state);
|
|
757
1121
|
const pathname = window.location.pathname;
|
|
758
1122
|
const href = `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
759
1123
|
if (!hasRegisteredApps()) return;
|
|
760
1124
|
const applyResolved = (targets) => {
|
|
761
1125
|
try {
|
|
762
|
-
applyPopStateNavigationTargets(request.id, previousHref,
|
|
1126
|
+
applyPopStateNavigationTargets(request.id, previousHref, historyIndex, pathname, href, event.state, targets, navigateToPublicHref);
|
|
763
1127
|
} catch (error) {
|
|
764
1128
|
logger.error("[Askr] popstate navigation failed:", error);
|
|
765
1129
|
}
|
|
@@ -771,6 +1135,7 @@ function handlePopState(event) {
|
|
|
771
1135
|
}, (error) => {
|
|
772
1136
|
if (isStaleRouteRequest(request.id)) return;
|
|
773
1137
|
logger.error("[Askr] popstate navigation failed:", error);
|
|
1138
|
+
if (!returnToRenderedHistoryEntry()) reloadDocument();
|
|
774
1139
|
});
|
|
775
1140
|
return;
|
|
776
1141
|
}
|
|
@@ -778,11 +1143,13 @@ function handlePopState(event) {
|
|
|
778
1143
|
}
|
|
779
1144
|
function initializeNavigation() {
|
|
780
1145
|
ensureNavigationRegistryHost();
|
|
781
|
-
if (typeof window === "undefined"
|
|
1146
|
+
if (typeof window === "undefined") return;
|
|
1147
|
+
if (getRegisteredAppsSnapshot().length <= 1) initializeHistoryIndex();
|
|
1148
|
+
if (navigationInitialized) return;
|
|
782
1149
|
navigationInitialized = true;
|
|
783
1150
|
window.addEventListener("popstate", handlePopState);
|
|
784
1151
|
document.addEventListener("pointerdown", captureNavigationFocus, true);
|
|
785
1152
|
document.addEventListener("click", releaseNavigationFocusCapture);
|
|
786
1153
|
}
|
|
787
1154
|
//#endregion
|
|
788
|
-
export { configureRootUpdateHost as _,
|
|
1155
|
+
export { currentRoute as C, clearStagedAppRenderRouteLocation as D, configureRootUpdateHost as E, createAppRenderRuntime as O, unregisterAppInstance as S, setServerLocation as T, registerAppInstance as _, nextHistoryIndex as a, syncRegisteredRouteSnapshot as b, reconcileRouteMeta as c, configureScrollRestoration as d, saveScrollPosition as f, parseTargetUrl as g, getWindowHref$1 as h, commitHistoryIndex as i, stageAppRenderRouteLocation as k, resolveRouteMeta as l, getRegisteredAppsSnapshot as m, navigate as n, readHistoryIndex as o, getCurrentHref as p, navigateToPublicHref as r, loadDocument as s, initializeNavigation as t, serializeRouteMeta as u, setCurrentRouteLocation as v, onRouteChange as w, toDocumentUrl as x, syncAppRegistrationLocation as y };
|