@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/dist/ssg/index.d.ts
CHANGED
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { d as DocumentRenderContext, f as DocumentRenderer, u as DocumentRenderArgs } from "../control-CMzFPMjr.js";
|
|
2
|
+
import { D as RoutePathParams, N as RouteRegistry, O as RoutePolicy, T as RouteOptions, y as RouteHandler } from "../router-eRoaj11J.js";
|
|
3
3
|
import { AuthRequirement } from "@askrjs/auth";
|
|
4
4
|
//#region src/ssg/types.d.ts
|
|
5
5
|
type SSGMode = 'full' | 'incremental';
|
|
6
6
|
type RouteRenderStatus = 'success' | 'error' | 'skipped' | 'removed';
|
|
7
7
|
type RouteRenderReason = 'full' | 'changed-key' | 'changed-route' | 'new-route' | 'no-keys' | 'unchanged' | 'deleted' | 'runtime-only';
|
|
8
8
|
type RouteConfigParams<Path extends string> = string extends Path ? Record<string, string> : RoutePathParams<Path>;
|
|
9
|
-
type RouteComponentProps<Path extends string> = RouteConfigParams<Path> & Record<string, unknown>;
|
|
10
|
-
type StaticRouteComponent<Path extends string> = (props: RouteComponentProps<Path>, context?: Parameters<ComponentFunction>[1]) => RenderableChild;
|
|
11
9
|
/**
|
|
12
10
|
* Route config accepted by SSG.
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
* `component` is kept for compatibility and is normalized to `handler`.
|
|
12
|
+
* The handler is defined by the route registry and matches router/SSR naming.
|
|
16
13
|
*/
|
|
17
14
|
interface RouteConfig<Path extends string = string> {
|
|
18
15
|
/** URL path to generate (e.g., "/blog/post-1", "/") */
|
|
19
16
|
path: Path;
|
|
20
17
|
/** Route handler compatible with router/SSR */
|
|
21
|
-
handler
|
|
22
|
-
/** Backward-compatible alias for handler */
|
|
23
|
-
component?: StaticRouteComponent<Path>;
|
|
18
|
+
handler: RouteHandler;
|
|
24
19
|
/** Optional base props merged with route params during render */
|
|
25
20
|
props?: Record<string, unknown>;
|
|
26
21
|
/** Optional namespace for router compatibility */
|
|
@@ -72,13 +67,9 @@ interface SSGAssetSource {
|
|
|
72
67
|
to?: string;
|
|
73
68
|
}
|
|
74
69
|
/** Options for createStaticGen */
|
|
75
|
-
type SSGOptions<TRoutes extends readonly RouteConfig[] = RouteConfig[]> = SSGBaseOptions &
|
|
76
|
-
/** Routes to generate. Prefer `registry` when routes are already defined through the router DSL. */routes: TRoutes;
|
|
77
|
-
registry?: never;
|
|
78
|
-
} | {
|
|
70
|
+
type SSGOptions<TRoutes extends readonly RouteConfig[] = RouteConfig[]> = SSGBaseOptions & {
|
|
79
71
|
/** Explicit route registry captured with `createRouteRegistry()`. */registry: RouteRegistry;
|
|
80
|
-
|
|
81
|
-
});
|
|
72
|
+
};
|
|
82
73
|
/** Options for a single generation run */
|
|
83
74
|
interface SSGGenerateOptions {
|
|
84
75
|
/** Generation mode */
|
|
@@ -190,29 +181,13 @@ interface DiscoveredResources {
|
|
|
190
181
|
}
|
|
191
182
|
//#endregion
|
|
192
183
|
//#region src/ssg/create-static-gen.d.ts
|
|
193
|
-
type AnyRouteConfig = RouteConfig<string>;
|
|
194
|
-
type StrictRouteConfig<TRoute extends AnyRouteConfig> = Omit<TRoute, 'params' | 'entries'> & {
|
|
195
|
-
params?: RouteConfig<TRoute['path']>['params'];
|
|
196
|
-
entries?: RouteConfig<TRoute['path']>['entries'];
|
|
197
|
-
};
|
|
198
|
-
type StrictRouteConfigs<TRoutes extends readonly AnyRouteConfig[]> = { [TIndex in keyof TRoutes]: TRoutes[TIndex] extends AnyRouteConfig ? StrictRouteConfig<TRoutes[TIndex]> : never };
|
|
199
|
-
type StaticGenRouteSource<TRoutes extends readonly AnyRouteConfig[]> = {
|
|
200
|
-
routes: TRoutes & StrictRouteConfigs<TRoutes>;
|
|
201
|
-
registry?: never;
|
|
202
|
-
} | {
|
|
203
|
-
registry: RouteRegistry;
|
|
204
|
-
routes?: never;
|
|
205
|
-
};
|
|
206
184
|
/**
|
|
207
185
|
* Create a Static Site Generator
|
|
208
186
|
*
|
|
209
187
|
* Usage:
|
|
210
188
|
* ```ts
|
|
211
189
|
* const ssg = createStaticGen({
|
|
212
|
-
*
|
|
213
|
-
* { path: '/', component: HomePage },
|
|
214
|
-
* { path: '/about', component: AboutPage },
|
|
215
|
-
* ],
|
|
190
|
+
* registry,
|
|
216
191
|
* outputDir: './dist',
|
|
217
192
|
* dataOverrides: {
|
|
218
193
|
* '/api/posts': { posts: [...] }
|
|
@@ -223,7 +198,7 @@ type StaticGenRouteSource<TRoutes extends readonly AnyRouteConfig[]> = {
|
|
|
223
198
|
* console.log(`Generated ${result.successful}/${result.totalRoutes} routes`);
|
|
224
199
|
* ```
|
|
225
200
|
*/
|
|
226
|
-
declare function createStaticGen
|
|
201
|
+
declare function createStaticGen(options: SSGOptions): {
|
|
227
202
|
/**
|
|
228
203
|
* Generate static HTML for all routes
|
|
229
204
|
* Writes to outputDir with metadata.json
|
package/dist/ssg/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import "../access-ePqdQqkV.js";
|
|
2
|
-
import { a as resolveDeferredValues,
|
|
2
|
+
import { S as getRenderHandler, a as resolveDeferredValues, l as createRouteRegistry, u as bindResolvedRouteData } from "../deferred-B71c2eio.js";
|
|
3
|
+
import { a as route } from "../authoring-CXMPffwN.js";
|
|
3
4
|
import { n as renderDocument } from "../ssr-CbQO5xd3.js";
|
|
4
|
-
import { n as renderToString } from "../ssr-
|
|
5
|
+
import { n as renderToString } from "../ssr-C0hzqhY8.js";
|
|
5
6
|
import * as fs from "node:fs/promises";
|
|
6
7
|
import * as pathModule from "node:path";
|
|
7
8
|
import * as os from "node:os";
|
|
@@ -73,7 +74,7 @@ function isStringRecord(value) {
|
|
|
73
74
|
}
|
|
74
75
|
function validateRouteShape(route) {
|
|
75
76
|
if (typeof route.path !== "string" || !route.path.startsWith("/")) throw new Error(`route path must be a string starting with "/", got "${route.path}"`);
|
|
76
|
-
if (typeof
|
|
77
|
+
if (typeof route.handler !== "function") throw new Error(`route handler must be a function for path "${route.path}"`);
|
|
77
78
|
if (route.params !== void 0 && !isStringRecord(route.params)) throw new Error(`route "${route.path}" params must be an object containing only string values`);
|
|
78
79
|
if (route.invalidationKeys !== void 0) {
|
|
79
80
|
if (!Array.isArray(route.invalidationKeys)) throw new Error(`route "${route.path}" invalidationKeys must be an array of strings`);
|
|
@@ -141,28 +142,25 @@ async function batchRenderRoutes(routes, options = {}) {
|
|
|
141
142
|
const results = [];
|
|
142
143
|
results.length = routes.length;
|
|
143
144
|
let nextIndex = 0;
|
|
144
|
-
const
|
|
145
|
+
const sharedRegistry = new Set(routes.map((route) => route.path)).size === routes.length ? createRouteRegistry(() => {
|
|
146
|
+
for (const route$1 of routes) route(route$1.path, route$1.handler, { namespace: route$1.namespace });
|
|
147
|
+
}) : void 0;
|
|
148
|
+
const renderOne = async (route$2) => {
|
|
145
149
|
const startTime = performance.now();
|
|
146
|
-
const url = interpolateRoutePath(route.path, route.params);
|
|
150
|
+
const url = interpolateRoutePath(route$2.path, route$2.params);
|
|
147
151
|
const requestUrl = new URL(url, "http://localhost");
|
|
148
|
-
const resolvedData = resolveSsgRouteData(dataMap, route.path, url);
|
|
152
|
+
const resolvedData = resolveSsgRouteData(dataMap, route$2.path, url);
|
|
149
153
|
const baseData = resolvedData.hasData ? resolvedData.data : void 0;
|
|
150
154
|
const resourceCount = resolvedData.hasData && baseData ? Object.keys(baseData).length : 0;
|
|
151
|
-
const mergedHandler = route.handler
|
|
152
|
-
const component = route.component;
|
|
153
|
-
return component({
|
|
154
|
-
...route.props,
|
|
155
|
-
...params
|
|
156
|
-
}, ctx);
|
|
157
|
-
};
|
|
155
|
+
const mergedHandler = route$2.handler;
|
|
158
156
|
try {
|
|
159
157
|
let routeData;
|
|
160
158
|
let hasRouteData = false;
|
|
161
|
-
if (route.loader) {
|
|
159
|
+
if (route$2.loader) {
|
|
162
160
|
const controller = new AbortController();
|
|
163
161
|
const loadContext = {
|
|
164
162
|
mode: "ssg",
|
|
165
|
-
params: route.params ?? {},
|
|
163
|
+
params: route$2.params ?? {},
|
|
166
164
|
pathname: requestUrl.pathname,
|
|
167
165
|
search: requestUrl.search,
|
|
168
166
|
hash: requestUrl.hash,
|
|
@@ -175,7 +173,7 @@ async function batchRenderRoutes(routes, options = {}) {
|
|
|
175
173
|
},
|
|
176
174
|
signal: controller.signal
|
|
177
175
|
};
|
|
178
|
-
const loaded = await route.loader({
|
|
176
|
+
const loaded = await route$2.loader({
|
|
179
177
|
...loadContext,
|
|
180
178
|
request: new Request(requestUrl)
|
|
181
179
|
});
|
|
@@ -183,13 +181,12 @@ async function batchRenderRoutes(routes, options = {}) {
|
|
|
183
181
|
routeData = loaded;
|
|
184
182
|
hasRouteData = true;
|
|
185
183
|
}
|
|
184
|
+
const renderedHandler = hasRouteData ? bindResolvedRouteData(mergedHandler, routeData) : mergedHandler;
|
|
186
185
|
const html = renderToString({
|
|
187
186
|
url,
|
|
188
|
-
|
|
189
|
-
path: route.
|
|
190
|
-
|
|
191
|
-
namespace: route.namespace
|
|
192
|
-
}],
|
|
187
|
+
registry: hasRouteData || !sharedRegistry ? createRouteRegistry(() => {
|
|
188
|
+
route(route$2.path, renderedHandler, { namespace: route$2.namespace });
|
|
189
|
+
}) : sharedRegistry,
|
|
193
190
|
seed,
|
|
194
191
|
data: baseData,
|
|
195
192
|
document: document === void 0 ? void 0 : ({ appHtml }) => renderDocument(document, {
|
|
@@ -200,12 +197,12 @@ async function batchRenderRoutes(routes, options = {}) {
|
|
|
200
197
|
pathname: requestUrl.pathname,
|
|
201
198
|
search: requestUrl.search,
|
|
202
199
|
hash: requestUrl.hash,
|
|
203
|
-
params: route.params ?? {},
|
|
200
|
+
params: route$2.params ?? {},
|
|
204
201
|
data: baseData,
|
|
205
202
|
seed,
|
|
206
203
|
route: {
|
|
207
|
-
path: route.path,
|
|
208
|
-
namespace: route.namespace
|
|
204
|
+
path: route$2.path,
|
|
205
|
+
namespace: route$2.namespace
|
|
209
206
|
}
|
|
210
207
|
}
|
|
211
208
|
}, "createStaticGen()")
|
|
@@ -579,21 +576,24 @@ async function splitStaticRoutes(routes) {
|
|
|
579
576
|
};
|
|
580
577
|
}
|
|
581
578
|
function normalizeStaticRoutes(options) {
|
|
582
|
-
|
|
583
|
-
return [...options.routes ?? []];
|
|
579
|
+
return routeRegistryToRouteConfigs(options.registry);
|
|
584
580
|
}
|
|
585
581
|
function routeRegistryToRouteConfigs(registry) {
|
|
586
582
|
const routeConfigs = [];
|
|
587
583
|
for (const record of registry.manifest.records) {
|
|
588
584
|
if (record.isFallback) continue;
|
|
585
|
+
const options = record.options;
|
|
589
586
|
routeConfigs.push({
|
|
590
587
|
path: record.path,
|
|
591
588
|
handler: getRenderHandler(record),
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
589
|
+
params: options.params,
|
|
590
|
+
props: options.props,
|
|
591
|
+
invalidationKeys: options.invalidationKeys ? [...options.invalidationKeys] : void 0,
|
|
592
|
+
namespace: options.namespace,
|
|
593
|
+
auth: options.auth,
|
|
594
|
+
policies: options.policies,
|
|
595
|
+
entries: options.entries,
|
|
596
|
+
loader: options.loader
|
|
597
597
|
});
|
|
598
598
|
}
|
|
599
599
|
return routeConfigs;
|
|
@@ -672,10 +672,7 @@ async function replaceOutputDirectory(stagingDir, outputDir, fileOperations = fs
|
|
|
672
672
|
* Usage:
|
|
673
673
|
* ```ts
|
|
674
674
|
* const ssg = createStaticGen({
|
|
675
|
-
*
|
|
676
|
-
* { path: '/', component: HomePage },
|
|
677
|
-
* { path: '/about', component: AboutPage },
|
|
678
|
-
* ],
|
|
675
|
+
* registry,
|
|
679
676
|
* outputDir: './dist',
|
|
680
677
|
* dataOverrides: {
|
|
681
678
|
* '/api/posts': { posts: [...] }
|
|
@@ -688,11 +685,12 @@ async function replaceOutputDirectory(stagingDir, outputDir, fileOperations = fs
|
|
|
688
685
|
*/
|
|
689
686
|
function createStaticGen(options) {
|
|
690
687
|
let result = null;
|
|
688
|
+
if (!options || !("registry" in options) || !options.registry) throw new Error("route registry is required");
|
|
691
689
|
const configuredRoutes = normalizeStaticRoutes(options);
|
|
692
690
|
const seed = options.seed ?? 12345;
|
|
693
691
|
const resolvedParallelism = resolveParallelism(options.parallelism);
|
|
694
692
|
const resolvedConcurrency = Math.max(1, options.concurrency ?? resolvedParallelism);
|
|
695
|
-
if (configuredRoutes.length === 0) throw new Error("
|
|
693
|
+
if (configuredRoutes.length === 0) throw new Error("route registry is required");
|
|
696
694
|
if (!options.outputDir || options.outputDir.trim().length === 0) throw new Error("outputDir is required");
|
|
697
695
|
return {
|
|
698
696
|
/**
|
package/dist/ssr/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as RenderableChild, d as DocumentRenderContext, f as DocumentRenderer, p as SSRData, u as DocumentRenderArgs } from "../control-
|
|
1
|
+
import { c as RenderableChild, d as DocumentRenderContext, f as DocumentRenderer, p as SSRData, u as DocumentRenderArgs } from "../control-CMzFPMjr.js";
|
|
2
2
|
import { r as JSXElement, s as Props } from "../jsx-Dvr2SGRG.js";
|
|
3
3
|
import { D as QueryPrefetchContext, n as ServerQueryRegistry, u as CoreTelemetry, v as DataRuntime } from "../index-Bki2rqcr.js";
|
|
4
|
-
import { L as RouteRequestResult,
|
|
4
|
+
import { A as RouteRecord, L as RouteRequestResult, N as RouteRegistry, a as AccessRedirectDecision, f as Route, p as RouteAuthOptions, r as AccessDenyDecision, y as RouteHandler } from "../router-eRoaj11J.js";
|
|
5
5
|
import { AuthContext } from "@askrjs/auth";
|
|
6
6
|
|
|
7
7
|
//#region src/common/page-render-envelope.d.ts
|
|
@@ -50,6 +50,7 @@ interface RenderContext {
|
|
|
50
50
|
renderData: PageRenderEnvelope | null;
|
|
51
51
|
hydrationData: PageRenderEnvelope | null;
|
|
52
52
|
deferredBoundaries: DeferredBoundaryRegistration[];
|
|
53
|
+
cspNonce?: string;
|
|
53
54
|
}
|
|
54
55
|
declare function createRenderContext(seed?: number, opts?: {
|
|
55
56
|
url?: string;
|
|
@@ -63,6 +64,7 @@ declare function createRenderContext(seed?: number, opts?: {
|
|
|
63
64
|
queryPrefetch?: QueryPrefetchContext;
|
|
64
65
|
framework?: Readonly<Record<string, unknown>>;
|
|
65
66
|
envelope?: PageRenderEnvelope;
|
|
67
|
+
cspNonce?: string;
|
|
66
68
|
}): RenderContext;
|
|
67
69
|
/**
|
|
68
70
|
* Run a function with the given render context.
|
|
@@ -84,10 +86,6 @@ type SSRRoute = {
|
|
|
84
86
|
};
|
|
85
87
|
type SSRRouteSource = {
|
|
86
88
|
registry: RouteRegistry;
|
|
87
|
-
routes?: readonly SSRRoute[];
|
|
88
|
-
} | {
|
|
89
|
-
registry?: RouteRegistry;
|
|
90
|
-
routes: readonly SSRRoute[];
|
|
91
89
|
};
|
|
92
90
|
type RouteRenderOptions = SSRRouteSource & {
|
|
93
91
|
url: string;
|
|
@@ -99,6 +97,7 @@ type RouteRenderOptions = SSRRouteSource & {
|
|
|
99
97
|
queryPrefetch?: QueryPrefetchContext;
|
|
100
98
|
telemetry?: Pick<CoreTelemetry, 'ssrRender'>; /** @internal Precomposed page state used by hydration verification. */
|
|
101
99
|
envelope?: PageRenderEnvelope;
|
|
100
|
+
cspNonce?: string;
|
|
102
101
|
};
|
|
103
102
|
type RouteStreamOptions = RouteRenderOptions & {
|
|
104
103
|
onChunk(html: string): void;
|
|
@@ -107,38 +106,6 @@ type RouteStreamOptions = RouteRenderOptions & {
|
|
|
107
106
|
declare function resolveRequest(opts: {
|
|
108
107
|
url: string;
|
|
109
108
|
registry: RouteRegistry;
|
|
110
|
-
manifest?: RouteManifest;
|
|
111
|
-
routes?: Array<{
|
|
112
|
-
path: string;
|
|
113
|
-
handler: RouteHandler;
|
|
114
|
-
namespace?: string;
|
|
115
|
-
}>;
|
|
116
|
-
auth?: RouteAuthOptions;
|
|
117
|
-
authContext?: AuthContext;
|
|
118
|
-
request?: Request;
|
|
119
|
-
signal?: AbortSignal;
|
|
120
|
-
} | {
|
|
121
|
-
url: string;
|
|
122
|
-
manifest: RouteManifest;
|
|
123
|
-
registry?: RouteRegistry;
|
|
124
|
-
routes?: Array<{
|
|
125
|
-
path: string;
|
|
126
|
-
handler: RouteHandler;
|
|
127
|
-
namespace?: string;
|
|
128
|
-
}>;
|
|
129
|
-
auth?: RouteAuthOptions;
|
|
130
|
-
authContext?: AuthContext;
|
|
131
|
-
request?: Request;
|
|
132
|
-
signal?: AbortSignal;
|
|
133
|
-
} | {
|
|
134
|
-
url: string;
|
|
135
|
-
manifest?: RouteManifest;
|
|
136
|
-
registry?: RouteRegistry;
|
|
137
|
-
routes: Array<{
|
|
138
|
-
path: string;
|
|
139
|
-
handler: RouteHandler;
|
|
140
|
-
namespace?: string;
|
|
141
|
-
}>;
|
|
142
109
|
auth?: RouteAuthOptions;
|
|
143
110
|
authContext?: AuthContext;
|
|
144
111
|
request?: Request;
|
|
@@ -167,16 +134,13 @@ declare function renderToStringSync(component: (props?: Record<string, unknown>)
|
|
|
167
134
|
seed?: number;
|
|
168
135
|
data?: SSRData; /** @internal A composed page render envelope. */
|
|
169
136
|
envelope?: PageRenderEnvelope;
|
|
137
|
+
cspNonce?: string;
|
|
170
138
|
}): string;
|
|
171
139
|
//#endregion
|
|
172
140
|
//#region src/ssr/render-resolved.d.ts
|
|
173
141
|
declare function renderResolvedToStringSync(opts: {
|
|
174
142
|
url: string;
|
|
175
|
-
|
|
176
|
-
path: string;
|
|
177
|
-
handler: RouteHandler;
|
|
178
|
-
namespace?: string;
|
|
179
|
-
}>;
|
|
143
|
+
registry: RouteRegistry;
|
|
180
144
|
handler: RouteHandler;
|
|
181
145
|
params?: Record<string, string>;
|
|
182
146
|
options?: {
|
|
@@ -184,14 +148,14 @@ declare function renderResolvedToStringSync(opts: {
|
|
|
184
148
|
data?: SSRData;
|
|
185
149
|
dataRuntime?: DataRuntime;
|
|
186
150
|
envelope?: PageRenderEnvelope;
|
|
151
|
+
cspNonce?: string;
|
|
187
152
|
};
|
|
188
153
|
}): string;
|
|
189
154
|
//#endregion
|
|
190
155
|
//#region src/ssr/route-request-render.d.ts
|
|
191
156
|
interface RenderRouteRequestOptions {
|
|
192
157
|
url: string;
|
|
193
|
-
|
|
194
|
-
registry?: RouteRegistry;
|
|
158
|
+
registry: RouteRegistry;
|
|
195
159
|
auth?: RouteAuthOptions;
|
|
196
160
|
authContext?: AuthContext;
|
|
197
161
|
request?: Request;
|
|
@@ -204,6 +168,7 @@ interface RenderRouteRequestOptions {
|
|
|
204
168
|
queryPrefetch?: QueryPrefetchContext;
|
|
205
169
|
queryRegistry?: ServerQueryRegistry;
|
|
206
170
|
telemetry?: CoreTelemetry;
|
|
171
|
+
cspNonce?: string;
|
|
207
172
|
}
|
|
208
173
|
type RenderRouteRequestResult = {
|
|
209
174
|
kind: 'render';
|
package/dist/ssr/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { u as SSRDataMissingError } from "../render-context-Clw3rFiO.js";
|
|
2
|
-
import { a as renderResolvedToStringSync, c as createRenderContext, d as withRenderContextAsync, i as renderRouteRequestToString, l as getRenderContext, n as renderToString, o as renderToStringSync, r as renderRouteRequest, s as resolveRequest, t as renderToStream, u as withRenderContext } from "../ssr-
|
|
2
|
+
import { a as renderResolvedToStringSync, c as createRenderContext, d as withRenderContextAsync, i as renderRouteRequestToString, l as getRenderContext, n as renderToString, o as renderToStringSync, r as renderRouteRequest, s as resolveRequest, t as renderToStream, u as withRenderContext } from "../ssr-C0hzqhY8.js";
|
|
3
3
|
export { SSRDataMissingError, createRenderContext, getRenderContext, renderResolvedToStringSync, renderRouteRequest, renderRouteRequestToString, renderToStream, renderToString, renderToStringSync, resolveRequest, withRenderContext, withRenderContextAsync };
|
|
@@ -6,13 +6,15 @@ import { c as callWithContext, d as getExecutionContextFrame, f as getVNodeConte
|
|
|
6
6
|
import { f as getCurrentComponentInstance, w as setCurrentComponentInstance } from "./component-scope-CC3cojX9.js";
|
|
7
7
|
import { h as cleanupComponent, m as isPromiseLike } from "./component-lifecycle-DEMPihoZ.js";
|
|
8
8
|
import { g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, o as rollbackForStateTransaction, r as commitForStateTransaction } from "./for-internal-B2rBq6yB.js";
|
|
9
|
+
import { _ as isEmptyPageRenderEnvelope, g as createPageRenderEnvelope, i as isDeferred, l as createRouteRegistry, m as resolveRouteRequest, t as DEFERRED_BOUNDARY, v as isPageRenderEnvelope } from "./deferred-B71c2eio.js";
|
|
9
10
|
import { c as stopRenderPhase$1, o as startRenderPhase$1, t as configureRenderContextProvider, u as SSRDataMissingError } from "./render-context-Clw3rFiO.js";
|
|
10
11
|
import { a as disposeDefaultPortalScope, t as DefaultPortal } from "./portal-D2tPWNrC.js";
|
|
11
12
|
import { t as getPublicAttributeName } from "./attr-names-CjhzmHOn.js";
|
|
13
|
+
import { r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-C-8E85El.js";
|
|
12
14
|
import { t as createDataRuntime } from "./data-runtime-SJ1BSUvk.js";
|
|
13
15
|
import { c as withTelemetry, i as dehydrateDataRuntime, t as createQueryPrefetchContext } from "./query-registry-CQIT4mXr.js";
|
|
14
|
-
import { a as resolveRouteFromRoutes, t as _resolveRouteMatchFromRoutes } from "./route-matching-
|
|
15
|
-
import {
|
|
16
|
+
import { a as resolveRouteFromRoutes, t as _resolveRouteMatchFromRoutes } from "./route-matching-C93To416.js";
|
|
17
|
+
import { a as route } from "./authoring-CXMPffwN.js";
|
|
16
18
|
import { n as renderDocument, t as SSR_RENDER_DATA_ATTR } from "./ssr-CbQO5xd3.js";
|
|
17
19
|
//#region src/ssr/escape.ts
|
|
18
20
|
/**
|
|
@@ -260,7 +262,8 @@ function createRenderContext(seed = 12345, opts = {}) {
|
|
|
260
262
|
keyCounter: 0,
|
|
261
263
|
renderData: envelope,
|
|
262
264
|
hydrationData: envelope,
|
|
263
|
-
deferredBoundaries: []
|
|
265
|
+
deferredBoundaries: [],
|
|
266
|
+
cspNonce: opts.cspNonce
|
|
264
267
|
};
|
|
265
268
|
}
|
|
266
269
|
/**
|
|
@@ -487,31 +490,20 @@ var StreamSink = class {
|
|
|
487
490
|
//#endregion
|
|
488
491
|
//#region src/ssr/route-render.ts
|
|
489
492
|
async function resolveRequest(opts) {
|
|
493
|
+
if (!opts?.registry) throw new TypeError("resolveRequest requires opts.registry.");
|
|
490
494
|
const { url, auth, authContext, request, signal } = opts;
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (manifest) return await resolveRouteRequest(url, {
|
|
494
|
-
manifest,
|
|
495
|
+
return await resolveRouteRequest(url, {
|
|
496
|
+
manifest: opts.registry.manifest,
|
|
495
497
|
mode: "ssr",
|
|
496
498
|
auth,
|
|
497
499
|
authContext,
|
|
498
500
|
request,
|
|
499
501
|
signal
|
|
500
502
|
});
|
|
501
|
-
if (!routes) throw new Error("resolveRequest requires a route manifest or route table.");
|
|
502
|
-
const resolved = resolveRouteFromRoutes(new URL(url, "http://localhost").pathname, routes);
|
|
503
|
-
if (!resolved) return null;
|
|
504
|
-
await _preloadRouteHandler(resolved.handler);
|
|
505
|
-
return {
|
|
506
|
-
kind: "render",
|
|
507
|
-
handler: resolved.handler,
|
|
508
|
-
params: resolved.params
|
|
509
|
-
};
|
|
510
503
|
}
|
|
511
504
|
function resolveSSRRouteSource(source) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
return routes.map((route) => ({
|
|
505
|
+
if (source.registry.routes.length === 0) throw new Error("SSR requires a non-empty route registry.");
|
|
506
|
+
return source.registry.routes.map((route) => ({
|
|
515
507
|
...route,
|
|
516
508
|
path: route.path,
|
|
517
509
|
handler: route.handler,
|
|
@@ -520,6 +512,7 @@ function resolveSSRRouteSource(source) {
|
|
|
520
512
|
}
|
|
521
513
|
function resolveSSRRouteRender(opts) {
|
|
522
514
|
const { seed = 12345, data, document } = opts;
|
|
515
|
+
const cspNonce = validateCspNonce(opts.cspNonce);
|
|
523
516
|
const routeTable = resolveSSRRouteSource(opts);
|
|
524
517
|
const resolvedRoute = resolvePolicyAwareSSRRoute(opts, routeTable);
|
|
525
518
|
const requestUrl = new URL(resolvedRoute.url, "http://localhost");
|
|
@@ -530,7 +523,8 @@ function resolveSSRRouteRender(opts) {
|
|
|
530
523
|
routes: routeTable,
|
|
531
524
|
dataRuntime: opts.dataRuntime,
|
|
532
525
|
queryPrefetch: opts.queryPrefetch,
|
|
533
|
-
envelope: opts.envelope
|
|
526
|
+
envelope: opts.envelope,
|
|
527
|
+
cspNonce
|
|
534
528
|
});
|
|
535
529
|
return {
|
|
536
530
|
url: resolvedRoute.url,
|
|
@@ -540,7 +534,8 @@ function resolveSSRRouteRender(opts) {
|
|
|
540
534
|
seed,
|
|
541
535
|
data,
|
|
542
536
|
ctx,
|
|
543
|
-
document
|
|
537
|
+
document,
|
|
538
|
+
cspNonce
|
|
544
539
|
};
|
|
545
540
|
}
|
|
546
541
|
function buildDocumentRenderArgs(resolved, appHtml) {
|
|
@@ -558,7 +553,8 @@ function buildDocumentRenderArgs(resolved, appHtml) {
|
|
|
558
553
|
route: {
|
|
559
554
|
path: resolved.route.path,
|
|
560
555
|
namespace: resolved.route.namespace
|
|
561
|
-
}
|
|
556
|
+
},
|
|
557
|
+
cspNonce: resolved.cspNonce
|
|
562
558
|
}
|
|
563
559
|
};
|
|
564
560
|
}
|
|
@@ -800,7 +796,10 @@ function withControlBoundaryChildren(node, render) {
|
|
|
800
796
|
const ESCAPED_ATTR_VALUE_CACHE_LIMIT = 512;
|
|
801
797
|
const escapedAttrValueCache = /* @__PURE__ */ new Map();
|
|
802
798
|
function isEventHandler(key) {
|
|
803
|
-
return key.length >=
|
|
799
|
+
return key.length >= 2 && key.slice(0, 2).toLowerCase() === "on";
|
|
800
|
+
}
|
|
801
|
+
function assertAttributeName(name) {
|
|
802
|
+
if (!/^[A-Za-z_:][A-Za-z0-9_.:-]*$/.test(name)) throw new TypeError(`Invalid SSR attribute name: ${JSON.stringify(name)}`);
|
|
804
803
|
}
|
|
805
804
|
function getEscapedAttrValue(value) {
|
|
806
805
|
if (value.length > 64) return escapeAttr(value);
|
|
@@ -825,6 +824,7 @@ function renderAttrsDirect(props, sink) {
|
|
|
825
824
|
if (isEventHandler(key)) continue;
|
|
826
825
|
if (key.charCodeAt(0) === 95) continue;
|
|
827
826
|
const attrName = getPublicAttributeName(key);
|
|
827
|
+
assertAttributeName(attrName);
|
|
828
828
|
if (attrName === "style") {
|
|
829
829
|
const css = typeof value === "string" ? value : styleObjToCss(value);
|
|
830
830
|
if (!css) continue;
|
|
@@ -864,6 +864,7 @@ function renderAttrs(props, opts) {
|
|
|
864
864
|
if (isEventHandler(key)) continue;
|
|
865
865
|
if (key.charCodeAt(0) === 95) continue;
|
|
866
866
|
const attrName = key === "class" ? "class" : getPublicAttributeName(key);
|
|
867
|
+
assertAttributeName(attrName);
|
|
867
868
|
if (attrName === "style") {
|
|
868
869
|
const css = typeof value === "string" ? value : styleObjToCss(value);
|
|
869
870
|
if (css === null || css === "") continue;
|
|
@@ -1066,6 +1067,7 @@ function renderNodeSync(node, ctx) {
|
|
|
1066
1067
|
throw new Error(`renderNodeSync: unsupported VNode symbol type: ${String(type)}`);
|
|
1067
1068
|
}
|
|
1068
1069
|
const typeStr = type;
|
|
1070
|
+
assertElementName(typeStr);
|
|
1069
1071
|
if (VOID_ELEMENTS.has(typeStr)) return `<${typeStr}${renderAttrs(props)} />`;
|
|
1070
1072
|
const maybeDangerous = props?.dangerouslySetInnerHTML;
|
|
1071
1073
|
if (maybeDangerous !== void 0 && maybeDangerous !== null) {
|
|
@@ -1118,6 +1120,7 @@ function renderNodeSyncToSink(node, sink, ctx) {
|
|
|
1118
1120
|
throw new Error(`renderNodeSyncToSink: unsupported VNode symbol type: ${String(type)}`);
|
|
1119
1121
|
}
|
|
1120
1122
|
const typeStr = type;
|
|
1123
|
+
assertElementName(typeStr);
|
|
1121
1124
|
if (VOID_ELEMENTS.has(typeStr)) {
|
|
1122
1125
|
sinkWrite2(sink, "<", typeStr);
|
|
1123
1126
|
renderAttrsDirect(props, sink);
|
|
@@ -1171,14 +1174,19 @@ function renderNodeSyncToSink(node, sink, ctx) {
|
|
|
1171
1174
|
sinkWrite3(sink, "</", typeStr, ">");
|
|
1172
1175
|
}
|
|
1173
1176
|
function renderToStringSync(component, props, options) {
|
|
1174
|
-
const
|
|
1177
|
+
const seed = options?.seed ?? 12345;
|
|
1178
|
+
const nonce = validateCspNonce(options?.cspNonce);
|
|
1179
|
+
const ctx = createRenderContext(seed, {
|
|
1175
1180
|
data: options?.data,
|
|
1176
1181
|
envelope: options?.envelope
|
|
1177
1182
|
});
|
|
1178
1183
|
return withRenderContext(ctx, () => {
|
|
1179
1184
|
startRenderPhase(ctx.renderData);
|
|
1180
1185
|
try {
|
|
1181
|
-
const node = renderSyncComponentRoot(component
|
|
1186
|
+
const node = renderSyncComponentRoot(nonce === void 0 ? component : () => CspNonceScope({
|
|
1187
|
+
value: nonce,
|
|
1188
|
+
children: () => component(props)
|
|
1189
|
+
}), props || {}, ctx);
|
|
1182
1190
|
if (!node) throw new Error("renderToStringSync: wrapped component returned empty");
|
|
1183
1191
|
const sink = new StringSink();
|
|
1184
1192
|
renderNodeSyncToSink(node, sink, ctx);
|
|
@@ -1199,7 +1207,10 @@ function renderSSRRouteAppToSink(input) {
|
|
|
1199
1207
|
withRenderContext(ctx, () => {
|
|
1200
1208
|
startRenderPhase(ctx.renderData);
|
|
1201
1209
|
try {
|
|
1202
|
-
renderRenderableSyncToSink(executeComponentSync(route.handler
|
|
1210
|
+
renderRenderableSyncToSink(executeComponentSync(ctx.cspNonce === void 0 ? route.handler : () => CspNonceScope({
|
|
1211
|
+
value: ctx.cspNonce,
|
|
1212
|
+
children: () => route.handler(params)
|
|
1213
|
+
}), params, ctx), sink, ctx);
|
|
1203
1214
|
if (ctx.deferredBoundaries.length === 0) sink.write(serializeHydrationRenderData(ctx.hydrationData ?? data, ctx.dataRuntime));
|
|
1204
1215
|
} finally {
|
|
1205
1216
|
try {
|
|
@@ -1210,6 +1221,9 @@ function renderSSRRouteAppToSink(input) {
|
|
|
1210
1221
|
}
|
|
1211
1222
|
});
|
|
1212
1223
|
}
|
|
1224
|
+
function assertElementName(name) {
|
|
1225
|
+
if (!/^[A-Za-z][A-Za-z0-9._:-]*$/.test(name)) throw new TypeError(`Invalid SSR element name: ${JSON.stringify(name)}`);
|
|
1226
|
+
}
|
|
1213
1227
|
//#endregion
|
|
1214
1228
|
//#region src/ssr/render-resolved.ts
|
|
1215
1229
|
function sameRouteParams(left, right) {
|
|
@@ -1221,23 +1235,28 @@ function sameRouteParams(left, right) {
|
|
|
1221
1235
|
return true;
|
|
1222
1236
|
}
|
|
1223
1237
|
function renderResolvedToStringSync(opts) {
|
|
1224
|
-
const { url,
|
|
1238
|
+
const { url, registry, handler, params, options } = opts;
|
|
1239
|
+
const routes = registry.routes;
|
|
1225
1240
|
const requestUrl = new URL(url, "http://localhost");
|
|
1226
1241
|
const matchedIndex = routes.findIndex((route) => {
|
|
1227
1242
|
const resolved = resolveRouteFromRoutes(requestUrl.pathname, [route]);
|
|
1228
1243
|
return resolved !== null && resolved.handler === route.handler && sameRouteParams(resolved.params, params);
|
|
1229
1244
|
});
|
|
1230
1245
|
if (matchedIndex < 0) throw new Error(`renderResolvedToStringSync: no route found for url: ${url}`);
|
|
1246
|
+
const effectiveRoutes = routes.map((route, index) => index === matchedIndex ? {
|
|
1247
|
+
...route,
|
|
1248
|
+
handler
|
|
1249
|
+
} : route);
|
|
1231
1250
|
return renderToString({
|
|
1232
1251
|
url,
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
} : route),
|
|
1252
|
+
registry: createRouteRegistry(() => {
|
|
1253
|
+
for (const route$1 of effectiveRoutes) route(route$1.path, route$1.handler, { namespace: route$1.namespace });
|
|
1254
|
+
}),
|
|
1237
1255
|
seed: options?.seed,
|
|
1238
1256
|
data: options?.data,
|
|
1239
1257
|
dataRuntime: options?.dataRuntime,
|
|
1240
|
-
envelope: options?.envelope
|
|
1258
|
+
envelope: options?.envelope,
|
|
1259
|
+
cspNonce: options?.cspNonce
|
|
1241
1260
|
});
|
|
1242
1261
|
}
|
|
1243
1262
|
//#endregion
|
|
@@ -1264,17 +1283,18 @@ function settleWithSignal(promise, signal) {
|
|
|
1264
1283
|
function stripHydrationPayload(html) {
|
|
1265
1284
|
return html.replace(/<script type="application\/json" data-askr-render-data="true">[\s\S]*?<\/script>$/, "");
|
|
1266
1285
|
}
|
|
1267
|
-
function renderBoundary(boundary, state, payload, seed, data) {
|
|
1286
|
+
function renderBoundary(boundary, state, payload, seed, data, cspNonce) {
|
|
1268
1287
|
return stripHydrationPayload(renderToStringSync(() => state === "fulfilled" ? boundary.fulfilled(payload) : boundary.rejected(payload), {}, {
|
|
1269
1288
|
seed,
|
|
1270
|
-
envelope: data ?? void 0
|
|
1289
|
+
envelope: data ?? void 0,
|
|
1290
|
+
cspNonce
|
|
1271
1291
|
}));
|
|
1272
1292
|
}
|
|
1273
|
-
function patchChunk(id, html) {
|
|
1293
|
+
function patchChunk(id, html, cspNonce) {
|
|
1274
1294
|
const selector = `askr-resolve[data-askr-deferred=${JSON.stringify(id)}]`;
|
|
1275
|
-
return `<template data-askr-deferred-patch="${id}">${html}</template><script data-askr-deferred-apply="${id}">(function(s){var t=s.previousElementSibling;var b=document.querySelector(${JSON.stringify(selector)});if(b&&t){b.replaceWith(t.content.cloneNode(true));}if(t)t.remove();s.remove();})(document.currentScript)<\/script>`;
|
|
1295
|
+
return `<template data-askr-deferred-patch="${id}">${html}</template><script${cspNonce ? ` nonce="${cspNonce}"` : ""} data-askr-deferred-apply="${id}">(function(s){var t=s.previousElementSibling;var b=document.querySelector(${JSON.stringify(selector)});if(b&&t){b.replaceWith(t.content.cloneNode(true));}if(t)t.remove();s.remove();})(document.currentScript)<\/script>`;
|
|
1276
1296
|
}
|
|
1277
|
-
function createDeferredRenderStream(initialHtml, boundaries, signal, seed, data, runtime) {
|
|
1297
|
+
function createDeferredRenderStream(initialHtml, boundaries, signal, seed, data, runtime, cspNonce) {
|
|
1278
1298
|
const encoder = new TextEncoder();
|
|
1279
1299
|
const local = new AbortController();
|
|
1280
1300
|
let cancelled = false;
|
|
@@ -1310,13 +1330,13 @@ function createDeferredRenderStream(initialHtml, boundaries, signal, seed, data,
|
|
|
1310
1330
|
boundaryIndex += 1;
|
|
1311
1331
|
try {
|
|
1312
1332
|
const value = await settleWithSignal(boundary.promise, local.signal);
|
|
1313
|
-
if (!cancelled && !closed) controller.enqueue(encoder.encode(patchChunk(boundary.id, renderBoundary(boundary, "fulfilled", value, seed, data))));
|
|
1333
|
+
if (!cancelled && !closed) controller.enqueue(encoder.encode(patchChunk(boundary.id, renderBoundary(boundary, "fulfilled", value, seed, data, cspNonce), cspNonce)));
|
|
1314
1334
|
} catch (error) {
|
|
1315
1335
|
if (local.signal.aborted) {
|
|
1316
1336
|
if (!cancelled && !closed) close(controller);
|
|
1317
1337
|
return;
|
|
1318
1338
|
}
|
|
1319
|
-
if (!cancelled && !closed) controller.enqueue(encoder.encode(patchChunk(boundary.id, renderBoundary(boundary, "rejected", error, seed, data))));
|
|
1339
|
+
if (!cancelled && !closed) controller.enqueue(encoder.encode(patchChunk(boundary.id, renderBoundary(boundary, "rejected", error, seed, data, cspNonce), cspNonce)));
|
|
1320
1340
|
}
|
|
1321
1341
|
return;
|
|
1322
1342
|
}
|
|
@@ -1344,8 +1364,9 @@ async function renderRouteRequestToString(options) {
|
|
|
1344
1364
|
};
|
|
1345
1365
|
}
|
|
1346
1366
|
async function renderRouteRequestInternal(options) {
|
|
1347
|
-
|
|
1348
|
-
|
|
1367
|
+
if (!options?.registry) throw new TypeError("renderRouteRequest requires options.registry.");
|
|
1368
|
+
const cspNonce = validateCspNonce(options.cspNonce);
|
|
1369
|
+
const manifest = options.registry.manifest;
|
|
1349
1370
|
const signal = options.signal ?? options.request?.signal ?? new AbortController().signal;
|
|
1350
1371
|
const request = options.request ?? new Request(new URL(options.url, "http://localhost"));
|
|
1351
1372
|
const runtime = options.dataRuntime ?? createDataRuntime();
|
|
@@ -1364,7 +1385,8 @@ async function renderRouteRequestInternal(options) {
|
|
|
1364
1385
|
signal,
|
|
1365
1386
|
dataRuntime: runtime,
|
|
1366
1387
|
queryPrefetch: prefetch,
|
|
1367
|
-
mode: "ssr"
|
|
1388
|
+
mode: "ssr",
|
|
1389
|
+
cspNonce
|
|
1368
1390
|
});
|
|
1369
1391
|
return withTelemetry(options.telemetry?.ssrRender, {}, () => withRenderContextAsync(context, async () => {
|
|
1370
1392
|
const resolved = await resolveRouteRequest(options.url, {
|
|
@@ -1395,7 +1417,7 @@ async function renderRouteRequestInternal(options) {
|
|
|
1395
1417
|
const result = {
|
|
1396
1418
|
kind: "render",
|
|
1397
1419
|
html,
|
|
1398
|
-
...boundaries.length > 0 ? { stream: createDeferredRenderStream(html, boundaries, signal, options.seed, context.hydrationData, runtime) } : {},
|
|
1420
|
+
...boundaries.length > 0 ? { stream: createDeferredRenderStream(html, boundaries, signal, options.seed, context.hydrationData, runtime, cspNonce) } : {},
|
|
1399
1421
|
params: resolved.params
|
|
1400
1422
|
};
|
|
1401
1423
|
if (resolved.record) Object.defineProperty(result, "record", {
|