@askrjs/askr 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/access-CEA2pOry.js +1046 -0
- package/dist/actions/index.d.ts +16 -2
- package/dist/actions/index.js +5 -5
- package/dist/{activity-FirwPXA_.js → activity-oy2YFTFF.js} +10 -16
- package/dist/benchmark.js +8 -9
- package/dist/boot/index.d.ts +19 -9
- package/dist/boot/index.js +1 -1
- package/dist/{boot-tWyF6121.js → boot-BMBMWUGI.js} +680 -441
- package/dist/capabilities-Bbf0mlp9.js +42 -0
- package/dist/components/index.d.ts +11 -8
- package/dist/components/index.js +1 -1
- package/dist/{compose-ref-Bfaf4kUC.js → compose-ref-v0qGlHoN.js} +7 -1
- package/dist/control/index.d.ts +20 -2
- package/dist/control/index.js +1 -1
- package/dist/{control-C-wSaq9V.js → control-BR_mrX__.js} +4 -4
- package/dist/core.d.ts +163 -0
- package/dist/csp-nonce-BfFWcd0w.js +173 -0
- package/dist/data/index.d.ts +74 -2
- package/dist/data/index.js +3 -3
- package/dist/{data-ghKopAbp.js → data-2edW0jhF.js} +108 -122
- package/dist/{data-runtime-7t_Y0iv3.js → data-runtime-CxoY98lX.js} +22 -15
- package/dist/{deferred-CxjWCkIn.js → deferred-CbgNhf67.js} +5 -5
- package/dist/dom-renderer.d.ts +77 -0
- package/dist/domains/actions.d.ts +70 -0
- package/dist/domains/component.d.ts +119 -0
- package/dist/domains/context.d.ts +73 -0
- package/dist/domains/control.d.ts +328 -0
- package/dist/domains/data.d.ts +461 -0
- package/dist/domains/diagnostics.d.ts +21 -0
- package/dist/domains/lifecycle.d.ts +112 -0
- package/dist/domains/refs.d.ts +18 -0
- package/dist/domains/renderer.d.ts +127 -0
- package/dist/domains/routing.d.ts +501 -0
- package/dist/domains/scheduler.d.ts +79 -0
- package/dist/domains/server.d.ts +112 -0
- package/dist/domains/state.d.ts +129 -0
- package/dist/domains/telemetry.d.ts +29 -0
- package/dist/eager-control.d.ts +8 -0
- package/dist/{effect-CNwstBBD.js → effect-C3Zsc1k6.js} +14 -41
- package/dist/{types-DWVrKn9N.d.ts → elements.d.ts} +79 -42
- package/dist/{for-internal-DyBHfKJA.js → for-state-BDykeOJL.js} +598 -494
- package/dist/foundations/icon/index.d.ts +67 -16
- package/dist/foundations/icon/index.js +1 -1
- package/dist/foundations/index.d.ts +26 -3
- package/dist/foundations/index.js +1 -1
- package/dist/foundations/interactions/index.d.ts +73 -34
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +12 -5
- package/dist/foundations/state/index.js +1 -1
- package/dist/foundations/structures/index.d.ts +44 -3
- package/dist/foundations/structures/index.js +1 -1
- package/dist/foundations/utilities/index.d.ts +34 -3
- package/dist/foundations/utilities/index.js +1 -1
- package/dist/fx/index.d.ts +72 -25
- package/dist/fx/index.js +8 -8
- package/dist/index.d.ts +121 -4
- package/dist/index.js +316 -47
- package/dist/{component-internal-C4hKKQBF.js → instance-VAFYVJOa.js} +495 -283
- package/dist/{interactions-D102JxBR.js → interactions-CTeACdT_.js} +1 -1
- package/dist/jsx-dev-runtime.d.ts +51 -11
- package/dist/jsx-globals.d.ts +2 -0
- package/dist/jsx-runtime-CPJXAOBF.js +43 -0
- package/dist/jsx-runtime.d.ts +17 -3
- package/dist/jsx-runtime.js +2 -42
- package/dist/jsx.d.ts +108 -0
- package/dist/{manifest-D6rfj_jC.js → manifest-D1P3UNWV.js} +4 -9
- package/dist/{navigate-ZZu1IV0B.js → navigate-prC5V8XV.js} +125 -472
- package/dist/{navigate-F4sHgWbw.d.ts → navigation.d.ts} +8 -8
- package/dist/{readable-Di1fzSwm.js → notify-DxxuDPeY.js} +96 -34
- package/dist/{query-registry-Bua3cDyb.js → query-registry-LrYpQsDJ.js} +1 -1
- package/dist/{compose-ref-DOJHoW5X.d.ts → refs.d.ts} +11 -7
- package/dist/{ssr-Doog-AfJ.js → render-resolved-BC0W7_5b.js} +165 -558
- package/dist/{renderer-Cl23O0hV.js → renderer-CT0HeY58.js} +6825 -5692
- package/dist/{resolution-ClD-K7Lz.js → resolution-3DskUkjG.js} +189 -180
- package/dist/{resource-operation-Vz1S5h2k.js → resource-18-sFRl9.js} +98 -58
- package/dist/resources/index.d.ts +64 -12
- package/dist/resources/index.js +214 -8
- package/dist/route-activity.d.ts +21 -0
- package/dist/{route-matching-Bo6UqG-5.js → route-matching-CUzZ31gT.js} +68 -82
- package/dist/router/index.d.ts +378 -79
- package/dist/router/index.js +9 -9
- package/dist/router-internal-7nuNEHfG.js +36 -0
- package/dist/scope-Cq_iYbTR.js +403 -0
- package/dist/{selector-CsKuomw0.js → selector-BSPVkjKP.js} +26 -27
- package/dist/{shared-CYBH3F8I.js → shared-kU8yvryp.js} +3 -6
- package/dist/{snapshot-source-BB42xgwb.js → snapshot-source-An4iKajG.js} +1 -1
- package/dist/ssg/index.d.ts +51 -15
- package/dist/ssg/index.js +269 -209
- package/dist/ssr/index.d.ts +121 -68
- package/dist/ssr/index.js +2 -1
- package/dist/ssr-CiNm6Vzl.js +393 -0
- package/dist/{ssr-DSHd_Q9K.js → ssr-EPjwJH5R.js} +5 -1
- package/dist/{state-BGJgYSyQ.js → state-BXJfudq1.js} +10 -15
- package/dist/{structures-DFyK5AdQ.js → structures-CjEzfj4h.js} +37 -57
- package/dist/{index-CTpMmzgN.d.ts → structures.d.ts} +57 -36
- package/dist/testing/index.d.ts +123 -24
- package/dist/testing/index.js +24 -61
- package/dist/timer-C7uqnJVt.js +85 -0
- package/dist/{index-B1gu0_m0.d.ts → utilities.d.ts} +24 -13
- package/dist/{verify-hydration-BZPJ_kYa.js → verify-hydration-ikaxotf7.js} +8 -5
- package/package.json +12 -14
- package/dist/access-BucjYZcX.js +0 -509
- package/dist/activity-DooIuxQO.d.ts +0 -14
- package/dist/benchmark.d.ts +0 -26
- package/dist/cleanup-TXSsApEl.js +0 -535
- package/dist/component-cleanup-jdbgjkT1.js +0 -143
- package/dist/component-scope-4I3yEMkb.js +0 -195
- package/dist/control-Cxag1yqV.d.ts +0 -8
- package/dist/csp-nonce-9NFUb7hJ.js +0 -72
- package/dist/css-BuPaTOfe.js +0 -70
- package/dist/fastlane-BKZduiBQ.js +0 -166
- package/dist/index-Dgjbo2gU.d.ts +0 -1456
- package/dist/jsx-runtime-Cg7GqY1S.d.ts +0 -2
- package/dist/jsx-runtime-gVIaTPw0.d.ts +0 -38
- package/dist/lifecycle-batch-63aXFf2S.js +0 -253
- package/dist/lifecycle-operations-Q0k3EFnG.js +0 -287
package/dist/ssr/index.d.ts
CHANGED
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
SSRStyleRegistrationValidation,
|
|
3
|
+
SSRPortalState,
|
|
4
|
+
DocumentRenderer,
|
|
5
|
+
DeferredBoundaryRegistration,
|
|
6
|
+
DocumentRenderContext,
|
|
7
|
+
RouteAuthOptions,
|
|
8
|
+
RouteRegistry,
|
|
9
|
+
RouteHandler,
|
|
10
|
+
SSRData,
|
|
11
|
+
DocumentRenderArgs,
|
|
12
|
+
Route,
|
|
13
|
+
RouteRecord,
|
|
14
|
+
RouteRequestResult,
|
|
15
|
+
ServerQueryRegistry,
|
|
16
|
+
CoreTelemetry,
|
|
17
|
+
AccessDenyDecision,
|
|
18
|
+
QueryPrefetchContext,
|
|
19
|
+
SSRStyleRegistration,
|
|
20
|
+
DataRuntime,
|
|
21
|
+
AccessRedirectDecision,
|
|
22
|
+
} from '../core.js';
|
|
23
|
+
import { JSXElement, Props } from '../elements.js';
|
|
24
|
+
import { AuthContext } from '@askrjs/auth';
|
|
5
25
|
declare const PAGE_RENDER_ENVELOPE_VERSION: 1;
|
|
6
26
|
interface PageRenderEnvelope {
|
|
7
27
|
readonly version: typeof PAGE_RENDER_ENVELOPE_VERSION;
|
|
@@ -9,17 +29,13 @@ interface PageRenderEnvelope {
|
|
|
9
29
|
readonly route: unknown;
|
|
10
30
|
readonly framework: Readonly<Record<string, unknown>>;
|
|
11
31
|
}
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/common/ssr-errors.d.ts
|
|
14
32
|
/**
|
|
15
33
|
* Common call contracts: SSR error types
|
|
16
34
|
*/
|
|
17
35
|
declare class SSRDataMissingError extends Error {
|
|
18
|
-
readonly code =
|
|
36
|
+
readonly code = 'SSR_DATA_MISSING';
|
|
19
37
|
constructor(message?: string);
|
|
20
38
|
}
|
|
21
|
-
//#endregion
|
|
22
|
-
//#region src/ssr/context.d.ts
|
|
23
39
|
interface RenderContext {
|
|
24
40
|
url: string;
|
|
25
41
|
seed: number;
|
|
@@ -45,36 +61,40 @@ interface RenderContext {
|
|
|
45
61
|
cspNonce?: string;
|
|
46
62
|
}
|
|
47
63
|
/** Build a fresh SSR render context (data cache, routes, seed) for a render pass. */
|
|
48
|
-
declare function createRenderContext(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
declare function createRenderContext(
|
|
65
|
+
seed?: number,
|
|
66
|
+
opts?: {
|
|
67
|
+
url?: string;
|
|
68
|
+
data?: SSRData;
|
|
69
|
+
params?: Record<string, string>;
|
|
70
|
+
routes?: readonly Route[];
|
|
71
|
+
routeAuth?: RouteAuthOptions;
|
|
72
|
+
authContext?: AuthContext;
|
|
73
|
+
basePath?: string;
|
|
74
|
+
signal?: AbortSignal;
|
|
75
|
+
dataRuntime?: unknown;
|
|
76
|
+
mode?: 'ssr' | 'spa';
|
|
77
|
+
queryPrefetch?: QueryPrefetchContext;
|
|
78
|
+
framework?: Readonly<Record<string, unknown>>;
|
|
79
|
+
envelope?: PageRenderEnvelope;
|
|
80
|
+
cspNonce?: string;
|
|
81
|
+
}
|
|
82
|
+
): RenderContext;
|
|
64
83
|
/**
|
|
65
84
|
* Run a function with the given render context.
|
|
66
85
|
* Concurrency-safe in Node.js via AsyncLocalStorage.
|
|
67
86
|
*/
|
|
68
87
|
declare function withRenderContext<T>(ctx: RenderContext, fn: () => T): T;
|
|
69
88
|
/** Run `fn` with `ctx` as the active SSR render context, using async-local storage. */
|
|
70
|
-
declare function withRenderContextAsync<T>(
|
|
89
|
+
declare function withRenderContextAsync<T>(
|
|
90
|
+
ctx: RenderContext,
|
|
91
|
+
fn: () => T | PromiseLike<T>
|
|
92
|
+
): Promise<T>;
|
|
71
93
|
/**
|
|
72
94
|
* Get the current render context.
|
|
73
95
|
* Returns null if not inside a render.
|
|
74
96
|
*/
|
|
75
97
|
declare function getRenderContext(): RenderContext | null;
|
|
76
|
-
//#endregion
|
|
77
|
-
//#region src/ssr/route-render.d.ts
|
|
78
98
|
/** A single path-to-handler route binding accepted by low-level SSR route rendering. */
|
|
79
99
|
type SSRRoute = {
|
|
80
100
|
path: string;
|
|
@@ -115,8 +135,6 @@ declare function resolveRequest(opts: {
|
|
|
115
135
|
request?: Request;
|
|
116
136
|
signal?: AbortSignal;
|
|
117
137
|
}): Promise<RouteRequestResult>;
|
|
118
|
-
//#endregion
|
|
119
|
-
//#region src/ssr/types.d.ts
|
|
120
138
|
/** VNode representation for SSR rendering */
|
|
121
139
|
type VNode = {
|
|
122
140
|
type: string | SSRComponent | symbol;
|
|
@@ -128,26 +146,31 @@ type VNode = {
|
|
|
128
146
|
* Component function signature for SSR.
|
|
129
147
|
* Components receive props and an optional context with signal and SSR context.
|
|
130
148
|
*/
|
|
131
|
-
type SSRComponent = (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
149
|
+
type SSRComponent = (
|
|
150
|
+
props: Props,
|
|
151
|
+
context?: {
|
|
152
|
+
signal?: AbortSignal;
|
|
153
|
+
ssr?: RenderContext;
|
|
154
|
+
}
|
|
155
|
+
) => VNode | JSXElement | string | number | boolean | null | undefined;
|
|
137
156
|
/** Synchronously render a component to an HTML string, without route resolution. */
|
|
138
|
-
declare function renderToStringSync(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
declare function renderToStringSync(
|
|
158
|
+
component: (
|
|
159
|
+
props?: Record<string, unknown>
|
|
160
|
+
) => VNode | JSXElement | string | number | boolean | null | undefined,
|
|
161
|
+
props?: Record<string, unknown>,
|
|
162
|
+
options?: {
|
|
163
|
+
seed?: number;
|
|
164
|
+
data?: SSRData;
|
|
165
|
+
/** @internal A composed page render envelope. */
|
|
166
|
+
envelope?: PageRenderEnvelope;
|
|
167
|
+
cspNonce?: string;
|
|
168
|
+
/** @internal Request-local authentication for deferred SSR passes. */
|
|
169
|
+
authContext?: import('@askrjs/auth').AuthContext;
|
|
170
|
+
/** @internal Capture request-local registrations produced by this pass. */
|
|
171
|
+
onContext?: (ctx: RenderContext) => void;
|
|
172
|
+
}
|
|
173
|
+
): string;
|
|
151
174
|
/** Synchronously render an already-resolved route handler to an HTML string. */
|
|
152
175
|
declare function renderResolvedToStringSync(opts: {
|
|
153
176
|
url: string;
|
|
@@ -162,8 +185,6 @@ declare function renderResolvedToStringSync(opts: {
|
|
|
162
185
|
cspNonce?: string;
|
|
163
186
|
};
|
|
164
187
|
}): string;
|
|
165
|
-
//#endregion
|
|
166
|
-
//#region src/ssr/route-request-render.d.ts
|
|
167
188
|
/** Options for {@link renderRouteRequest} and {@link renderRouteRequestToString}. */
|
|
168
189
|
interface RenderRouteRequestOptions {
|
|
169
190
|
url: string;
|
|
@@ -183,28 +204,60 @@ interface RenderRouteRequestOptions {
|
|
|
183
204
|
cspNonce?: string;
|
|
184
205
|
}
|
|
185
206
|
/** Outcome of rendering a route request for SSR: a render, redirect, deny, or no-match. */
|
|
186
|
-
type RenderRouteRequestResult =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
207
|
+
type RenderRouteRequestResult =
|
|
208
|
+
| {
|
|
209
|
+
kind: 'render';
|
|
210
|
+
html: string;
|
|
211
|
+
stream?: ReadableStream<Uint8Array>;
|
|
212
|
+
styles: readonly SSRStyleRegistration[];
|
|
213
|
+
params: Record<string, string>;
|
|
214
|
+
record?: RouteRecord;
|
|
215
|
+
}
|
|
216
|
+
| AccessRedirectDecision
|
|
217
|
+
| AccessDenyDecision
|
|
218
|
+
| {
|
|
219
|
+
kind: 'no-match';
|
|
220
|
+
};
|
|
196
221
|
/** Resolve and render a route request for SSR, streaming the body when possible. */
|
|
197
|
-
declare function renderRouteRequest(
|
|
222
|
+
declare function renderRouteRequest(
|
|
223
|
+
options: RenderRouteRequestOptions
|
|
224
|
+
): Promise<RenderRouteRequestResult>;
|
|
198
225
|
/** Resolve and render a route request for SSR, always fully buffering the HTML. */
|
|
199
|
-
declare function renderRouteRequestToString(
|
|
200
|
-
|
|
201
|
-
|
|
226
|
+
declare function renderRouteRequestToString(
|
|
227
|
+
options: RenderRouteRequestOptions
|
|
228
|
+
): Promise<RenderRouteRequestResult>;
|
|
202
229
|
/**
|
|
203
230
|
* Render a component or route request to a complete HTML string, synchronously.
|
|
204
231
|
*/
|
|
205
|
-
declare function renderToString(
|
|
232
|
+
declare function renderToString(
|
|
233
|
+
component: (
|
|
234
|
+
props?: Record<string, unknown>
|
|
235
|
+
) => VNode | JSXElement | string | number | null
|
|
236
|
+
): string;
|
|
206
237
|
declare function renderToString(opts: RouteRenderOptions): string;
|
|
207
238
|
/** Stream a route request's rendered HTML to the response sink described by `opts`. */
|
|
208
239
|
declare function renderToStream(opts: RouteStreamOptions): void;
|
|
209
|
-
|
|
210
|
-
|
|
240
|
+
export {
|
|
241
|
+
type DocumentRenderArgs,
|
|
242
|
+
type DocumentRenderContext,
|
|
243
|
+
type DocumentRenderer,
|
|
244
|
+
type RenderRouteRequestOptions,
|
|
245
|
+
type RenderRouteRequestResult,
|
|
246
|
+
type SSRComponent,
|
|
247
|
+
SSRDataMissingError,
|
|
248
|
+
type SSRRoute,
|
|
249
|
+
type SSRStyleRegistration,
|
|
250
|
+
type SSRStyleRegistrationValidation,
|
|
251
|
+
type VNode,
|
|
252
|
+
createRenderContext,
|
|
253
|
+
getRenderContext,
|
|
254
|
+
renderResolvedToStringSync,
|
|
255
|
+
renderRouteRequest,
|
|
256
|
+
renderRouteRequestToString,
|
|
257
|
+
renderToStream,
|
|
258
|
+
renderToString,
|
|
259
|
+
renderToStringSync,
|
|
260
|
+
resolveRequest,
|
|
261
|
+
withRenderContext,
|
|
262
|
+
withRenderContextAsync,
|
|
263
|
+
};
|
package/dist/ssr/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { C as SSRDataMissingError } from "../render-context-B7ohlh6t.js";
|
|
2
|
-
import {
|
|
2
|
+
import { c as createRenderContext, d as withRenderContext, f as withRenderContextAsync, i as renderToStringSync, l as getRenderContext, n as renderResolvedToStringSync } from "../render-resolved-BC0W7_5b.js";
|
|
3
|
+
import { a as resolveRequest, i as renderRouteRequestToString, n as renderToString, r as renderRouteRequest, t as renderToStream } from "../ssr-CiNm6Vzl.js";
|
|
3
4
|
export { SSRDataMissingError, createRenderContext, getRenderContext, renderResolvedToStringSync, renderRouteRequest, renderRouteRequestToString, renderToStream, renderToString, renderToStringSync, resolveRequest, withRenderContext, withRenderContextAsync };
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { P as isPromiseLike } from "./instance-VAFYVJOa.js";
|
|
2
|
+
import { r as validateCspNonce } from "./csp-nonce-BfFWcd0w.js";
|
|
3
|
+
import { t as createDataRuntime } from "./data-runtime-CxoY98lX.js";
|
|
4
|
+
import { c as withTelemetry, t as createQueryPrefetchContext } from "./query-registry-LrYpQsDJ.js";
|
|
5
|
+
import { a as resolveRouteRequest, n as getRouteRenderContext, o as resolvedRouteHasLoader } from "./resolution-3DskUkjG.js";
|
|
6
|
+
import { c as removeRouteBasePath, s as normalizeRouteBasePath, t as _resolveRouteMatchFromRoutes } from "./route-matching-CUzZ31gT.js";
|
|
7
|
+
import { n as renderDocument } from "./ssr-EPjwJH5R.js";
|
|
8
|
+
import { a as serializeHydrationRenderData, c as createRenderContext, f as withRenderContextAsync, i as renderToStringSync, o as StreamSink, r as renderSSRRouteAppToSink, s as StringSink, u as throwSSRDataMissing } from "./render-resolved-BC0W7_5b.js";
|
|
9
|
+
//#region src/ssr/route-policy-resolution.ts
|
|
10
|
+
const MAX_SSR_REDIRECTS = 20;
|
|
11
|
+
function getRouteRequestResultSync(result) {
|
|
12
|
+
if (isPromiseLike(result)) throwSSRDataMissing();
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
function buildDeniedRoute(route, status) {
|
|
16
|
+
return {
|
|
17
|
+
...route,
|
|
18
|
+
handler: () => ({
|
|
19
|
+
type: "div",
|
|
20
|
+
props: { "data-route-denied": String(status) },
|
|
21
|
+
children: [String(status)]
|
|
22
|
+
})
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function resolvePolicyAwareSSRRoute(opts, routeTable) {
|
|
26
|
+
const manifest = opts.registry.manifest;
|
|
27
|
+
let href = opts.url;
|
|
28
|
+
const visited = /* @__PURE__ */ new Set();
|
|
29
|
+
for (let redirects = 0; redirects <= MAX_SSR_REDIRECTS; redirects += 1) {
|
|
30
|
+
const logicalHref = removeRouteBasePath(href, normalizeRouteBasePath(manifest.basePath));
|
|
31
|
+
if (logicalHref === void 0) throw new Error(`SSR: no route found for url: ${href}`);
|
|
32
|
+
const logicalUrl = new URL(logicalHref, "http://localhost");
|
|
33
|
+
const matched = _resolveRouteMatchFromRoutes(logicalUrl.pathname, routeTable);
|
|
34
|
+
if (!matched) throw new Error(`SSR: no route found for url: ${href}`);
|
|
35
|
+
const resolved = getRouteRequestResultSync(resolveRouteRequest(href, {
|
|
36
|
+
registry: opts.registry,
|
|
37
|
+
mode: "ssr",
|
|
38
|
+
auth: opts.auth,
|
|
39
|
+
authContext: opts.authContext,
|
|
40
|
+
request: opts.request,
|
|
41
|
+
signal: opts.signal
|
|
42
|
+
}));
|
|
43
|
+
if (resolved === null) throw new Error(`SSR: no route found for url: ${href}`);
|
|
44
|
+
if (resolved.kind === "redirect") {
|
|
45
|
+
const redirectTarget = new URL(resolved.to, "http://localhost");
|
|
46
|
+
const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;
|
|
47
|
+
if (redirectHref === href || visited.has(redirectHref)) throw new Error(`[Askr] SSR redirect cycle detected at ${redirectHref}.`);
|
|
48
|
+
if (redirects >= MAX_SSR_REDIRECTS) throw new Error(`[Askr] SSR redirect limit exceeded (${MAX_SSR_REDIRECTS}).`);
|
|
49
|
+
visited.add(redirectHref);
|
|
50
|
+
href = redirectHref;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (resolved.kind === "deny") return {
|
|
54
|
+
url: href,
|
|
55
|
+
route: buildDeniedRoute(matched.route, resolved.status),
|
|
56
|
+
params: matched.params
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
url: href,
|
|
60
|
+
route: {
|
|
61
|
+
...matched.route,
|
|
62
|
+
handler: resolvedRouteHasLoader(resolved) ? resolved.handler : matched.route.handler
|
|
63
|
+
},
|
|
64
|
+
params: matched.params,
|
|
65
|
+
authContext: getRouteRenderContext(resolved)?.auth
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`[Askr] SSR redirect limit exceeded (${MAX_SSR_REDIRECTS}).`);
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/ssr/route-render.ts
|
|
72
|
+
/** Resolve a URL against a route registry for SSR, applying auth/policies before render. */
|
|
73
|
+
async function resolveRequest(opts) {
|
|
74
|
+
if (!opts?.registry) throw new TypeError("resolveRequest requires opts.registry.");
|
|
75
|
+
const { url, auth, authContext, request, signal } = opts;
|
|
76
|
+
return await resolveRouteRequest(url, {
|
|
77
|
+
registry: opts.registry,
|
|
78
|
+
mode: "ssr",
|
|
79
|
+
auth,
|
|
80
|
+
authContext,
|
|
81
|
+
request,
|
|
82
|
+
signal
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function resolveSSRRouteSource(source) {
|
|
86
|
+
if (source.registry.routes.length === 0) throw new Error("SSR requires a non-empty route registry.");
|
|
87
|
+
return source.registry.routes.map((route) => ({
|
|
88
|
+
...route,
|
|
89
|
+
path: route.path,
|
|
90
|
+
handler: route.handler,
|
|
91
|
+
namespace: route.namespace
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
function resolveSSRRouteRender(opts) {
|
|
95
|
+
const { seed = 12345, data, document, styleRegistrationValidation = "warn" } = opts;
|
|
96
|
+
const cspNonce = validateCspNonce(opts.cspNonce);
|
|
97
|
+
const routeTable = resolveSSRRouteSource(opts);
|
|
98
|
+
const resolvedRoute = resolvePolicyAwareSSRRoute(opts, routeTable);
|
|
99
|
+
const requestUrl = new URL(resolvedRoute.url, "http://localhost");
|
|
100
|
+
const ctx = createRenderContext(seed, {
|
|
101
|
+
url: resolvedRoute.url,
|
|
102
|
+
basePath: opts.registry.manifest.basePath,
|
|
103
|
+
data,
|
|
104
|
+
params: resolvedRoute.params,
|
|
105
|
+
routes: routeTable,
|
|
106
|
+
routeAuth: opts.auth ?? opts.registry.manifest.auth,
|
|
107
|
+
signal: opts.signal ?? opts.request?.signal,
|
|
108
|
+
dataRuntime: opts.dataRuntime,
|
|
109
|
+
queryPrefetch: opts.queryPrefetch,
|
|
110
|
+
authContext: resolvedRoute.authContext,
|
|
111
|
+
envelope: opts.envelope,
|
|
112
|
+
cspNonce
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
url: resolvedRoute.url,
|
|
116
|
+
requestUrl,
|
|
117
|
+
route: resolvedRoute.route,
|
|
118
|
+
params: resolvedRoute.params,
|
|
119
|
+
seed,
|
|
120
|
+
data,
|
|
121
|
+
ctx,
|
|
122
|
+
document,
|
|
123
|
+
cspNonce,
|
|
124
|
+
styleRegistrationValidation
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function buildDocumentRenderArgs(resolved, appHtml) {
|
|
128
|
+
return {
|
|
129
|
+
appHtml,
|
|
130
|
+
context: {
|
|
131
|
+
mode: "ssr",
|
|
132
|
+
url: resolved.url,
|
|
133
|
+
pathname: resolved.requestUrl.pathname,
|
|
134
|
+
search: resolved.requestUrl.search,
|
|
135
|
+
hash: resolved.requestUrl.hash,
|
|
136
|
+
params: resolved.params,
|
|
137
|
+
data: resolved.data,
|
|
138
|
+
seed: resolved.seed,
|
|
139
|
+
route: {
|
|
140
|
+
path: resolved.route.path,
|
|
141
|
+
namespace: resolved.route.namespace
|
|
142
|
+
},
|
|
143
|
+
cspNonce: resolved.cspNonce,
|
|
144
|
+
styles: Array.from(resolved.ctx.ssrStyles.values())
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function renderResolvedRouteAppToSink(resolved, sink, host) {
|
|
149
|
+
host.renderAppToSink({
|
|
150
|
+
route: resolved.route,
|
|
151
|
+
params: resolved.params,
|
|
152
|
+
data: resolved.data,
|
|
153
|
+
ctx: resolved.ctx,
|
|
154
|
+
sink
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function renderToSinkInternal(opts, host) {
|
|
158
|
+
const { sink, ...renderOptions } = opts;
|
|
159
|
+
return withTelemetry(renderOptions.telemetry?.ssrRender, {}, () => {
|
|
160
|
+
const resolved = resolveSSRRouteRender(renderOptions);
|
|
161
|
+
if (!resolved.document) {
|
|
162
|
+
renderResolvedRouteAppToSink(resolved, sink, host);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const appSink = new StringSink();
|
|
166
|
+
renderResolvedRouteAppToSink(resolved, appSink, host);
|
|
167
|
+
appSink.end();
|
|
168
|
+
sink.write(renderDocument(resolved.document, buildDocumentRenderArgs(resolved, appSink.toString()), "renderToString()/renderToStream()", resolved.styleRegistrationValidation));
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function renderRouteToString(opts, host) {
|
|
172
|
+
const sink = new StringSink();
|
|
173
|
+
renderToSinkInternal({
|
|
174
|
+
...opts,
|
|
175
|
+
sink
|
|
176
|
+
}, host);
|
|
177
|
+
sink.end();
|
|
178
|
+
return sink.toString();
|
|
179
|
+
}
|
|
180
|
+
function renderRouteToStream(opts, host) {
|
|
181
|
+
const sink = new StreamSink(opts.onChunk, opts.onComplete);
|
|
182
|
+
renderToSinkInternal({
|
|
183
|
+
...opts,
|
|
184
|
+
sink
|
|
185
|
+
}, host);
|
|
186
|
+
sink.end();
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/ssr/route-request-render.ts
|
|
190
|
+
function abortError() {
|
|
191
|
+
const error = /* @__PURE__ */ new Error("Deferred route rendering was aborted.");
|
|
192
|
+
error.name = "AbortError";
|
|
193
|
+
return error;
|
|
194
|
+
}
|
|
195
|
+
function settleWithSignal(promise, signal) {
|
|
196
|
+
if (signal.aborted) return Promise.reject(abortError());
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
const abort = () => reject(abortError());
|
|
199
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
200
|
+
promise.then((value) => {
|
|
201
|
+
signal.removeEventListener("abort", abort);
|
|
202
|
+
resolve(value);
|
|
203
|
+
}, (error) => {
|
|
204
|
+
signal.removeEventListener("abort", abort);
|
|
205
|
+
reject(error);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function stripHydrationPayload(html) {
|
|
210
|
+
return html.replace(/<script type="application\/json" data-askr-render-data="true">[\s\S]*?<\/script>$/, "");
|
|
211
|
+
}
|
|
212
|
+
function renderBoundary(boundary, state, payload, seed, data, cspNonce, authContext) {
|
|
213
|
+
const styles = [];
|
|
214
|
+
return {
|
|
215
|
+
html: stripHydrationPayload(renderToStringSync(() => state === "fulfilled" ? boundary.fulfilled(payload) : boundary.rejected(payload), {}, {
|
|
216
|
+
seed,
|
|
217
|
+
envelope: data ?? void 0,
|
|
218
|
+
cspNonce,
|
|
219
|
+
authContext,
|
|
220
|
+
onContext: (context) => styles.push(...context.ssrStyles.values())
|
|
221
|
+
})),
|
|
222
|
+
styles
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function escapeHtmlAttribute(value) {
|
|
226
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
227
|
+
}
|
|
228
|
+
function escapeStyleRawText(value) {
|
|
229
|
+
return value.replace(/<\/style/gi, "<\\/style");
|
|
230
|
+
}
|
|
231
|
+
function patchChunk(id, html, styles, cspNonce) {
|
|
232
|
+
const selector = `askr-resolve[data-askr-deferred=${JSON.stringify(id)}]`;
|
|
233
|
+
return `${styles.length === 0 ? "" : `<style data-askr-style-registry="true" data-askr-deferred-style="true"${cspNonce ? ` nonce="${escapeHtmlAttribute(cspNonce)}"` : ""}>${escapeStyleRawText(styles.map((style) => style.cssText).join("\n"))}\n</style>`}<template data-askr-deferred-patch="${escapeHtmlAttribute(id)}">${html}</template><script${cspNonce ? ` nonce="${escapeHtmlAttribute(cspNonce)}"` : ""} data-askr-deferred-apply="${escapeHtmlAttribute(id)}">(function(s){var t=s.previousElementSibling;var c=t&&t.previousElementSibling;var r=document.querySelector('style[data-askr-style-registry="true"]:not([data-askr-deferred-style])');if(c&&c.tagName==='STYLE'&&r&&r!==c){r.append(c.textContent||'');c.remove();}else if(c&&c.tagName==='STYLE'){c.removeAttribute('data-askr-deferred-style');}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>`;
|
|
234
|
+
}
|
|
235
|
+
function createDeferredRenderStream(initialHtml, boundaries, signal, seed, data, runtime, cspNonce, authContext) {
|
|
236
|
+
const encoder = new TextEncoder();
|
|
237
|
+
const local = new AbortController();
|
|
238
|
+
let cancelled = false;
|
|
239
|
+
let closed = false;
|
|
240
|
+
let initialPending = true;
|
|
241
|
+
let boundaryIndex = 0;
|
|
242
|
+
let controllerRef;
|
|
243
|
+
const cleanup = () => signal.removeEventListener("abort", abort);
|
|
244
|
+
const close = (controller) => {
|
|
245
|
+
if (closed) return;
|
|
246
|
+
closed = true;
|
|
247
|
+
cleanup();
|
|
248
|
+
controller.close();
|
|
249
|
+
};
|
|
250
|
+
const abort = () => {
|
|
251
|
+
local.abort(signal.reason);
|
|
252
|
+
if (controllerRef) close(controllerRef);
|
|
253
|
+
};
|
|
254
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
255
|
+
return new ReadableStream({
|
|
256
|
+
start(controller) {
|
|
257
|
+
controllerRef = controller;
|
|
258
|
+
},
|
|
259
|
+
async pull(controller) {
|
|
260
|
+
if (cancelled || closed) return;
|
|
261
|
+
if (initialPending) {
|
|
262
|
+
initialPending = false;
|
|
263
|
+
controller.enqueue(encoder.encode(stripHydrationPayload(initialHtml)));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const boundary = boundaries[boundaryIndex];
|
|
267
|
+
if (boundary) {
|
|
268
|
+
boundaryIndex += 1;
|
|
269
|
+
try {
|
|
270
|
+
const value = await settleWithSignal(boundary.promise, local.signal);
|
|
271
|
+
if (!cancelled && !closed) {
|
|
272
|
+
const rendered = renderBoundary(boundary, "fulfilled", value, seed, data, cspNonce, authContext);
|
|
273
|
+
controller.enqueue(encoder.encode(patchChunk(boundary.id, rendered.html, rendered.styles, cspNonce)));
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
if (local.signal.aborted) {
|
|
277
|
+
if (!cancelled && !closed) close(controller);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (!cancelled && !closed) {
|
|
281
|
+
const rendered = renderBoundary(boundary, "rejected", error, seed, data, cspNonce, authContext);
|
|
282
|
+
controller.enqueue(encoder.encode(patchChunk(boundary.id, rendered.html, rendered.styles, cspNonce)));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const hydration = serializeHydrationRenderData(data ?? void 0, runtime);
|
|
288
|
+
if (hydration) controller.enqueue(encoder.encode(hydration));
|
|
289
|
+
close(controller);
|
|
290
|
+
},
|
|
291
|
+
cancel(reason) {
|
|
292
|
+
cancelled = true;
|
|
293
|
+
local.abort(reason);
|
|
294
|
+
cleanup();
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/** Resolve and render a route request for SSR, streaming the body when possible. */
|
|
299
|
+
async function renderRouteRequest(options) {
|
|
300
|
+
return renderRouteRequestInternal(options);
|
|
301
|
+
}
|
|
302
|
+
/** Resolve and render a route request for SSR, always fully buffering the HTML. */
|
|
303
|
+
async function renderRouteRequestToString(options) {
|
|
304
|
+
const result = await renderRouteRequestInternal(options);
|
|
305
|
+
if (result.kind !== "render" || !result.stream) return result;
|
|
306
|
+
return {
|
|
307
|
+
...result,
|
|
308
|
+
html: await new Response(result.stream).text(),
|
|
309
|
+
stream: void 0
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
async function renderRouteRequestInternal(options) {
|
|
313
|
+
if (!options?.registry) throw new TypeError("renderRouteRequest requires options.registry.");
|
|
314
|
+
const cspNonce = validateCspNonce(options.cspNonce);
|
|
315
|
+
const manifest = options.registry.manifest;
|
|
316
|
+
const signal = options.signal ?? options.request?.signal ?? new AbortController().signal;
|
|
317
|
+
const request = options.request ?? new Request(new URL(options.url, "http://localhost"));
|
|
318
|
+
const runtime = options.dataRuntime ?? createDataRuntime();
|
|
319
|
+
const prefetch = options.queryPrefetch ?? createQueryPrefetchContext({
|
|
320
|
+
runtime,
|
|
321
|
+
registry: options.queryRegistry,
|
|
322
|
+
request,
|
|
323
|
+
signal,
|
|
324
|
+
mode: "ssr",
|
|
325
|
+
telemetry: options.telemetry
|
|
326
|
+
});
|
|
327
|
+
const context = createRenderContext(options.seed, {
|
|
328
|
+
url: options.url,
|
|
329
|
+
basePath: manifest.basePath,
|
|
330
|
+
routes: options.registry.routes,
|
|
331
|
+
routeAuth: options.auth ?? manifest.auth,
|
|
332
|
+
data: options.data,
|
|
333
|
+
framework: options.framework,
|
|
334
|
+
signal,
|
|
335
|
+
dataRuntime: runtime,
|
|
336
|
+
queryPrefetch: prefetch,
|
|
337
|
+
mode: "ssr",
|
|
338
|
+
cspNonce
|
|
339
|
+
});
|
|
340
|
+
return withTelemetry(options.telemetry?.ssrRender, {}, () => withRenderContextAsync(context, async () => {
|
|
341
|
+
const resolved = await resolveRouteRequest(options.url, {
|
|
342
|
+
registry: options.registry,
|
|
343
|
+
mode: "ssr",
|
|
344
|
+
auth: options.auth ?? manifest.auth,
|
|
345
|
+
authContext: options.authContext,
|
|
346
|
+
request,
|
|
347
|
+
signal,
|
|
348
|
+
telemetry: options.telemetry
|
|
349
|
+
});
|
|
350
|
+
if (!resolved) return { kind: "no-match" };
|
|
351
|
+
if (resolved.kind !== "render") return resolved;
|
|
352
|
+
context.params = resolved.params;
|
|
353
|
+
const sink = new StringSink();
|
|
354
|
+
renderSSRRouteAppToSink({
|
|
355
|
+
route: {
|
|
356
|
+
path: "",
|
|
357
|
+
handler: resolved.handler
|
|
358
|
+
},
|
|
359
|
+
params: resolved.params,
|
|
360
|
+
data: options.data,
|
|
361
|
+
ctx: context,
|
|
362
|
+
sink
|
|
363
|
+
});
|
|
364
|
+
sink.end();
|
|
365
|
+
const html = sink.toString();
|
|
366
|
+
const boundaries = [...context.deferredBoundaries];
|
|
367
|
+
const result = {
|
|
368
|
+
kind: "render",
|
|
369
|
+
html,
|
|
370
|
+
styles: Array.from(context.ssrStyles.values()),
|
|
371
|
+
...boundaries.length > 0 ? { stream: createDeferredRenderStream(html, boundaries, signal, options.seed, context.hydrationData, runtime, cspNonce, context.authContext) } : {},
|
|
372
|
+
params: resolved.params
|
|
373
|
+
};
|
|
374
|
+
if (resolved.record) Object.defineProperty(result, "record", {
|
|
375
|
+
value: resolved.record,
|
|
376
|
+
enumerable: false
|
|
377
|
+
});
|
|
378
|
+
return result;
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region src/ssr/index-internal.ts
|
|
383
|
+
const routeRenderHost = { renderAppToSink: renderSSRRouteAppToSink };
|
|
384
|
+
function renderToString(arg) {
|
|
385
|
+
if (typeof arg === "function") return renderToStringSync(arg);
|
|
386
|
+
return renderRouteToString(arg, routeRenderHost);
|
|
387
|
+
}
|
|
388
|
+
/** Stream a route request's rendered HTML to the response sink described by `opts`. */
|
|
389
|
+
function renderToStream(opts) {
|
|
390
|
+
renderRouteToStream(opts, routeRenderHost);
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
393
|
+
export { resolveRequest as a, renderRouteRequestToString as i, renderToString as n, renderRouteRequest as r, renderToStream as t };
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { P as isPromiseLike } from "./instance-VAFYVJOa.js";
|
|
1
2
|
//#region src/common/ssr.ts
|
|
3
|
+
/**
|
|
4
|
+
* Common call contracts: SSR types
|
|
5
|
+
*/
|
|
2
6
|
function escapedStyleRawText(value) {
|
|
3
7
|
return value.replace(/<\//gi, "<\\/");
|
|
4
8
|
}
|
|
@@ -14,7 +18,7 @@ function validateDocumentStyleRegistrations(html, styles, apiName, validation) {
|
|
|
14
18
|
function renderDocument(document, args, apiName, styleRegistrationValidation = "warn") {
|
|
15
19
|
const html = document(args);
|
|
16
20
|
if (typeof html !== "string") {
|
|
17
|
-
const received =
|
|
21
|
+
const received = isPromiseLike(html) ? "a Promise-like value" : html === null ? "null" : Array.isArray(html) ? "an array" : typeof html;
|
|
18
22
|
throw new Error(`${apiName} document() must synchronously return a string; received ${received}.`);
|
|
19
23
|
}
|
|
20
24
|
validateDocumentStyleRegistrations(html, args.context.styles, apiName, styleRegistrationValidation);
|