@askrjs/askr 0.0.64 → 0.0.65
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 +5 -5
- package/dist/authoring-CXMPffwN.js +200 -0
- package/dist/benchmark.js +3 -3
- package/dist/boot/index.d.ts +8 -32
- package/dist/boot/index.js +1 -1
- package/dist/{boot-BwqMApN4.js → boot-OnYuVVfg.js} +30 -66
- package/dist/{deferred-vBQJwHl5.js → deferred-B71c2eio.js} +50 -9
- package/dist/{navigate-CaFA0DdW.js → navigate-DTZ2SM8_.js} +7 -272
- package/dist/{route-matching-vJpvLVbX.js → route-matching-C93To416.js} +2 -2
- package/dist/router/index.d.ts +3 -9
- package/dist/router/index.js +4 -4
- package/dist/{router-Dz7ZoLja.d.ts → router-eRoaj11J.d.ts} +7 -4
- package/dist/ssg/index.d.ts +8 -33
- package/dist/ssg/index.js +34 -36
- package/dist/ssr/index.d.ts +3 -44
- package/dist/ssr/index.js +1 -1
- package/dist/{ssr-CAYo-kNy.js → ssr-C0hzqhY8.js} +19 -25
- package/dist/testing/index.d.ts +4 -5
- package/dist/testing/index.js +7 -10
- package/dist/{verify-hydration-DfsBcGSf.js → verify-hydration-BdKE9Hi8.js} +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,16 +54,16 @@ Startup belongs in `@askrjs/askr/boot`. Routing helpers live in
|
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
56
|
import { createSPA } from '@askrjs/askr/boot';
|
|
57
|
-
import {
|
|
57
|
+
import { createRouteRegistry, route } from '@askrjs/askr/router';
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
route('/',
|
|
61
|
-
route('/about',
|
|
59
|
+
const registry = createRouteRegistry(() => {
|
|
60
|
+
route('/', Home);
|
|
61
|
+
route('/about', About);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
createSPA({
|
|
65
65
|
root: document.body,
|
|
66
|
-
|
|
66
|
+
registry,
|
|
67
67
|
});
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { f as getCurrentComponentInstance } from "./component-scope-CC3cojX9.js";
|
|
2
|
+
import { T as getExecutionModel, x as createRouteHandler } from "./deferred-B71c2eio.js";
|
|
3
|
+
import { A as insertRecordSorted, C as getCurrentPathPrefix, I as computeRank, L as normalizeRouteSegmentName, M as pushRegistrationScope, O as hasActivePageScope, R as parseSegments, S as getCurrentPageScope, _ as getCurrentInheritedAuthRequirements, b as getCurrentLayoutChain, f as addRouteToStores, p as assertRouteRegistrationUnlocked, v as getCurrentInheritedMeta, w as getCurrentScopeKind, x as getCurrentPageChain, y as getCurrentInheritedPolicies } from "./route-matching-C93To416.js";
|
|
4
|
+
import { allOf } from "@askrjs/auth";
|
|
5
|
+
//#region src/router/access.ts
|
|
6
|
+
function compileNodePolicies(node) {
|
|
7
|
+
return node.policies ? [...node.policies] : [];
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/router/authoring.ts
|
|
11
|
+
function validateRoutePath(path) {
|
|
12
|
+
if (!path.startsWith("/")) throw new Error(`Route path must begin with "/". Got: "${path}"`);
|
|
13
|
+
if (/\/{2,}/.test(path)) throw new Error("Route path cannot contain consecutive slashes.");
|
|
14
|
+
if (/:([^/{}]+)/.test(path)) {
|
|
15
|
+
const suggested = path.replace(/:([^/{}]+)/g, "{$1}");
|
|
16
|
+
throw new Error(`Route parameter syntax uses {name} interpolation, not :name. Use "${suggested}" instead of "${path}".`);
|
|
17
|
+
}
|
|
18
|
+
const segments = path.split("/").filter(Boolean);
|
|
19
|
+
const seenParamNames = /* @__PURE__ */ new Set();
|
|
20
|
+
for (let index = 0; index < segments.length; index++) {
|
|
21
|
+
const segment = segments[index];
|
|
22
|
+
if (segment === "*") continue;
|
|
23
|
+
const hasOpenBrace = segment.includes("{");
|
|
24
|
+
const hasCloseBrace = segment.includes("}");
|
|
25
|
+
if (!hasOpenBrace && !hasCloseBrace) continue;
|
|
26
|
+
if (!(segment.startsWith("{") && segment.endsWith("}"))) throw new Error("Route parameter segments must use complete {name} interpolation.");
|
|
27
|
+
const rawParamName = normalizeRouteSegmentName(segment.slice(1, -1));
|
|
28
|
+
const isSplat = rawParamName.startsWith("*");
|
|
29
|
+
const paramName = isSplat ? normalizeRouteSegmentName(rawParamName.slice(1)) : rawParamName;
|
|
30
|
+
if (!paramName) throw new Error(isSplat ? "Route splat parameter name cannot be empty." : "Route parameter name cannot be empty.");
|
|
31
|
+
if (isSplat && paramName === "*") throw new Error("Route named splat parameter name cannot be \"*\".");
|
|
32
|
+
if (isSplat && index !== segments.length - 1) throw new Error("Route named splat parameters must be the final segment.");
|
|
33
|
+
if (seenParamNames.has(paramName)) throw new Error(`Route path cannot reuse duplicate parameter name "${paramName}".`);
|
|
34
|
+
seenParamNames.add(paramName);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function normalizeAbsoluteRoutePath(path) {
|
|
38
|
+
if (!path || path === "/") return "/";
|
|
39
|
+
return (path.endsWith("/") ? path.slice(0, -1) : path) || "/";
|
|
40
|
+
}
|
|
41
|
+
function joinRoutePaths(prefix, path) {
|
|
42
|
+
const normalizedPrefix = normalizeAbsoluteRoutePath(prefix || "/");
|
|
43
|
+
const normalizedPath = path.replace(/^\/+|\/+$/g, "");
|
|
44
|
+
if (!normalizedPath) return normalizedPrefix;
|
|
45
|
+
return normalizedPrefix === "/" ? `/${normalizedPath}` : `${normalizedPrefix}/${normalizedPath}`;
|
|
46
|
+
}
|
|
47
|
+
function resolvePageScopePath(path) {
|
|
48
|
+
if (!path) throw new Error("page(path, Component, fn) requires a non-empty path.");
|
|
49
|
+
if (path.startsWith("/")) {
|
|
50
|
+
validateRoutePath(path);
|
|
51
|
+
return normalizeAbsoluteRoutePath(path);
|
|
52
|
+
}
|
|
53
|
+
return joinRoutePaths(getCurrentPathPrefix(), path);
|
|
54
|
+
}
|
|
55
|
+
function resolveIndexPath() {
|
|
56
|
+
return normalizeAbsoluteRoutePath(getCurrentPathPrefix() || "/");
|
|
57
|
+
}
|
|
58
|
+
function resolveRouteRegistrationPath(path) {
|
|
59
|
+
if (path.startsWith("/")) {
|
|
60
|
+
if (hasActivePageScope()) throw new Error(`Child route paths inside page() must be relative. Use "${path.slice(1)}" instead of "${path}".`);
|
|
61
|
+
validateRoutePath(path);
|
|
62
|
+
return normalizeAbsoluteRoutePath(path);
|
|
63
|
+
}
|
|
64
|
+
const prefix = getCurrentPathPrefix();
|
|
65
|
+
if (!prefix) throw new Error(`Route path must begin with "/". Got: "${path}"`);
|
|
66
|
+
return joinRoutePaths(prefix, path);
|
|
67
|
+
}
|
|
68
|
+
function pushGroupScope(options, fn) {
|
|
69
|
+
const policies = compileNodePolicies(options);
|
|
70
|
+
pushRegistrationScope({
|
|
71
|
+
kind: "group",
|
|
72
|
+
pathPrefix: getCurrentPathPrefix(),
|
|
73
|
+
layout: options.layout,
|
|
74
|
+
auth: options.auth,
|
|
75
|
+
policies,
|
|
76
|
+
meta: options.meta
|
|
77
|
+
}, fn);
|
|
78
|
+
}
|
|
79
|
+
function pushPageScope(path, Component, options, fn) {
|
|
80
|
+
if (hasActivePageScope()) throw new Error("page() cannot be nested inside another page(). Use route() for child leaves or group() for inherited behavior inside the existing page scope.");
|
|
81
|
+
const policies = compileNodePolicies(options);
|
|
82
|
+
pushRegistrationScope({
|
|
83
|
+
kind: "page",
|
|
84
|
+
pathPrefix: resolvePageScopePath(path),
|
|
85
|
+
page: Component,
|
|
86
|
+
hasIndex: false,
|
|
87
|
+
auth: options.auth,
|
|
88
|
+
policies,
|
|
89
|
+
meta: options.meta
|
|
90
|
+
}, fn);
|
|
91
|
+
}
|
|
92
|
+
function normalizeRouteOptions(options) {
|
|
93
|
+
if (!options) return;
|
|
94
|
+
const loader = options.loader;
|
|
95
|
+
const preload = options.preload;
|
|
96
|
+
const policies = compileNodePolicies(options);
|
|
97
|
+
if (!loader && !preload && !options.entries && policies.length === 0 && !options.title && !options.namespace && !options.search && !options.meta && !options.actions && options.auth === void 0) return;
|
|
98
|
+
return {
|
|
99
|
+
...loader ? { loader } : {},
|
|
100
|
+
...preload ? { preload } : {},
|
|
101
|
+
...options.entries ? { entries: options.entries } : {},
|
|
102
|
+
...options.auth !== void 0 ? { auth: options.auth } : {},
|
|
103
|
+
...policies.length > 0 ? { policies } : {},
|
|
104
|
+
...options.title ? { title: options.title } : {},
|
|
105
|
+
...options.namespace ? { namespace: options.namespace } : {},
|
|
106
|
+
...options.search ? { search: options.search } : {},
|
|
107
|
+
...options.meta ? { meta: options.meta } : {},
|
|
108
|
+
...options.actions ? { actions: options.actions } : {}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function registerRouteAtResolvedPath(path, Component, options, metadata) {
|
|
112
|
+
validateRoutePath(path);
|
|
113
|
+
const chain = getCurrentLayoutChain();
|
|
114
|
+
const pageChain = getCurrentPageChain();
|
|
115
|
+
const segments = parseSegments(path);
|
|
116
|
+
const rank = computeRank(segments);
|
|
117
|
+
const isFallback = metadata?.isFallback ?? path === "/*";
|
|
118
|
+
const comp = Component;
|
|
119
|
+
const normalizedOptions = normalizeRouteOptions(options);
|
|
120
|
+
const policies = [...getCurrentInheritedPolicies(), ...normalizedOptions?.policies ?? []];
|
|
121
|
+
const authRequirements = [...getCurrentInheritedAuthRequirements(), ...normalizedOptions?.auth ? [normalizedOptions.auth] : []];
|
|
122
|
+
const auth = authRequirements.length === 0 ? void 0 : authRequirements.length === 1 ? authRequirements[0] : allOf(...authRequirements);
|
|
123
|
+
const metaChain = [...getCurrentInheritedMeta(), ...normalizedOptions?.meta ? [normalizedOptions.meta] : []];
|
|
124
|
+
const handler = createRouteHandler(comp, pageChain, chain);
|
|
125
|
+
const renderHandler = createRouteHandler(comp, pageChain, chain, true);
|
|
126
|
+
insertRecordSorted({
|
|
127
|
+
path,
|
|
128
|
+
component: comp,
|
|
129
|
+
segments,
|
|
130
|
+
rank,
|
|
131
|
+
layoutChain: chain,
|
|
132
|
+
pageChain,
|
|
133
|
+
options: normalizedOptions ? {
|
|
134
|
+
...normalizedOptions,
|
|
135
|
+
...auth ? { auth } : {},
|
|
136
|
+
...policies.length > 0 ? { policies } : {}
|
|
137
|
+
} : policies.length > 0 ? {
|
|
138
|
+
policies,
|
|
139
|
+
...auth ? { auth } : {}
|
|
140
|
+
} : auth ? { auth } : {},
|
|
141
|
+
...metaChain.length > 0 ? { metaChain } : {},
|
|
142
|
+
isFallback,
|
|
143
|
+
handler,
|
|
144
|
+
renderHandler,
|
|
145
|
+
...metadata?.fallbackPrefix ? { fallbackPrefix: metadata.fallbackPrefix } : {}
|
|
146
|
+
});
|
|
147
|
+
addRouteToStores({
|
|
148
|
+
path,
|
|
149
|
+
handler,
|
|
150
|
+
namespace: normalizedOptions?.namespace ?? options?.namespace,
|
|
151
|
+
...metadata?.fallbackPrefix ? { fallbackPrefix: metadata.fallbackPrefix } : {}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function group(options, fn) {
|
|
155
|
+
pushGroupScope(options, fn);
|
|
156
|
+
}
|
|
157
|
+
function page(path, Component, optionsOrFn, maybeFn) {
|
|
158
|
+
const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
|
|
159
|
+
const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
|
|
160
|
+
if (typeof Component !== "function") throw new Error("page(path, Component, fn) requires a component function as the second argument.");
|
|
161
|
+
if (typeof fn !== "function") throw new Error("page(path, Component, fn) requires a route definition callback as the final argument.");
|
|
162
|
+
pushPageScope(path, Component, options, fn);
|
|
163
|
+
}
|
|
164
|
+
function index(Component, options) {
|
|
165
|
+
const pageScope = getCurrentPageScope();
|
|
166
|
+
if (pageScope?.hasIndex) throw new Error("page() cannot declare multiple index routes.");
|
|
167
|
+
if (pageScope) pageScope.hasIndex = true;
|
|
168
|
+
registerRouteAtResolvedPath(resolveIndexPath(), Component, options);
|
|
169
|
+
}
|
|
170
|
+
function fallback(Component) {
|
|
171
|
+
if (hasActivePageScope()) {
|
|
172
|
+
if (getCurrentScopeKind() !== "page") throw new Error("fallback() inside page() must be declared directly in the page scope, not inside nested group().");
|
|
173
|
+
registerRouteAtResolvedPath(`${getCurrentPathPrefix()}/*`, Component, void 0, {
|
|
174
|
+
isFallback: true,
|
|
175
|
+
fallbackPrefix: getCurrentPathPrefix()
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (!(getCurrentInheritedPolicies().length === 0 && getCurrentInheritedAuthRequirements().length === 0)) throw new Error("fallback() can only be registered at the root scope. Use route(\"/*\", Component) if you need compatibility behavior.");
|
|
180
|
+
registerRouteAtResolvedPath("/*", Component, void 0, {
|
|
181
|
+
isFallback: true,
|
|
182
|
+
fallbackPrefix: "/"
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function route(path, Component, options) {
|
|
186
|
+
if (typeof path === "undefined") throw new Error("route() is only for route registration. Use currentRoute() inside components.");
|
|
187
|
+
if (getExecutionModel() === "islands") throw new Error("Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead.");
|
|
188
|
+
const currentInst = getCurrentComponentInstance();
|
|
189
|
+
if (currentInst && currentInst.ssr) throw new Error("route() cannot be called during SSR rendering. Register routes at module load time instead.");
|
|
190
|
+
assertRouteRegistrationUnlocked();
|
|
191
|
+
if (typeof Component !== "function") throw new Error("route(path, Component) requires a component function as the second argument. Passing JSX elements or VNodes directly is not supported.");
|
|
192
|
+
if (options?.search && options.search.kind !== "object") throw new Error("route search must be an object schema.");
|
|
193
|
+
registerRouteAtResolvedPath(resolveRouteRegistrationPath(path), Component, options);
|
|
194
|
+
return Object.freeze({
|
|
195
|
+
path: resolveRouteRegistrationPath(path),
|
|
196
|
+
...options?.search ? { searchSchema: options.search } : {}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
export { route as a, page as i, group as n, index as r, fallback as t };
|
package/dist/benchmark.js
CHANGED
|
@@ -4,7 +4,7 @@ import { t as state } from "./state-Cc5HumQ0.js";
|
|
|
4
4
|
import { t as selector } from "./selector-pApF_lGd.js";
|
|
5
5
|
import { jsx, jsxs } from "./jsx-runtime.js";
|
|
6
6
|
import { i as For } from "./control-KVP6LMEQ.js";
|
|
7
|
-
import { t as createIsland } from "./boot-
|
|
7
|
+
import { t as createIsland } from "./boot-OnYuVVfg.js";
|
|
8
8
|
//#region src/bench/components/benchmark-row.tsx
|
|
9
9
|
function BenchmarkRow({ item, isSelected, onSelect, onRemove }) {
|
|
10
10
|
return /* @__PURE__ */ jsxs("tr", {
|
|
@@ -63,8 +63,8 @@ function BenchmarkTable({ rows, isSelected, onSelect, onRemove }) {
|
|
|
63
63
|
installRendererBridge();
|
|
64
64
|
const benchmarkMetadata = {
|
|
65
65
|
packageName: "@askrjs/askr",
|
|
66
|
-
packageVersion: "0.0.
|
|
67
|
-
buildLabel: "0.0.
|
|
66
|
+
packageVersion: "0.0.65",
|
|
67
|
+
buildLabel: "0.0.65-local"
|
|
68
68
|
};
|
|
69
69
|
function getBenchmarkMetadata() {
|
|
70
70
|
return { ...benchmarkMetadata };
|
package/dist/boot/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as ComponentFunction } from "../control-CMzFPMjr.js";
|
|
2
2
|
import { v as DataRuntime } from "../index-Bki2rqcr.js";
|
|
3
|
-
import {
|
|
3
|
+
import { N as RouteRegistry, p as RouteAuthOptions } from "../router-eRoaj11J.js";
|
|
4
4
|
import { d as ScrollRestorationOptions } from "../navigate-BVDCzOuB.js";
|
|
5
5
|
|
|
6
6
|
//#region src/boot/types.d.ts
|
|
@@ -17,28 +17,10 @@ type IslandsConfig = {
|
|
|
17
17
|
};
|
|
18
18
|
type BootRouteSource = {
|
|
19
19
|
registry: RouteRegistry;
|
|
20
|
-
manifest?: RouteManifest;
|
|
21
|
-
routes?: Route[];
|
|
22
|
-
} | {
|
|
23
|
-
manifest: RouteManifest;
|
|
24
|
-
registry?: RouteRegistry;
|
|
25
|
-
routes?: Route[];
|
|
26
|
-
} | {
|
|
27
|
-
manifest?: RouteManifest;
|
|
28
|
-
registry?: RouteRegistry;
|
|
29
|
-
routes: Route[];
|
|
30
20
|
};
|
|
31
21
|
type SPAConfig = BootRouteSource & {
|
|
32
22
|
root: Element | string;
|
|
33
|
-
cspNonce?: string;
|
|
34
|
-
/**
|
|
35
|
-
* Preferred: pass a route registry built via `createRouteRegistry(() => { ... })`.
|
|
36
|
-
* ```ts
|
|
37
|
-
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
38
|
-
* const registry = createRouteRegistry(() => { ... });
|
|
39
|
-
* await createSPA({ root: '#app', registry });
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
23
|
+
cspNonce?: string; /** Pass a route registry built via `createRouteRegistry(() => { ... })`. */
|
|
42
24
|
auth?: RouteAuthOptions;
|
|
43
25
|
scrollRestoration?: boolean | ScrollRestorationOptions;
|
|
44
26
|
cleanupStrict?: boolean;
|
|
@@ -47,7 +29,7 @@ type SPAConfig = BootRouteSource & {
|
|
|
47
29
|
type HydrateSPAConfig = BootRouteSource & {
|
|
48
30
|
root: Element | string;
|
|
49
31
|
cspNonce?: string;
|
|
50
|
-
dataRuntime?: DataRuntime; /**
|
|
32
|
+
dataRuntime?: DataRuntime; /** Pass the same explicit route registry used for the server render. */
|
|
51
33
|
auth?: RouteAuthOptions;
|
|
52
34
|
scrollRestoration?: boolean | ScrollRestorationOptions;
|
|
53
35
|
cleanupStrict?: boolean;
|
|
@@ -75,23 +57,17 @@ declare function createIsland(config: IslandConfig): void;
|
|
|
75
57
|
*/
|
|
76
58
|
declare function createIslands(config: IslandsConfig): void;
|
|
77
59
|
/**
|
|
78
|
-
* createSPA: Initializes router and mounts the app with
|
|
79
|
-
*
|
|
80
|
-
* Preferred usage with manifest:
|
|
60
|
+
* createSPA: Initializes the router and mounts the app with an explicit route registry.
|
|
81
61
|
* ```ts
|
|
82
|
-
* import {
|
|
83
|
-
*
|
|
62
|
+
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
63
|
+
* const registry = createRouteRegistry(() => { ... });
|
|
64
|
+
* await createSPA({ root: '#app', registry });
|
|
84
65
|
* ```
|
|
85
66
|
*
|
|
86
|
-
* Legacy usage with plain routes array (still supported):
|
|
87
|
-
* ```ts
|
|
88
|
-
* await createSPA({ root: '#app', routes: getRoutes() });
|
|
89
|
-
* ```
|
|
90
67
|
*/
|
|
91
68
|
declare function createSPA(config: SPAConfig): Promise<void>;
|
|
92
69
|
/**
|
|
93
|
-
* hydrateSPA: Hydrate server-rendered HTML.
|
|
94
|
-
* Accepts either a `manifest` (preferred) or a legacy `routes` array.
|
|
70
|
+
* hydrateSPA: Hydrate server-rendered HTML with an explicit route registry.
|
|
95
71
|
*/
|
|
96
72
|
declare function hydrateSPA(config: HydrateSPAConfig): Promise<void>;
|
|
97
73
|
//#endregion
|
package/dist/boot/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as cleanupApp, i as hydrateSPA, n as createIslands, o as hasApp, r as createSPA, t as createIsland } from "../boot-
|
|
1
|
+
import { a as cleanupApp, i as hydrateSPA, n as createIslands, o as hasApp, r as createSPA, t as createIsland } from "../boot-OnYuVVfg.js";
|
|
2
2
|
export { cleanupApp, createIsland, createIslands, createSPA, hasApp, hydrateSPA };
|
|
@@ -4,14 +4,14 @@ import { n as createComponentInstance, r as executeComponent } from "./component
|
|
|
4
4
|
import { h as cleanupComponent, m as isPromiseLike } from "./component-lifecycle-DEMPihoZ.js";
|
|
5
5
|
import { a as withIntrinsicHydrationAdoption, i as registerDeferredHydrationBoundary, n as activateHydrationBoundary$1, o as setStaticChildSlotsCacheEnabled, r as clearDeferredHydrationBoundaries, t as installRendererBridge } from "./renderer-aUD-acDe.js";
|
|
6
6
|
import { _ as teardownNodeSubtree, d as removeAllListeners } from "./cleanup-B6KovDZZ.js";
|
|
7
|
-
import {
|
|
7
|
+
import { c as _applyManifest, d as getRouteRenderContext, f as getRouteRenderData, m as resolveRouteRequest, o as reviveDeferredValue, p as hasRouteRenderData, w as assertExecutionModel, y as pageRenderEnvelope } from "./deferred-B71c2eio.js";
|
|
8
8
|
import { a as startHydrationRenderPhase, s as stopHydrationRenderPhase } from "./render-context-Clw3rFiO.js";
|
|
9
9
|
import { a as disposeDefaultPortalScope, t as DefaultPortal } from "./portal-D2tPWNrC.js";
|
|
10
10
|
import { r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-C-8E85El.js";
|
|
11
11
|
import { i as getDefaultDataRuntime } from "./data-runtime-SJ1BSUvk.js";
|
|
12
12
|
import { a as hydrateDataRuntime } from "./query-registry-CQIT4mXr.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { d as _setActiveRouteAuthOptions, j as lockRouteRegistration, k as hasRegisteredRoutes, m as clearRouteState } from "./route-matching-C93To416.js";
|
|
14
|
+
import { a as resolveRouteMeta, c as registerAppInstance, d as setServerLocation, f as createAppRenderRuntime, i as reconcileRouteMeta, l as unregisterAppInstance, s as configureScrollRestoration, t as initializeNavigation } from "./navigate-DTZ2SM8_.js";
|
|
15
15
|
import "./ssr-CbQO5xd3.js";
|
|
16
16
|
//#region src/boot/hydration.ts
|
|
17
17
|
const DEFERRED_PAYLOAD = "__askr_deferred__";
|
|
@@ -364,7 +364,7 @@ function cleanupApp(root) {
|
|
|
364
364
|
const wasRoutedRoot = routedRoots.delete(rootElement);
|
|
365
365
|
instancesByRoot.delete(rootElement);
|
|
366
366
|
clearRootCleanupCallbacks(rootElement);
|
|
367
|
-
if (wasRoutedRoot && routedRoots.size === 0)
|
|
367
|
+
if (wasRoutedRoot && routedRoots.size === 0) clearRouteState();
|
|
368
368
|
try {
|
|
369
369
|
delete rootElement[CLEANUP_SYMBOL];
|
|
370
370
|
} catch {}
|
|
@@ -408,21 +408,11 @@ async function resolveInitialRoute(auth, source) {
|
|
|
408
408
|
for (let redirects = 0; redirects <= MAX_INITIAL_ROUTE_REDIRECTS; redirects++) {
|
|
409
409
|
if (visited.has(href)) throw new Error(`[Askr] Route redirect cycle detected at ${href}.`);
|
|
410
410
|
visited.add(href);
|
|
411
|
-
const resolved = source?.
|
|
412
|
-
manifest: source.manifest,
|
|
411
|
+
const resolved = source?.registry ? await resolveRouteRequest(href, {
|
|
412
|
+
manifest: source.registry.manifest,
|
|
413
413
|
auth,
|
|
414
414
|
load: source.load
|
|
415
|
-
}) :
|
|
416
|
-
const match = resolveRouteFromRoutes(path, source.routes);
|
|
417
|
-
if (!match) return null;
|
|
418
|
-
const lazyImport = _preloadRouteHandler(match.handler);
|
|
419
|
-
const result = {
|
|
420
|
-
kind: "render",
|
|
421
|
-
handler: match.handler,
|
|
422
|
-
params: match.params
|
|
423
|
-
};
|
|
424
|
-
return lazyImport ? lazyImport.then(() => result) : result;
|
|
425
|
-
})() : await resolveRouteRequest(href, { auth });
|
|
415
|
+
}) : await resolveRouteRequest(href, { auth });
|
|
426
416
|
if (typeof window === "undefined" || !resolved || resolved.kind !== "redirect") return {
|
|
427
417
|
path,
|
|
428
418
|
href,
|
|
@@ -473,47 +463,34 @@ function createIslands(config) {
|
|
|
473
463
|
});
|
|
474
464
|
}
|
|
475
465
|
/**
|
|
476
|
-
* createSPA: Initializes router and mounts the app with
|
|
477
|
-
*
|
|
478
|
-
* Preferred usage with manifest:
|
|
466
|
+
* createSPA: Initializes the router and mounts the app with an explicit route registry.
|
|
479
467
|
* ```ts
|
|
480
|
-
* import {
|
|
481
|
-
*
|
|
468
|
+
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
469
|
+
* const registry = createRouteRegistry(() => { ... });
|
|
470
|
+
* await createSPA({ root: '#app', registry });
|
|
482
471
|
* ```
|
|
483
472
|
*
|
|
484
|
-
* Legacy usage with plain routes array (still supported):
|
|
485
|
-
* ```ts
|
|
486
|
-
* await createSPA({ root: '#app', routes: getRoutes() });
|
|
487
|
-
* ```
|
|
488
473
|
*/
|
|
489
474
|
async function createSPA(config) {
|
|
490
475
|
assertExecutionModel("spa");
|
|
491
476
|
if (!config || typeof config !== "object") throw new Error("createSPA requires a config object");
|
|
492
477
|
validateCspNonce(config.cspNonce);
|
|
493
|
-
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
const hasRoutes = Array.isArray(routeTable) && routeTable.length > 0;
|
|
497
|
-
if (!hasManifest && !hasRoutes) throw new Error("createSPA requires a route manifest or route table. Pass `manifest: getManifest()` or `routes: getRoutes()`. If you are enhancing existing HTML, use createIsland instead.");
|
|
478
|
+
if (!config.registry) throw new Error("createSPA requires a route registry.");
|
|
479
|
+
const { manifest } = config.registry;
|
|
480
|
+
if (manifest.records.length === 0) throw new Error("createSPA requires a route registry with at least one route. If you are enhancing existing HTML, use createIsland instead.");
|
|
498
481
|
const rootElement = resolveRootElement(config.root);
|
|
499
482
|
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
500
483
|
configureScrollRestoration(config.scrollRestoration);
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const routeAuth = config.auth ?? manifest?.auth;
|
|
505
|
-
const activeManifest = hasManifest ? manifest : void 0;
|
|
484
|
+
clearRouteState();
|
|
485
|
+
_applyManifest(manifest);
|
|
486
|
+
const routeAuth = config.auth ?? manifest.auth;
|
|
506
487
|
const appRouteSource = {
|
|
507
|
-
|
|
508
|
-
routes: hasManifest ? void 0 : routeTable,
|
|
488
|
+
registry: config.registry,
|
|
509
489
|
auth: routeAuth
|
|
510
490
|
};
|
|
511
491
|
_setActiveRouteAuthOptions(routeAuth);
|
|
512
492
|
if (isProductionEnvironment()) lockRouteRegistration();
|
|
513
|
-
const { path, resolved } = await resolveInitialRoute(routeAuth, {
|
|
514
|
-
manifest: activeManifest,
|
|
515
|
-
routes: hasManifest ? void 0 : routeTable
|
|
516
|
-
});
|
|
493
|
+
const { path, resolved } = await resolveInitialRoute(routeAuth, { registry: config.registry });
|
|
517
494
|
const appRuntime = createAppRenderRuntime(resolved?.kind === "render" && hasRouteRenderData(resolved) ? {
|
|
518
495
|
route: getRouteRenderData(resolved),
|
|
519
496
|
hasRoute: true
|
|
@@ -554,18 +531,15 @@ async function createSPA(config) {
|
|
|
554
531
|
await registerAppNavigation(rootElement, path, { ...appRouteSource });
|
|
555
532
|
}
|
|
556
533
|
/**
|
|
557
|
-
* hydrateSPA: Hydrate server-rendered HTML.
|
|
558
|
-
* Accepts either a `manifest` (preferred) or a legacy `routes` array.
|
|
534
|
+
* hydrateSPA: Hydrate server-rendered HTML with an explicit route registry.
|
|
559
535
|
*/
|
|
560
536
|
async function hydrateSPA(config) {
|
|
561
537
|
assertExecutionModel("spa");
|
|
562
538
|
if (!config || typeof config !== "object") throw new Error("hydrateSPA requires a config object");
|
|
563
539
|
validateCspNonce(config.cspNonce);
|
|
564
|
-
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
const hasRoutes = Array.isArray(routeTable) && routeTable.length > 0;
|
|
568
|
-
if (!hasManifest && !hasRoutes) throw new Error("hydrateSPA requires a route manifest or route table. Pass `manifest: getManifest()` or `routes: getRoutes()`. If you are enhancing existing HTML, use createIsland instead.");
|
|
540
|
+
if (!config.registry) throw new Error("hydrateSPA requires a route registry.");
|
|
541
|
+
const { manifest } = config.registry;
|
|
542
|
+
if (manifest.records.length === 0) throw new Error("hydrateSPA requires a route registry with at least one route. If you are enhancing existing HTML, use createIsland instead.");
|
|
569
543
|
const rootElement = resolveRootElement(config.root);
|
|
570
544
|
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
571
545
|
applyDeferredStreamPatches(rootElement);
|
|
@@ -574,14 +548,11 @@ async function hydrateSPA(config) {
|
|
|
574
548
|
if (hydrationQueryCache) hydrateDataRuntime(config.dataRuntime ?? getDefaultDataRuntime(), hydrationQueryCache);
|
|
575
549
|
const hydrationRenderDataForApp = hydrationRenderData;
|
|
576
550
|
configureScrollRestoration(config.scrollRestoration);
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const routeAuth = config.auth ?? manifest?.auth;
|
|
581
|
-
const activeManifest = hasManifest ? manifest : void 0;
|
|
551
|
+
clearRouteState();
|
|
552
|
+
_applyManifest(manifest);
|
|
553
|
+
const routeAuth = config.auth ?? manifest.auth;
|
|
582
554
|
const appRouteSource = {
|
|
583
|
-
|
|
584
|
-
routes: hasManifest ? void 0 : routeTable,
|
|
555
|
+
registry: config.registry,
|
|
585
556
|
auth: routeAuth,
|
|
586
557
|
runtime: createAppRenderRuntime({
|
|
587
558
|
framework: hydrationRenderData?.framework,
|
|
@@ -591,8 +562,7 @@ async function hydrateSPA(config) {
|
|
|
591
562
|
};
|
|
592
563
|
_setActiveRouteAuthOptions(routeAuth);
|
|
593
564
|
const { path, href: currentUrl, resolved } = await resolveInitialRoute(routeAuth, {
|
|
594
|
-
|
|
595
|
-
routes: hasManifest ? void 0 : routeTable,
|
|
565
|
+
registry: config.registry,
|
|
596
566
|
load: false
|
|
597
567
|
});
|
|
598
568
|
setServerLocation(currentUrl);
|
|
@@ -612,17 +582,11 @@ async function hydrateSPA(config) {
|
|
|
612
582
|
cspNonce: config.cspNonce
|
|
613
583
|
}));
|
|
614
584
|
if (shouldVerifyHydrationMarkup(config)) {
|
|
615
|
-
const
|
|
616
|
-
...r,
|
|
617
|
-
path: r.path,
|
|
618
|
-
handler: r.handler,
|
|
619
|
-
namespace: r.options.namespace
|
|
620
|
-
})) : routeTable;
|
|
621
|
-
const { verifyHydrationSyncForUrl } = await import("./verify-hydration-DfsBcGSf.js");
|
|
585
|
+
const { verifyHydrationSyncForUrl } = await import("./verify-hydration-BdKE9Hi8.js");
|
|
622
586
|
if (!verifyHydrationSyncForUrl({
|
|
623
587
|
root: rootElement,
|
|
624
588
|
url: currentUrl,
|
|
625
|
-
|
|
589
|
+
registry: config.registry,
|
|
626
590
|
resolved: hydrationResolved,
|
|
627
591
|
options: {
|
|
628
592
|
data: hydrationRenderDataForApp?.resources,
|
|
@@ -6,7 +6,19 @@ import { n as getActiveRenderContext, r as getCurrentRenderData } from "./render
|
|
|
6
6
|
import { t as resource } from "./resource-operation-Dz2Nxi1q.js";
|
|
7
7
|
import { i as ROUTE_ROOT_COMPONENT } from "./csp-nonce-C-8E85El.js";
|
|
8
8
|
import { c as withTelemetry, t as createQueryPrefetchContext } from "./query-registry-CQIT4mXr.js";
|
|
9
|
-
import {
|
|
9
|
+
import { A as insertRecordSorted, D as getRouteRecords, E as getRouteList, F as snapshotRouteState, N as restoreRouteState, P as setDefaultRouteAuthOptions, T as getDefaultRouteAuthOptions, f as addRouteToStores, h as getActiveRouteAuthOptions, i as getMatchingRouteRecord, m as clearRouteState, o as buildRouteContext, s as buildRouteContextBase } from "./route-matching-C93To416.js";
|
|
10
|
+
//#region src/runtime/execution-model.ts
|
|
11
|
+
const EXECUTION_MODEL_KEY = Symbol.for("__ASKR_EXECUTION_MODEL__");
|
|
12
|
+
function getExecutionModel() {
|
|
13
|
+
return globalThis[EXECUTION_MODEL_KEY];
|
|
14
|
+
}
|
|
15
|
+
function assertExecutionModel(model) {
|
|
16
|
+
const g = globalThis;
|
|
17
|
+
const cur = g[EXECUTION_MODEL_KEY];
|
|
18
|
+
if (cur && cur !== model) throw new Error(`[Askr] mixing execution models is not allowed (current: ${cur}, attempted: ${model}). Choose exactly one: createSPA, createSSR, or createIslands.`);
|
|
19
|
+
if (!cur) g[EXECUTION_MODEL_KEY] = model;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
10
22
|
//#region src/router/lazy.ts
|
|
11
23
|
const pendingLazy = /* @__PURE__ */ new Set();
|
|
12
24
|
const lazyStates = /* @__PURE__ */ new WeakMap();
|
|
@@ -55,13 +67,6 @@ function _preloadRouteRecord(record) {
|
|
|
55
67
|
function _associateLazyHandler(handler, components) {
|
|
56
68
|
handlerLazyComponents.set(handler, components);
|
|
57
69
|
}
|
|
58
|
-
function _preloadRouteHandler(handler) {
|
|
59
|
-
const imports = (handlerLazyComponents.get(handler) ?? []).flatMap((component) => {
|
|
60
|
-
const state = lazyStates.get(component);
|
|
61
|
-
return state ? [startLazyLoad(state)] : [];
|
|
62
|
-
});
|
|
63
|
-
return imports.length > 0 ? Promise.all(imports).then(() => void 0) : null;
|
|
64
|
-
}
|
|
65
70
|
//#endregion
|
|
66
71
|
//#region src/router/rendering.ts
|
|
67
72
|
const outletScope = defineScope(null);
|
|
@@ -321,6 +326,42 @@ function resolveRouteRequest(target, options = {}) {
|
|
|
321
326
|
});
|
|
322
327
|
}
|
|
323
328
|
//#endregion
|
|
329
|
+
//#region src/router/manifest.ts
|
|
330
|
+
function createRouteManifest() {
|
|
331
|
+
const auth = getDefaultRouteAuthOptions();
|
|
332
|
+
return {
|
|
333
|
+
records: [...getRouteRecords()],
|
|
334
|
+
...auth ? { auth } : {}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function _applyManifest(manifest) {
|
|
338
|
+
setDefaultRouteAuthOptions(manifest.auth);
|
|
339
|
+
for (const record of manifest.records) {
|
|
340
|
+
insertRecordSorted(record);
|
|
341
|
+
addRouteToStores({
|
|
342
|
+
path: record.path,
|
|
343
|
+
handler: record.handler,
|
|
344
|
+
namespace: record.options.namespace,
|
|
345
|
+
..."fallbackPrefix" in record && typeof record.fallbackPrefix === "string" ? { fallbackPrefix: record.fallbackPrefix } : {}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function createRouteRegistry(definition, options = {}) {
|
|
350
|
+
const previous = snapshotRouteState();
|
|
351
|
+
clearRouteState();
|
|
352
|
+
try {
|
|
353
|
+
setDefaultRouteAuthOptions(options.auth);
|
|
354
|
+
definition();
|
|
355
|
+
const manifest = createRouteManifest();
|
|
356
|
+
return Object.freeze({
|
|
357
|
+
manifest,
|
|
358
|
+
routes: getRouteList()
|
|
359
|
+
});
|
|
360
|
+
} finally {
|
|
361
|
+
restoreRouteState(previous);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
//#endregion
|
|
324
365
|
//#region src/router/deferred.tsx
|
|
325
366
|
const DEFERRED_VALUE = Symbol.for("@askrjs/askr/deferred-value");
|
|
326
367
|
const DEFERRED_BOUNDARY = Symbol.for("@askrjs/askr/deferred-boundary");
|
|
@@ -450,4 +491,4 @@ function Resolve(props) {
|
|
|
450
491
|
return props.children(result.value);
|
|
451
492
|
}
|
|
452
493
|
//#endregion
|
|
453
|
-
export { lazy as S,
|
|
494
|
+
export { lazy as C, getRenderHandler as S, getExecutionModel as T, isEmptyPageRenderEnvelope as _, resolveDeferredValues as a, Outlet as b, _applyManifest as c, getRouteRenderContext as d, getRouteRenderData as f, createPageRenderEnvelope as g, currentAuth as h, isDeferred as i, createRouteRegistry as l, resolveRouteRequest as m, Resolve as n, reviveDeferredValue as o, hasRouteRenderData as p, defer as r, routeData as s, DEFERRED_BOUNDARY as t, bindResolvedRouteData as u, isPageRenderEnvelope as v, assertExecutionModel as w, createRouteHandler as x, pageRenderEnvelope as y };
|