@askrjs/askr 0.0.62 → 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/actions/index.d.ts +1 -1
- package/dist/authoring-CXMPffwN.js +200 -0
- package/dist/benchmark.js +4 -4
- package/dist/boot/index.d.ts +12 -32
- package/dist/boot/index.js +1 -1
- package/dist/{boot-B0mMBddb.js → boot-OnYuVVfg.js} +64 -75
- package/dist/components/index.d.ts +1 -1
- package/dist/control/index.d.ts +1 -1
- package/dist/{control-CFIrGbD7.d.ts → control-CMzFPMjr.d.ts} +3 -0
- package/dist/csp-nonce-C-8E85El.js +33 -0
- package/dist/{deferred-BeNnjR4r.js → deferred-B71c2eio.js} +51 -10
- package/dist/foundations/index.d.ts +1 -1
- package/dist/foundations/state/index.d.ts +1 -1
- package/dist/foundations/structures/index.d.ts +1 -1
- package/dist/{index-DaWuMuIc.d.ts → index-Bjm0__Rr.d.ts} +1 -1
- package/dist/{index-DlvpxdSz.d.ts → index-Byqjltu2.d.ts} +1 -1
- package/dist/{index-CRck4XVe.d.ts → index-NKPpdRh9.d.ts} +6 -2
- package/dist/{index-Bce-RQg-.d.ts → index-PgRn5wmg.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -2
- package/dist/{navigate-DLi79IZr.js → navigate-DTZ2SM8_.js} +68 -279
- package/dist/{renderer-CVr_nktj.js → renderer-aUD-acDe.js} +1 -1
- package/dist/{route-matching-vJpvLVbX.js → route-matching-C93To416.js} +2 -2
- package/dist/router/index.d.ts +4 -10
- package/dist/router/index.js +4 -4
- package/dist/{router-lmH-Tmwh.d.ts → router-eRoaj11J.d.ts} +9 -6
- package/dist/ssg/index.d.ts +8 -33
- package/dist/ssg/index.js +34 -36
- package/dist/ssr/index.d.ts +10 -45
- package/dist/ssr/index.js +1 -1
- package/dist/{ssr-5HlhmLhU.js → ssr-C0hzqhY8.js} +65 -43
- package/dist/testing/index.d.ts +4 -5
- package/dist/testing/index.js +7 -10
- package/dist/{verify-hydration-13f0FOPd.js → verify-hydration-BdKE9Hi8.js} +3 -3
- package/package.json +1 -1
- package/dist/router-internal-BC0w1_xk.js +0 -4
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
|
|
package/dist/actions/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as action, i as ActionValidationError, n as ActionForm, o as defineAction, r as ActionStatus, t as ActionDescriptor } from "../index-
|
|
1
|
+
import { a as action, i as ActionValidationError, n as ActionForm, o as defineAction, r as ActionStatus, t as ActionDescriptor } from "../index-Bjm0__Rr.js";
|
|
2
2
|
export { ActionDescriptor, ActionForm, ActionStatus, ActionValidationError, action, defineAction };
|
|
@@ -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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { r as flushRuntimeScheduler } from "./access-ePqdQqkV.js";
|
|
2
|
-
import { t as installRendererBridge } from "./renderer-
|
|
2
|
+
import { t as installRendererBridge } from "./renderer-aUD-acDe.js";
|
|
3
3
|
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,41 +1,26 @@
|
|
|
1
|
-
import { s as ComponentFunction } from "../control-
|
|
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
|
|
7
7
|
type IslandConfig = {
|
|
8
8
|
root: Element | string;
|
|
9
9
|
component: ComponentFunction;
|
|
10
|
+
cspNonce?: string;
|
|
10
11
|
cleanupStrict?: boolean;
|
|
11
12
|
routes?: never;
|
|
12
13
|
};
|
|
13
14
|
type IslandsConfig = {
|
|
14
15
|
islands: IslandConfig[];
|
|
16
|
+
cspNonce?: string;
|
|
15
17
|
};
|
|
16
18
|
type BootRouteSource = {
|
|
17
19
|
registry: RouteRegistry;
|
|
18
|
-
manifest?: RouteManifest;
|
|
19
|
-
routes?: Route[];
|
|
20
|
-
} | {
|
|
21
|
-
manifest: RouteManifest;
|
|
22
|
-
registry?: RouteRegistry;
|
|
23
|
-
routes?: Route[];
|
|
24
|
-
} | {
|
|
25
|
-
manifest?: RouteManifest;
|
|
26
|
-
registry?: RouteRegistry;
|
|
27
|
-
routes: Route[];
|
|
28
20
|
};
|
|
29
21
|
type SPAConfig = BootRouteSource & {
|
|
30
22
|
root: Element | string;
|
|
31
|
-
/**
|
|
32
|
-
* Preferred: pass a route registry built via `createRouteRegistry(() => { ... })`.
|
|
33
|
-
* ```ts
|
|
34
|
-
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
35
|
-
* const registry = createRouteRegistry(() => { ... });
|
|
36
|
-
* await createSPA({ root: '#app', registry });
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
23
|
+
cspNonce?: string; /** Pass a route registry built via `createRouteRegistry(() => { ... })`. */
|
|
39
24
|
auth?: RouteAuthOptions;
|
|
40
25
|
scrollRestoration?: boolean | ScrollRestorationOptions;
|
|
41
26
|
cleanupStrict?: boolean;
|
|
@@ -43,7 +28,8 @@ type SPAConfig = BootRouteSource & {
|
|
|
43
28
|
};
|
|
44
29
|
type HydrateSPAConfig = BootRouteSource & {
|
|
45
30
|
root: Element | string;
|
|
46
|
-
|
|
31
|
+
cspNonce?: string;
|
|
32
|
+
dataRuntime?: DataRuntime; /** Pass the same explicit route registry used for the server render. */
|
|
47
33
|
auth?: RouteAuthOptions;
|
|
48
34
|
scrollRestoration?: boolean | ScrollRestorationOptions;
|
|
49
35
|
cleanupStrict?: boolean;
|
|
@@ -71,23 +57,17 @@ declare function createIsland(config: IslandConfig): void;
|
|
|
71
57
|
*/
|
|
72
58
|
declare function createIslands(config: IslandsConfig): void;
|
|
73
59
|
/**
|
|
74
|
-
* createSPA: Initializes router and mounts the app with
|
|
75
|
-
*
|
|
76
|
-
* Preferred usage with manifest:
|
|
60
|
+
* createSPA: Initializes the router and mounts the app with an explicit route registry.
|
|
77
61
|
* ```ts
|
|
78
|
-
* import {
|
|
79
|
-
*
|
|
62
|
+
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
63
|
+
* const registry = createRouteRegistry(() => { ... });
|
|
64
|
+
* await createSPA({ root: '#app', registry });
|
|
80
65
|
* ```
|
|
81
66
|
*
|
|
82
|
-
* Legacy usage with plain routes array (still supported):
|
|
83
|
-
* ```ts
|
|
84
|
-
* await createSPA({ root: '#app', routes: getRoutes() });
|
|
85
|
-
* ```
|
|
86
67
|
*/
|
|
87
68
|
declare function createSPA(config: SPAConfig): Promise<void>;
|
|
88
69
|
/**
|
|
89
|
-
* hydrateSPA: Hydrate server-rendered HTML.
|
|
90
|
-
* Accepts either a `manifest` (preferred) or a legacy `routes` array.
|
|
70
|
+
* hydrateSPA: Hydrate server-rendered HTML with an explicit route registry.
|
|
91
71
|
*/
|
|
92
72
|
declare function hydrateSPA(config: HydrateSPAConfig): Promise<void>;
|
|
93
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 };
|
|
@@ -2,15 +2,16 @@ import { b as isDevelopmentEnvironment, r as flushRuntimeScheduler, v as logger,
|
|
|
2
2
|
import { n as Fragment, t as ELEMENT_TYPE } from "./jsx-CUpRxrLH.js";
|
|
3
3
|
import { n as createComponentInstance, r as executeComponent } from "./component-internal-CbALfPGD.js";
|
|
4
4
|
import { h as cleanupComponent, m as isPromiseLike } from "./component-lifecycle-DEMPihoZ.js";
|
|
5
|
-
import { a as withIntrinsicHydrationAdoption, i as registerDeferredHydrationBoundary, n as activateHydrationBoundary$1, o as setStaticChildSlotsCacheEnabled, r as clearDeferredHydrationBoundaries, t as installRendererBridge } from "./renderer-
|
|
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
|
+
import { r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-C-8E85El.js";
|
|
10
11
|
import { i as getDefaultDataRuntime } from "./data-runtime-SJ1BSUvk.js";
|
|
11
12
|
import { a as hydrateDataRuntime } from "./query-registry-CQIT4mXr.js";
|
|
12
|
-
import {
|
|
13
|
-
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";
|
|
14
15
|
import "./ssr-CbQO5xd3.js";
|
|
15
16
|
//#region src/boot/hydration.ts
|
|
16
17
|
const DEFERRED_PAYLOAD = "__askr_deferred__";
|
|
@@ -259,6 +260,7 @@ function attachCleanupForRoot(rootElement, instance) {
|
|
|
259
260
|
} catch {}
|
|
260
261
|
}
|
|
261
262
|
function mountOrUpdate(rootElement, componentFn, options) {
|
|
263
|
+
const nonce = validateCspNonce(options?.cspNonce);
|
|
262
264
|
const wrappedFn = (props, ctx) => {
|
|
263
265
|
const out = componentFn(props, ctx);
|
|
264
266
|
if (isPromiseLike(out)) throw new Error("Async components are not supported. Components must return synchronously.");
|
|
@@ -268,11 +270,15 @@ function mountOrUpdate(rootElement, componentFn, options) {
|
|
|
268
270
|
props: { __askrAutoDefaultPortal: true },
|
|
269
271
|
key: "__default_portal"
|
|
270
272
|
};
|
|
271
|
-
|
|
273
|
+
const root = {
|
|
272
274
|
$$typeof: ELEMENT_TYPE,
|
|
273
275
|
type: Fragment,
|
|
274
276
|
props: { children: out === void 0 || out === null ? [portalVNode] : [out, portalVNode] }
|
|
275
277
|
};
|
|
278
|
+
return nonce === void 0 ? root : CspNonceScope({
|
|
279
|
+
value: nonce,
|
|
280
|
+
children: root
|
|
281
|
+
});
|
|
276
282
|
};
|
|
277
283
|
Object.defineProperty(wrappedFn, "name", { value: componentFn.name || "Component" });
|
|
278
284
|
const existingCleanup = rootElement[CLEANUP_SYMBOL];
|
|
@@ -298,6 +304,7 @@ function mountOrUpdate(rootElement, componentFn, options) {
|
|
|
298
304
|
instance.isRoot = true;
|
|
299
305
|
instance.portalScope = instance;
|
|
300
306
|
instance._appRenderRuntime = options?.appRuntime;
|
|
307
|
+
instance._cspNonce = nonce;
|
|
301
308
|
if (shouldResetHookState) {
|
|
302
309
|
instance.stateValues = [];
|
|
303
310
|
instance.hasPendingUpdate = false;
|
|
@@ -323,6 +330,7 @@ function mountOrUpdate(rootElement, componentFn, options) {
|
|
|
323
330
|
instance._rootComponentFn = componentFn;
|
|
324
331
|
instance.portalScope = instance;
|
|
325
332
|
instance._appRenderRuntime = options?.appRuntime;
|
|
333
|
+
instance._cspNonce = nonce;
|
|
326
334
|
if (options && typeof options.cleanupStrict === "boolean") instance.cleanupStrict = options.cleanupStrict;
|
|
327
335
|
}
|
|
328
336
|
attachCleanupForRoot(rootElement, instance);
|
|
@@ -356,7 +364,7 @@ function cleanupApp(root) {
|
|
|
356
364
|
const wasRoutedRoot = routedRoots.delete(rootElement);
|
|
357
365
|
instancesByRoot.delete(rootElement);
|
|
358
366
|
clearRootCleanupCallbacks(rootElement);
|
|
359
|
-
if (wasRoutedRoot && routedRoots.size === 0)
|
|
367
|
+
if (wasRoutedRoot && routedRoots.size === 0) clearRouteState();
|
|
360
368
|
try {
|
|
361
369
|
delete rootElement[CLEANUP_SYMBOL];
|
|
362
370
|
} catch {}
|
|
@@ -400,21 +408,11 @@ async function resolveInitialRoute(auth, source) {
|
|
|
400
408
|
for (let redirects = 0; redirects <= MAX_INITIAL_ROUTE_REDIRECTS; redirects++) {
|
|
401
409
|
if (visited.has(href)) throw new Error(`[Askr] Route redirect cycle detected at ${href}.`);
|
|
402
410
|
visited.add(href);
|
|
403
|
-
const resolved = source?.
|
|
404
|
-
manifest: source.manifest,
|
|
411
|
+
const resolved = source?.registry ? await resolveRouteRequest(href, {
|
|
412
|
+
manifest: source.registry.manifest,
|
|
405
413
|
auth,
|
|
406
414
|
load: source.load
|
|
407
|
-
}) :
|
|
408
|
-
const match = resolveRouteFromRoutes(path, source.routes);
|
|
409
|
-
if (!match) return null;
|
|
410
|
-
const lazyImport = _preloadRouteHandler(match.handler);
|
|
411
|
-
const result = {
|
|
412
|
-
kind: "render",
|
|
413
|
-
handler: match.handler,
|
|
414
|
-
params: match.params
|
|
415
|
-
};
|
|
416
|
-
return lazyImport ? lazyImport.then(() => result) : result;
|
|
417
|
-
})() : await resolveRouteRequest(href, { auth });
|
|
415
|
+
}) : await resolveRouteRequest(href, { auth });
|
|
418
416
|
if (typeof window === "undefined" || !resolved || resolved.kind !== "redirect") return {
|
|
419
417
|
path,
|
|
420
418
|
href,
|
|
@@ -440,11 +438,15 @@ function createIsland(config) {
|
|
|
440
438
|
assertExecutionModel("islands");
|
|
441
439
|
if (!config || typeof config !== "object") throw new Error("createIsland requires a config object");
|
|
442
440
|
if (typeof config.component !== "function") throw new Error("createIsland: component must be a function");
|
|
441
|
+
validateCspNonce(config.cspNonce);
|
|
443
442
|
const rootElement = resolveRootElement(config.root);
|
|
444
443
|
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
445
444
|
if ("routes" in config) throw new Error("createIsland does not accept routes; use createSPA for routed apps");
|
|
446
445
|
if (hasRegisteredRoutes()) throw new Error("Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead.");
|
|
447
|
-
mountOrUpdate(rootElement, config.component, {
|
|
446
|
+
mountOrUpdate(rootElement, config.component, {
|
|
447
|
+
cleanupStrict: config.cleanupStrict,
|
|
448
|
+
cspNonce: config.cspNonce
|
|
449
|
+
});
|
|
448
450
|
}
|
|
449
451
|
/**
|
|
450
452
|
* createIslands: Enhances one or more existing DOM roots (no router).
|
|
@@ -453,50 +455,42 @@ function createIsland(config) {
|
|
|
453
455
|
function createIslands(config) {
|
|
454
456
|
assertExecutionModel("islands");
|
|
455
457
|
if (!config || typeof config !== "object") throw new Error("createIslands requires a config object");
|
|
458
|
+
validateCspNonce(config.cspNonce);
|
|
456
459
|
if (!Array.isArray(config.islands) || config.islands.length === 0) throw new Error("createIslands requires a non-empty islands array");
|
|
457
|
-
for (const island of config.islands) createIsland(
|
|
460
|
+
for (const island of config.islands) createIsland({
|
|
461
|
+
...island,
|
|
462
|
+
cspNonce: island.cspNonce ?? config.cspNonce
|
|
463
|
+
});
|
|
458
464
|
}
|
|
459
465
|
/**
|
|
460
|
-
* createSPA: Initializes router and mounts the app with
|
|
461
|
-
*
|
|
462
|
-
* Preferred usage with manifest:
|
|
466
|
+
* createSPA: Initializes the router and mounts the app with an explicit route registry.
|
|
463
467
|
* ```ts
|
|
464
|
-
* import {
|
|
465
|
-
*
|
|
468
|
+
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
469
|
+
* const registry = createRouteRegistry(() => { ... });
|
|
470
|
+
* await createSPA({ root: '#app', registry });
|
|
466
471
|
* ```
|
|
467
472
|
*
|
|
468
|
-
* Legacy usage with plain routes array (still supported):
|
|
469
|
-
* ```ts
|
|
470
|
-
* await createSPA({ root: '#app', routes: getRoutes() });
|
|
471
|
-
* ```
|
|
472
473
|
*/
|
|
473
474
|
async function createSPA(config) {
|
|
474
475
|
assertExecutionModel("spa");
|
|
475
476
|
if (!config || typeof config !== "object") throw new Error("createSPA requires a config object");
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
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.");
|
|
477
|
+
validateCspNonce(config.cspNonce);
|
|
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.");
|
|
481
481
|
const rootElement = resolveRootElement(config.root);
|
|
482
482
|
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
483
483
|
configureScrollRestoration(config.scrollRestoration);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const routeAuth = config.auth ?? manifest?.auth;
|
|
488
|
-
const activeManifest = hasManifest ? manifest : void 0;
|
|
484
|
+
clearRouteState();
|
|
485
|
+
_applyManifest(manifest);
|
|
486
|
+
const routeAuth = config.auth ?? manifest.auth;
|
|
489
487
|
const appRouteSource = {
|
|
490
|
-
|
|
491
|
-
routes: hasManifest ? void 0 : routeTable,
|
|
488
|
+
registry: config.registry,
|
|
492
489
|
auth: routeAuth
|
|
493
490
|
};
|
|
494
491
|
_setActiveRouteAuthOptions(routeAuth);
|
|
495
492
|
if (isProductionEnvironment()) lockRouteRegistration();
|
|
496
|
-
const { path, resolved } = await resolveInitialRoute(routeAuth, {
|
|
497
|
-
manifest: activeManifest,
|
|
498
|
-
routes: hasManifest ? void 0 : routeTable
|
|
499
|
-
});
|
|
493
|
+
const { path, resolved } = await resolveInitialRoute(routeAuth, { registry: config.registry });
|
|
500
494
|
const appRuntime = createAppRenderRuntime(resolved?.kind === "render" && hasRouteRenderData(resolved) ? {
|
|
501
495
|
route: getRouteRenderData(resolved),
|
|
502
496
|
hasRoute: true
|
|
@@ -507,7 +501,8 @@ async function createSPA(config) {
|
|
|
507
501
|
children: []
|
|
508
502
|
}), {
|
|
509
503
|
cleanupStrict: config.cleanupStrict,
|
|
510
|
-
appRuntime
|
|
504
|
+
appRuntime,
|
|
505
|
+
cspNonce: config.cspNonce
|
|
511
506
|
});
|
|
512
507
|
await registerAppNavigation(rootElement, path, { ...appRouteSource });
|
|
513
508
|
return;
|
|
@@ -518,7 +513,8 @@ async function createSPA(config) {
|
|
|
518
513
|
children: []
|
|
519
514
|
}), {
|
|
520
515
|
cleanupStrict: config.cleanupStrict,
|
|
521
|
-
appRuntime
|
|
516
|
+
appRuntime,
|
|
517
|
+
cspNonce: config.cspNonce
|
|
522
518
|
});
|
|
523
519
|
await registerAppNavigation(rootElement, path, { ...appRouteSource });
|
|
524
520
|
return;
|
|
@@ -529,22 +525,21 @@ async function createSPA(config) {
|
|
|
529
525
|
params: resolved.params
|
|
530
526
|
}), {
|
|
531
527
|
cleanupStrict: config.cleanupStrict,
|
|
532
|
-
appRuntime
|
|
528
|
+
appRuntime,
|
|
529
|
+
cspNonce: config.cspNonce
|
|
533
530
|
});
|
|
534
531
|
await registerAppNavigation(rootElement, path, { ...appRouteSource });
|
|
535
532
|
}
|
|
536
533
|
/**
|
|
537
|
-
* hydrateSPA: Hydrate server-rendered HTML.
|
|
538
|
-
* Accepts either a `manifest` (preferred) or a legacy `routes` array.
|
|
534
|
+
* hydrateSPA: Hydrate server-rendered HTML with an explicit route registry.
|
|
539
535
|
*/
|
|
540
536
|
async function hydrateSPA(config) {
|
|
541
537
|
assertExecutionModel("spa");
|
|
542
538
|
if (!config || typeof config !== "object") throw new Error("hydrateSPA requires a config object");
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
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.");
|
|
539
|
+
validateCspNonce(config.cspNonce);
|
|
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.");
|
|
548
543
|
const rootElement = resolveRootElement(config.root);
|
|
549
544
|
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
550
545
|
applyDeferredStreamPatches(rootElement);
|
|
@@ -553,14 +548,11 @@ async function hydrateSPA(config) {
|
|
|
553
548
|
if (hydrationQueryCache) hydrateDataRuntime(config.dataRuntime ?? getDefaultDataRuntime(), hydrationQueryCache);
|
|
554
549
|
const hydrationRenderDataForApp = hydrationRenderData;
|
|
555
550
|
configureScrollRestoration(config.scrollRestoration);
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const routeAuth = config.auth ?? manifest?.auth;
|
|
560
|
-
const activeManifest = hasManifest ? manifest : void 0;
|
|
551
|
+
clearRouteState();
|
|
552
|
+
_applyManifest(manifest);
|
|
553
|
+
const routeAuth = config.auth ?? manifest.auth;
|
|
561
554
|
const appRouteSource = {
|
|
562
|
-
|
|
563
|
-
routes: hasManifest ? void 0 : routeTable,
|
|
555
|
+
registry: config.registry,
|
|
564
556
|
auth: routeAuth,
|
|
565
557
|
runtime: createAppRenderRuntime({
|
|
566
558
|
framework: hydrationRenderData?.framework,
|
|
@@ -570,8 +562,7 @@ async function hydrateSPA(config) {
|
|
|
570
562
|
};
|
|
571
563
|
_setActiveRouteAuthOptions(routeAuth);
|
|
572
564
|
const { path, href: currentUrl, resolved } = await resolveInitialRoute(routeAuth, {
|
|
573
|
-
|
|
574
|
-
routes: hasManifest ? void 0 : routeTable,
|
|
565
|
+
registry: config.registry,
|
|
575
566
|
load: false
|
|
576
567
|
});
|
|
577
568
|
setServerLocation(currentUrl);
|
|
@@ -586,24 +577,22 @@ async function hydrateSPA(config) {
|
|
|
586
577
|
handler: resolved.handler,
|
|
587
578
|
params: resolved.params
|
|
588
579
|
};
|
|
589
|
-
const mountHydratedRoot = (...args) => withIntrinsicHydrationAdoption(() => mountOrUpdate(
|
|
580
|
+
const mountHydratedRoot = (...args) => withIntrinsicHydrationAdoption(() => mountOrUpdate(args[0], args[1], {
|
|
581
|
+
...args[2],
|
|
582
|
+
cspNonce: config.cspNonce
|
|
583
|
+
}));
|
|
590
584
|
if (shouldVerifyHydrationMarkup(config)) {
|
|
591
|
-
const
|
|
592
|
-
...r,
|
|
593
|
-
path: r.path,
|
|
594
|
-
handler: r.handler,
|
|
595
|
-
namespace: r.options.namespace
|
|
596
|
-
})) : routeTable;
|
|
597
|
-
const { verifyHydrationSyncForUrl } = await import("./verify-hydration-13f0FOPd.js");
|
|
585
|
+
const { verifyHydrationSyncForUrl } = await import("./verify-hydration-BdKE9Hi8.js");
|
|
598
586
|
if (!verifyHydrationSyncForUrl({
|
|
599
587
|
root: rootElement,
|
|
600
588
|
url: currentUrl,
|
|
601
|
-
|
|
589
|
+
registry: config.registry,
|
|
602
590
|
resolved: hydrationResolved,
|
|
603
591
|
options: {
|
|
604
592
|
data: hydrationRenderDataForApp?.resources,
|
|
605
593
|
dataRuntime: config.dataRuntime ?? getDefaultDataRuntime(),
|
|
606
|
-
envelope: hydrationRenderDataForApp ?? void 0
|
|
594
|
+
envelope: hydrationRenderDataForApp ?? void 0,
|
|
595
|
+
cspNonce: config.cspNonce
|
|
607
596
|
}
|
|
608
597
|
})) throw new Error("[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output.");
|
|
609
598
|
}
|
package/dist/control/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as Show, c as ForProps, i as MatchProps, n as CaseProps, o as ShowProps, r as Match, s as For, t as Case } from "../index-
|
|
1
|
+
import { a as Show, c as ForProps, i as MatchProps, n as CaseProps, o as ShowProps, r as Match, s as For, t as Case } from "../index-Byqjltu2.js";
|
|
2
2
|
export { Case, type CaseProps, For, type ForProps, Match, type MatchProps, Show, type ShowProps };
|