@askrjs/askr 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +874 -0
  2. package/README.md +61 -29
  3. package/capabilities.json +19 -9
  4. package/dist/actions/index.js +56 -12
  5. package/dist/benchmark.js +10 -15
  6. package/dist/boot/index.d.ts +3 -2
  7. package/dist/boot/index.js +1 -1
  8. package/dist/boot-DaoTYoCw.js +3989 -0
  9. package/dist/browser-activity-CB8e-V4f.js +220 -0
  10. package/dist/components/index.d.ts +8 -1
  11. package/dist/components/index.js +1 -43
  12. package/dist/{aria-hKjThHcp.js → compose-ref-DWzdbxsD.js} +41 -4
  13. package/dist/control/index.d.ts +2 -0
  14. package/dist/control/index.js +1 -1
  15. package/dist/control-nfpT2fbK.js +209 -0
  16. package/dist/core.d.ts +5 -9
  17. package/dist/csp-nonce-DwGw1A6Q.js +35 -0
  18. package/dist/data/index.js +3 -3
  19. package/dist/{data-2edW0jhF.js → data-BCwClJiF.js} +283 -94
  20. package/dist/data-runtime-C1bFAlba.js +242 -0
  21. package/dist/{deferred-CbgNhf67.js → deferred-bEEHHvic.js} +42 -20
  22. package/dist/domains/actions.d.ts +8 -3
  23. package/dist/domains/component.d.ts +1 -107
  24. package/dist/domains/context.d.ts +0 -15
  25. package/dist/domains/control.d.ts +36 -249
  26. package/dist/domains/data.d.ts +31 -13
  27. package/dist/domains/diagnostics.d.ts +1 -2
  28. package/dist/domains/lifecycle.d.ts +2 -5
  29. package/dist/domains/refs.d.ts +0 -2
  30. package/dist/domains/routing.d.ts +43 -8
  31. package/dist/domains/scheduler.d.ts +1 -76
  32. package/dist/domains/server.d.ts +5 -2
  33. package/dist/domains/state.d.ts +2 -5
  34. package/dist/domains/telemetry.d.ts +0 -2
  35. package/dist/elements.d.ts +214 -51
  36. package/dist/error-boundary-Bpwofwps.js +293 -0
  37. package/dist/foundations/icon/index.d.ts +0 -58
  38. package/dist/foundations/icon/index.js +2 -2
  39. package/dist/foundations/index.js +2 -1
  40. package/dist/foundations/interactions/index.js +1 -1
  41. package/dist/foundations/state/index.d.ts +3 -3
  42. package/dist/foundations/state/index.js +5 -3
  43. package/dist/foundations/structures/index.js +2 -1
  44. package/dist/foundations/utilities/index.d.ts +2 -0
  45. package/dist/foundations/utilities/index.js +1 -2
  46. package/dist/fx/index.d.ts +31 -58
  47. package/dist/fx/index.js +313 -195
  48. package/dist/hooks-BimKStr4.js +202 -0
  49. package/dist/index.d.ts +11 -74
  50. package/dist/index.js +8 -447
  51. package/dist/instance-BAnfmXOR.js +988 -0
  52. package/dist/{interactions-CTeACdT_.js → interactions-D2BoREb-.js} +3 -2
  53. package/dist/jsx-dev-runtime.d.ts +41 -18
  54. package/dist/jsx-dev-runtime.js +1 -6
  55. package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
  56. package/dist/jsx-runtime.js +1 -1
  57. package/dist/jsx.d.ts +61 -37
  58. package/dist/{manifest-D1P3UNWV.js → manifest-CW9gyNBY.js} +3 -3
  59. package/dist/{navigate-prC5V8XV.js → navigate-CP_0Zy5U.js} +454 -87
  60. package/dist/navigation.d.ts +12 -3
  61. package/dist/peer-types.d.ts +8 -0
  62. package/dist/portal-DuOKfnLa.js +349 -0
  63. package/dist/promise-Dz8iIHAm.js +16 -0
  64. package/dist/query-registry-WMCN0BJL.js +320 -0
  65. package/dist/readable-DFWOsTac.js +21 -0
  66. package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
  67. package/dist/render-sync-IC5lMFpq.js +1240 -0
  68. package/dist/resolution-Cx82-xjW.js +1244 -0
  69. package/dist/resource-CLjB7RBQ.js +377 -0
  70. package/dist/resources/index.d.ts +21 -11
  71. package/dist/resources/index.js +45 -244
  72. package/dist/router/index.d.ts +14 -5
  73. package/dist/router/index.js +22 -53
  74. package/dist/scope-CwpVSHVf.js +55 -0
  75. package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
  76. package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
  77. package/dist/ssg/index.d.ts +1 -2
  78. package/dist/ssg/index.js +5 -13
  79. package/dist/ssr/index.d.ts +26 -28
  80. package/dist/ssr/index.js +4 -4
  81. package/dist/{ssr-CiNm6Vzl.js → ssr-BJ8ug7zH.js} +133 -122
  82. package/dist/ssr-DoV-vJ-5.js +229 -0
  83. package/dist/state-DWO1KzkX.js +232 -0
  84. package/dist/structures-D4dDMqHX.js +231 -0
  85. package/dist/testing/index.d.ts +8 -4
  86. package/dist/testing/index.js +29 -34
  87. package/dist/url-CnbfpX0K.js +185 -0
  88. package/dist/utilities.d.ts +48 -2
  89. package/dist/verify-hydration-DpVaDnqz.js +152 -0
  90. package/package.json +19 -9
  91. package/dist/access-CEA2pOry.js +0 -1046
  92. package/dist/activity-oy2YFTFF.js +0 -201
  93. package/dist/boot-BMBMWUGI.js +0 -1084
  94. package/dist/capabilities-Bbf0mlp9.js +0 -42
  95. package/dist/compose-ref-v0qGlHoN.js +0 -26
  96. package/dist/control-BR_mrX__.js +0 -173
  97. package/dist/control-C-ApnFs1.js +0 -20
  98. package/dist/csp-nonce-BfFWcd0w.js +0 -173
  99. package/dist/data-runtime-CxoY98lX.js +0 -149
  100. package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
  101. package/dist/dom-renderer.d.ts +0 -77
  102. package/dist/domains/renderer.d.ts +0 -127
  103. package/dist/eager-control.d.ts +0 -8
  104. package/dist/effect-C3Zsc1k6.js +0 -292
  105. package/dist/for-state-BDykeOJL.js +0 -1753
  106. package/dist/instance-VAFYVJOa.js +0 -1027
  107. package/dist/notify-DxxuDPeY.js +0 -266
  108. package/dist/portal-w3oQ6oQU.js +0 -29
  109. package/dist/query-registry-LrYpQsDJ.js +0 -83
  110. package/dist/render-resolved-BC0W7_5b.js +0 -1123
  111. package/dist/renderer-CT0HeY58.js +0 -8623
  112. package/dist/resolution-3DskUkjG.js +0 -577
  113. package/dist/resource-18-sFRl9.js +0 -344
  114. package/dist/route-matching-CUzZ31gT.js +0 -551
  115. package/dist/router-internal-7nuNEHfG.js +0 -36
  116. package/dist/scope-Cq_iYbTR.js +0 -403
  117. package/dist/selector-BSPVkjKP.js +0 -311
  118. package/dist/ssr-EPjwJH5R.js +0 -29
  119. package/dist/state-BXJfudq1.js +0 -91
  120. package/dist/structures-CjEzfj4h.js +0 -654
  121. package/dist/timer-C7uqnJVt.js +0 -85
  122. package/dist/verify-hydration-ikaxotf7.js +0 -27
  123. package/dist/vnode-D24p6IW2.js +0 -7
@@ -1,1123 +0,0 @@
1
- import { v as logger } from "./access-CEA2pOry.js";
2
- import { P as isPromiseLike, S as markVNodeTreeWithContextFrame, T as cleanupComponent, g as getExecutionContextFrame, n as createComponentInstance, p as callWithContext, x as getVNodeContextFrame } from "./instance-VAFYVJOa.js";
3
- import { s as endComponentScope, u as enterRenderScopedComponent } from "./scope-Cq_iYbTR.js";
4
- import { t as __CONTROL_BOUNDARY__ } from "./control-C-ApnFs1.js";
5
- import { t as __ERROR_BOUNDARY__ } from "./vnode-D24p6IW2.js";
6
- import { i as isFragmentType, n as Fragment, t as ELEMENT_TYPE } from "./jsx-DRS-qjiN.js";
7
- import { _ as setControlBoundaryContextFrame, g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, o as rollbackForStateTransaction, r as commitForStateTransaction } from "./for-state-BDykeOJL.js";
8
- import { C as SSRDataMissingError, _ as isEmptyPageRenderEnvelope, f as stopRenderPhase$1, h as createPageRenderEnvelope, t as configureRenderContextProvider, u as startRenderPhase$1, v as isPageRenderEnvelope, x as withHydrationRenderUrl } from "./render-context-B7ohlh6t.js";
9
- import { a as booleanAttributeValue, c as normalizeStylePropertyName, i as sanitizeCssValue, l as getPublicAttributeName, o as isAriaAttribute, r as validateCspNonce, s as isSkippedProp, t as CspNonceScope } from "./csp-nonce-BfFWcd0w.js";
10
- import { r as isUnsafeUrlAttribute } from "./router-internal-7nuNEHfG.js";
11
- import { n as SSR_PORTAL_HOST, t as SSR_PORTAL_ANCHOR } from "./portal-w3oQ6oQU.js";
12
- import { t as createDataRuntime } from "./data-runtime-CxoY98lX.js";
13
- import { i as dehydrateDataRuntime } from "./query-registry-LrYpQsDJ.js";
14
- import { n as disposeRegisteredDefaultPortalScope, r as getDefaultPortalHost } from "./default-portal-runtime-DvMvWJVp.js";
15
- import { S as isDeferred, b as DEFERRED_BOUNDARY, h as getRouteHydrationMetadata, v as validateRouteHydrationData } from "./resolution-3DskUkjG.js";
16
- import { a as resolveRouteFromRoutes, c as removeRouteBasePath, s as normalizeRouteBasePath } from "./route-matching-CUzZ31gT.js";
17
- import { t as SSR_RENDER_DATA_ATTR } from "./ssr-EPjwJH5R.js";
18
- //#region src/ssr/escape.ts
19
- /**
20
- * HTML escaping utilities for SSR
21
- *
22
- * Centralizes text and attribute escaping to avoid duplication
23
- * between sync and streaming SSR renderers.
24
- */
25
- const VOID_ELEMENTS = /* @__PURE__ */ new Set([
26
- "area",
27
- "base",
28
- "br",
29
- "col",
30
- "embed",
31
- "hr",
32
- "img",
33
- "input",
34
- "link",
35
- "meta",
36
- "param",
37
- "source",
38
- "track",
39
- "wbr"
40
- ]);
41
- const escapeCache = /* @__PURE__ */ new Map();
42
- const MAX_CACHE_SIZE = 256;
43
- const STYLE_PROP_CACHE = /* @__PURE__ */ new Map();
44
- const MAX_STYLE_PROP_CACHE_SIZE = 512;
45
- function toKebabCached(prop) {
46
- const cached = STYLE_PROP_CACHE.get(prop);
47
- if (cached !== void 0) return cached;
48
- const kebab = normalizeStylePropertyName(prop);
49
- if (STYLE_PROP_CACHE.size < MAX_STYLE_PROP_CACHE_SIZE) STYLE_PROP_CACHE.set(prop, kebab);
50
- return kebab;
51
- }
52
- /**
53
- * Clear the escape cache. Call between SSR requests in long-running servers
54
- * to prevent memory buildup from unique strings.
55
- */
56
- function clearEscapeCache() {
57
- escapeCache.clear();
58
- }
59
- /**
60
- * Fast check if a string needs text escaping.
61
- * Used to skip the full escape call when not needed.
62
- */
63
- function needsEscapeText(text) {
64
- for (let i = 0; i < text.length; i++) {
65
- const ch = text.charCodeAt(i);
66
- if (ch === 38 || ch === 60 || ch === 62) return true;
67
- }
68
- return false;
69
- }
70
- /**
71
- * Fast check if a string needs attribute escaping.
72
- * Checks for & " ' < > characters.
73
- */
74
- function needsEscapeAttr(value) {
75
- for (let i = 0; i < value.length; i++) {
76
- const ch = value.charCodeAt(i);
77
- if (ch === 38 || ch === 34 || ch === 39 || ch === 60 || ch === 62) return true;
78
- }
79
- return false;
80
- }
81
- /**
82
- * Escape HTML special characters in text content
83
- * Fast-path: cache first, then scan-then-escape for new strings
84
- */
85
- function escapeText(text) {
86
- const useCache = text.length <= 64;
87
- if (useCache) {
88
- const cached = escapeCache.get(text);
89
- if (cached !== void 0) return cached;
90
- }
91
- if (!needsEscapeText(text)) {
92
- if (useCache && escapeCache.size < MAX_CACHE_SIZE) escapeCache.set(text, text);
93
- return text;
94
- }
95
- let escaped = "";
96
- let lastIndex = 0;
97
- for (let i = 0; i < text.length; i++) {
98
- const ch = text.charCodeAt(i);
99
- let entity;
100
- if (ch === 38) entity = "&amp;";
101
- else if (ch === 60) entity = "&lt;";
102
- else if (ch === 62) entity = "&gt;";
103
- else continue;
104
- escaped += text.slice(lastIndex, i) + entity;
105
- lastIndex = i + 1;
106
- }
107
- const result = escaped + text.slice(lastIndex);
108
- if (useCache && escapeCache.size < MAX_CACHE_SIZE) escapeCache.set(text, result);
109
- return result;
110
- }
111
- /**
112
- * Escape HTML special characters in attribute values.
113
- * Single-pass scan: returns the original string unchanged when no special
114
- * characters are found, so callers can drop separate needsEscapeAttr checks.
115
- */
116
- function escapeAttr(value) {
117
- let escaped = "";
118
- let lastIndex = 0;
119
- for (let i = 0; i < value.length; i++) {
120
- const ch = value.charCodeAt(i);
121
- let entity;
122
- if (ch === 38) entity = "&amp;";
123
- else if (ch === 34) entity = "&quot;";
124
- else if (ch === 39) entity = "&#x27;";
125
- else if (ch === 60) entity = "&lt;";
126
- else if (ch === 62) entity = "&gt;";
127
- else continue;
128
- escaped += value.slice(lastIndex, i) + entity;
129
- lastIndex = i + 1;
130
- }
131
- return lastIndex === 0 ? value : escaped + value.slice(lastIndex);
132
- }
133
- /**
134
- * Convert a style object to a CSS string, omitting unsafe values.
135
- * Optimized to avoid Object.entries allocation
136
- */
137
- function styleObjToCss(value) {
138
- if (!value || typeof value !== "object") return null;
139
- const styleObj = value;
140
- let result = "";
141
- for (const k in styleObj) {
142
- const v = styleObj[k];
143
- if (v === null || v === void 0 || v === false) continue;
144
- const prop = toKebabCached(k);
145
- const safeValue = sanitizeCssValue(String(v));
146
- if (safeValue) result += `${prop}:${safeValue};`;
147
- }
148
- return result || null;
149
- }
150
- //#endregion
151
- //#region src/ssr/context.ts
152
- /**
153
- * SSR Context Management
154
- *
155
- * Provides render-context storage for server-side rendering.
156
- * Node SSR lazily installs AsyncLocalStorage on first use; browser builds use
157
- * the fallback stack.
158
- */
159
- const FALLBACK_ASYNC_CONTEXT_ERROR = "[Askr] async SSR render context fallback is unsupported in this environment. Use synchronous SSR rendering or a runtime with AsyncLocalStorage.";
160
- let renderContextAccessor = null;
161
- let renderContextAccessorInitialized = false;
162
- let asyncRenderContextAccessor = null;
163
- let fallbackStack = null;
164
- function ensureRenderContextAccessor() {
165
- if (renderContextAccessorInitialized) return;
166
- renderContextAccessorInitialized = true;
167
- if (typeof process === "undefined" || !process.versions?.node) return;
168
- try {
169
- const asyncHooks = new Function("return require(" + JSON.stringify("async_hooks") + ");")();
170
- if (asyncHooks.AsyncLocalStorage) {
171
- const asyncLocalStorage = new asyncHooks.AsyncLocalStorage();
172
- renderContextAccessor = {
173
- getStore() {
174
- return asyncLocalStorage.getStore();
175
- },
176
- run(store, fn) {
177
- return asyncLocalStorage.run(store, fn);
178
- }
179
- };
180
- }
181
- } catch {}
182
- }
183
- /** Build a fresh SSR render context (data cache, routes, seed) for a render pass. */
184
- function createRenderContext(seed = 12345, opts = {}) {
185
- clearEscapeCache();
186
- const queryCache = /* @__PURE__ */ new Map();
187
- const envelope = opts.envelope ?? createPageRenderEnvelope({
188
- resources: opts.data,
189
- framework: opts.framework
190
- });
191
- const hydrationEnvelope = opts.url ? withHydrationRenderUrl(envelope, opts.url) : envelope;
192
- return {
193
- url: opts.url ?? "",
194
- seed,
195
- data: opts.data,
196
- params: opts.params,
197
- routes: opts.routes,
198
- routeAuth: opts.routeAuth,
199
- authContext: opts.authContext,
200
- basePath: opts.basePath,
201
- signal: opts.signal,
202
- dataRuntime: opts.dataRuntime ?? createDataRuntime({ queryCache }),
203
- mode: opts.mode ?? "ssr",
204
- queryPrefetch: opts.queryPrefetch,
205
- queryCache,
206
- resourceDataProvided: opts.envelope ? Object.keys(envelope.resources).some((key) => key.startsWith("r:")) : opts.data !== void 0,
207
- ssrCleanupFns: [],
208
- keyCounter: 0,
209
- renderData: envelope,
210
- hydrationData: hydrationEnvelope,
211
- deferredBoundaries: [],
212
- ssrStyles: /* @__PURE__ */ new Map(),
213
- ssrPortals: {
214
- slots: /* @__PURE__ */ new Map(),
215
- nextHostId: 0
216
- },
217
- cspNonce: opts.cspNonce
218
- };
219
- }
220
- /**
221
- * Run a function with the given render context.
222
- * Concurrency-safe in Node.js via AsyncLocalStorage.
223
- */
224
- function withRenderContext(ctx, fn) {
225
- ensureRenderContextProvider();
226
- ensureRenderContextAccessor();
227
- if (renderContextAccessor) return renderContextAccessor.run(ctx, fn);
228
- const prev = fallbackStack;
229
- fallbackStack = ctx;
230
- try {
231
- const result = fn();
232
- if (isPromiseLike(result)) throw new Error(FALLBACK_ASYNC_CONTEXT_ERROR);
233
- return result;
234
- } finally {
235
- fallbackStack = prev;
236
- }
237
- }
238
- async function getAsyncRenderContextAccessor() {
239
- ensureRenderContextAccessor();
240
- if (renderContextAccessor) return renderContextAccessor;
241
- if (typeof process === "undefined" || !process.versions?.node) return null;
242
- asyncRenderContextAccessor ??= (async () => {
243
- try {
244
- const module = await import(
245
- /* @vite-ignore */
246
- "node:async_hooks"
247
- );
248
- if (!module.AsyncLocalStorage) return null;
249
- const storage = new module.AsyncLocalStorage();
250
- renderContextAccessor = {
251
- getStore: () => storage.getStore(),
252
- run: (store, fn) => storage.run(store, fn)
253
- };
254
- return renderContextAccessor;
255
- } catch {
256
- return null;
257
- }
258
- })();
259
- return asyncRenderContextAccessor;
260
- }
261
- /** Run `fn` with `ctx` as the active SSR render context, using async-local storage. */
262
- async function withRenderContextAsync(ctx, fn) {
263
- ensureRenderContextProvider();
264
- const accessor = await getAsyncRenderContextAccessor();
265
- if (accessor) return accessor.run(ctx, () => Promise.resolve(fn()));
266
- throw new Error(FALLBACK_ASYNC_CONTEXT_ERROR);
267
- }
268
- /**
269
- * Get the current render context.
270
- * Returns null if not inside a render.
271
- */
272
- function getRenderContext() {
273
- ensureRenderContextAccessor();
274
- if (renderContextAccessor) return renderContextAccessor.getStore() ?? null;
275
- return fallbackStack;
276
- }
277
- let renderContextProvided = false;
278
- /**
279
- * Publish this module's render-context accessor to `common/render-context`.
280
- *
281
- * Runtime code (resources, portals, deferred routes) asks that module for the
282
- * active SSR context, and it cannot import SSR. Installing the accessor at
283
- * module scope meant the answer depended on whether anything had imported this
284
- * module; entering a render context composes it instead. Consumers only ever
285
- * observe a context from inside one, so this covers every reader.
286
- */
287
- function ensureRenderContextProvider() {
288
- if (renderContextProvided) return;
289
- renderContextProvided = true;
290
- configureRenderContextProvider({ getRenderContext });
291
- }
292
- /**
293
- * Centralized SSR enforcement helper — throws a consistent error when async
294
- * data is encountered during synchronous SSR.
295
- */
296
- function throwSSRDataMissing() {
297
- throw new SSRDataMissingError();
298
- }
299
- //#endregion
300
- //#region src/ssr/sink.ts
301
- var StringSink = class StringSink {
302
- constructor() {
303
- this.chunks = [];
304
- this.bufferChunks = [];
305
- this.bufferLen = 0;
306
- }
307
- static {
308
- this.FLUSH_THRESHOLD = 32768;
309
- }
310
- write(html) {
311
- if (!html) return;
312
- this.bufferChunks.push(html);
313
- this.bufferLen += html.length;
314
- if (this.bufferLen >= StringSink.FLUSH_THRESHOLD) {
315
- this.chunks.push(this.bufferChunks.join(""));
316
- this.bufferChunks.length = 0;
317
- this.bufferLen = 0;
318
- }
319
- }
320
- write2(a, b) {
321
- if (a) {
322
- this.bufferChunks.push(a);
323
- this.bufferLen += a.length;
324
- }
325
- if (b) {
326
- this.bufferChunks.push(b);
327
- this.bufferLen += b.length;
328
- }
329
- if (this.bufferLen >= StringSink.FLUSH_THRESHOLD) {
330
- this.chunks.push(this.bufferChunks.join(""));
331
- this.bufferChunks.length = 0;
332
- this.bufferLen = 0;
333
- }
334
- }
335
- write3(a, b, c) {
336
- if (a) {
337
- this.bufferChunks.push(a);
338
- this.bufferLen += a.length;
339
- }
340
- if (b) {
341
- this.bufferChunks.push(b);
342
- this.bufferLen += b.length;
343
- }
344
- if (c) {
345
- this.bufferChunks.push(c);
346
- this.bufferLen += c.length;
347
- }
348
- if (this.bufferLen >= StringSink.FLUSH_THRESHOLD) {
349
- this.chunks.push(this.bufferChunks.join(""));
350
- this.bufferChunks.length = 0;
351
- this.bufferLen = 0;
352
- }
353
- }
354
- end() {
355
- if (this.bufferLen) {
356
- this.chunks.push(this.bufferChunks.join(""));
357
- this.bufferChunks.length = 0;
358
- this.bufferLen = 0;
359
- }
360
- }
361
- toString() {
362
- if (this.bufferLen) {
363
- this.chunks.push(this.bufferChunks.join(""));
364
- this.bufferChunks.length = 0;
365
- this.bufferLen = 0;
366
- }
367
- return this.chunks.join("");
368
- }
369
- };
370
- var StreamSink = class {
371
- constructor(onChunk, onComplete) {
372
- this.onChunk = onChunk;
373
- this.onComplete = onComplete;
374
- }
375
- write(html) {
376
- if (html) this.onChunk(html);
377
- }
378
- end() {
379
- this.onComplete();
380
- }
381
- };
382
- //#endregion
383
- //#region src/ssr/component-runtime.ts
384
- function executeComponentSync(component, props, ctx, ownerFrame = null) {
385
- try {
386
- const temp = createComponentInstance("ssr-temp", component, props || {}, null, null);
387
- temp.ssr = true;
388
- temp.ownerFrame = ownerFrame;
389
- temp.portalScope = temp;
390
- ctx.ssrCleanupFns.push(() => {
391
- let cleanupError = null;
392
- try {
393
- cleanupComponent(temp);
394
- } catch (error) {
395
- cleanupError = error;
396
- }
397
- try {
398
- disposeRegisteredDefaultPortalScope(temp);
399
- } catch (error) {
400
- if (cleanupError) throw new AggregateError([cleanupError, error], "SSR temporary owner cleanup failed");
401
- throw error;
402
- }
403
- if (cleanupError) throw cleanupError;
404
- });
405
- const previous = enterRenderScopedComponent(temp, 0);
406
- try {
407
- const executionFrame = getExecutionContextFrame(ownerFrame);
408
- const result = callWithContext(executionFrame, component, props || {}, { ssr: ctx });
409
- if (isPromiseLike(result)) throwSSRDataMissing();
410
- if (typeof result === "string" || typeof result === "number" || typeof result === "boolean" || result === null || result === void 0) {
411
- const inner = result === null || result === void 0 || result === false ? "" : String(result);
412
- return {
413
- $$typeof: ELEMENT_TYPE,
414
- type: Fragment,
415
- props: { children: inner ? [inner] : [] }
416
- };
417
- }
418
- return markVNodeTreeWithContextFrame(result, ownerFrame);
419
- } finally {
420
- endComponentScope(previous);
421
- }
422
- } finally {}
423
- }
424
- function disposeSSRTemporaryOwners(ctx) {
425
- const cleanupFns = ctx.ssrCleanupFns;
426
- ctx.ssrCleanupFns = [];
427
- const cleanupErrors = [];
428
- for (let index = cleanupFns.length - 1; index >= 0; index -= 1) try {
429
- cleanupFns[index]();
430
- } catch (error) {
431
- cleanupErrors.push(error);
432
- }
433
- if (cleanupErrors.length === 1) throw cleanupErrors[0];
434
- if (cleanupErrors.length > 1) throw new AggregateError(cleanupErrors, "SSR temporary owner cleanup failed");
435
- }
436
- function wrapWithDefaultPortal(out) {
437
- if (isPromiseLike(out)) throwSSRDataMissing();
438
- const portalVNode = {
439
- $$typeof: ELEMENT_TYPE,
440
- type: getDefaultPortalHost(),
441
- props: { __askrAutoDefaultPortal: true },
442
- key: "__default_portal"
443
- };
444
- if (out == null) return {
445
- $$typeof: ELEMENT_TYPE,
446
- type: Fragment,
447
- props: { children: [portalVNode] }
448
- };
449
- return {
450
- $$typeof: ELEMENT_TYPE,
451
- type: Fragment,
452
- props: { children: [out, portalVNode] }
453
- };
454
- }
455
- function renderSyncComponentRoot(component, props, ctx) {
456
- const wrapped = (p, c) => wrapWithDefaultPortal(component(p ?? {}, c));
457
- return executeComponentSync(wrapped, props || {}, ctx);
458
- }
459
- //#endregion
460
- //#region src/ssr/boundaries.ts
461
- function normalizeRenderableChildren(value) {
462
- if (value === null || value === void 0 || value === false) return [];
463
- return Array.isArray(value) ? value : [value];
464
- }
465
- function getRenderableChildren(node) {
466
- if (Array.isArray(node.children)) return node.children;
467
- if (Array.isArray(node.props?.children)) return node.props.children;
468
- if (node.props?.children !== void 0 && node.props.children !== null && node.props.children !== false) return [node.props.children];
469
- }
470
- function getErrorBoundaryState(node) {
471
- return node.__instance?.errorBoundaryState ?? null;
472
- }
473
- function resetErrorBoundaryState(node) {
474
- const state = node.__instance?.errorBoundaryState;
475
- if (state) {
476
- state.error = null;
477
- state.notified = false;
478
- }
479
- }
480
- function createErrorBoundaryReset(node) {
481
- return () => resetErrorBoundaryState(node);
482
- }
483
- function createDefaultErrorBoundaryFallbackVNode(error) {
484
- const message = error instanceof Error ? error.stack || error.message || error.name : typeof error === "string" ? error : (() => {
485
- try {
486
- return JSON.stringify(error);
487
- } catch {
488
- return String(error);
489
- }
490
- })();
491
- return {
492
- $$typeof: ELEMENT_TYPE,
493
- type: "div",
494
- props: {
495
- role: "alert",
496
- "data-askr-error-boundary": "true",
497
- style: {
498
- boxSizing: "border-box",
499
- padding: "1rem",
500
- border: "1px solid currentColor",
501
- borderRadius: "0.75rem",
502
- display: "grid",
503
- gap: "0.75rem",
504
- maxWidth: "100%"
505
- },
506
- children: [
507
- {
508
- $$typeof: ELEMENT_TYPE,
509
- type: "strong",
510
- props: { children: ["Something went wrong while rendering this view."] }
511
- },
512
- {
513
- $$typeof: ELEMENT_TYPE,
514
- type: "p",
515
- props: {
516
- style: { margin: "0" },
517
- children: ["The app recovered into a visible fallback so the error is not hidden in the console."]
518
- }
519
- },
520
- {
521
- $$typeof: ELEMENT_TYPE,
522
- type: "details",
523
- props: {
524
- open: false,
525
- children: [{
526
- $$typeof: ELEMENT_TYPE,
527
- type: "summary",
528
- props: { children: ["Error details"] }
529
- }, {
530
- $$typeof: ELEMENT_TYPE,
531
- type: "pre",
532
- props: {
533
- style: {
534
- margin: "0",
535
- whiteSpace: "pre-wrap",
536
- wordBreak: "break-word"
537
- },
538
- children: [message]
539
- }
540
- }]
541
- }
542
- }
543
- ]
544
- },
545
- key: null
546
- };
547
- }
548
- function resolveErrorBoundaryFallbackNode(fallback, error, reset) {
549
- return typeof fallback === "function" ? fallback(error, reset) : fallback !== void 0 ? fallback : createDefaultErrorBoundaryFallbackVNode(error);
550
- }
551
- function getControlBoundaryState(node) {
552
- const boundaryNode = node;
553
- const controlState = boundaryNode._controlState ?? null;
554
- const frame = getVNodeContextFrame(boundaryNode);
555
- if (controlState && frame) setControlBoundaryContextFrame(controlState, frame);
556
- return controlState;
557
- }
558
- function evaluateControlBoundaryChildren(node) {
559
- if (node.type !== __CONTROL_BOUNDARY__) return;
560
- const controlState = getControlBoundaryState(node);
561
- if (!controlState) return [];
562
- if (controlState.kind === "for") return evaluateForState(controlState);
563
- if (controlState.kind === "show") return evaluateShowState(controlState);
564
- return evaluateCaseState(controlState);
565
- }
566
- function withControlBoundaryChildren(node, render) {
567
- const controlState = getControlBoundaryState(node);
568
- if (!controlState || controlState.kind !== "for") return render(evaluateControlBoundaryChildren(node));
569
- const children = evaluateForState(controlState);
570
- try {
571
- const result = render(children);
572
- commitForStateTransaction(controlState);
573
- return result;
574
- } catch (error) {
575
- rollbackForStateTransaction(controlState);
576
- throw error;
577
- }
578
- }
579
- //#endregion
580
- //#region src/ssr/attrs.ts
581
- const ESCAPED_ATTR_VALUE_CACHE_LIMIT = 512;
582
- const escapedAttrValueCache = /* @__PURE__ */ new Map();
583
- function isEventHandler(key) {
584
- return key.length >= 2 && key.slice(0, 2).toLowerCase() === "on";
585
- }
586
- function assertAttributeName(name) {
587
- if (!/^[A-Za-z_:][A-Za-z0-9_.:-]*$/.test(name)) throw new TypeError(`Invalid SSR attribute name: ${JSON.stringify(name)}`);
588
- }
589
- function getEscapedAttrValue(value) {
590
- if (value.length > 64) return escapeAttr(value);
591
- const cached = escapedAttrValueCache.get(value);
592
- if (cached !== void 0) return cached;
593
- const escaped = escapeAttr(value);
594
- if (escaped === value) return value;
595
- if (escapedAttrValueCache.size >= ESCAPED_ATTR_VALUE_CACHE_LIMIT) escapedAttrValueCache.clear();
596
- escapedAttrValueCache.set(value, escaped);
597
- return escaped;
598
- }
599
- /**
600
- * Render attributes directly to a sink without intermediate string allocations.
601
- * This is the hot path for streaming SSR.
602
- */
603
- function renderAttrsDirect(props, sink) {
604
- if (!props || typeof props !== "object") return;
605
- const propsObj = props;
606
- for (const key in propsObj) {
607
- const value = propsObj[key];
608
- if (isSkippedProp(key) || key === "dangerouslySetInnerHTML") continue;
609
- if (isEventHandler(key)) continue;
610
- if (key.charCodeAt(0) === 95) continue;
611
- const attrName = getPublicAttributeName(key);
612
- assertAttributeName(attrName);
613
- if (attrName === "style") {
614
- const css = typeof value === "string" ? value : styleObjToCss(value);
615
- if (!css) continue;
616
- sink.write(" style=\"");
617
- if (needsEscapeAttr(css)) sink.write(getEscapedAttrValue(css));
618
- else sink.write(css);
619
- sink.write("\"");
620
- continue;
621
- }
622
- if (value === true) {
623
- sink.write(" ");
624
- sink.write(attrName);
625
- const booleanValue = booleanAttributeValue(attrName);
626
- if (booleanValue) {
627
- sink.write("=\"");
628
- sink.write(booleanValue);
629
- sink.write("\"");
630
- }
631
- continue;
632
- }
633
- if (value === null || value === void 0 || value === false && !isAriaAttribute(attrName)) continue;
634
- const strValue = String(value);
635
- if (isUnsafeUrlAttribute(attrName, strValue)) continue;
636
- sink.write(" ");
637
- sink.write(attrName);
638
- sink.write("=\"");
639
- sink.write(getEscapedAttrValue(strValue));
640
- sink.write("\"");
641
- }
642
- }
643
- //#endregion
644
- //#region src/ssr/hydration-data.ts
645
- const DEFERRED_PAYLOAD = "__askr_deferred__";
646
- function hydrationReplacer(_key, value) {
647
- if (!isDeferred(value)) return value;
648
- if (value.state === "fulfilled") return {
649
- [DEFERRED_PAYLOAD]: "fulfilled",
650
- value: value.value
651
- };
652
- if (value.state === "rejected") return {
653
- [DEFERRED_PAYLOAD]: "rejected",
654
- error: value.error instanceof Error ? value.error.message : String(value.error)
655
- };
656
- return { [DEFERRED_PAYLOAD]: "pending" };
657
- }
658
- function serializeHydrationRenderData(data, runtime) {
659
- const queryCache = runtime ? dehydrateDataRuntime(runtime) : void 0;
660
- const current = isPageRenderEnvelope(data) ? data : createPageRenderEnvelope({ resources: data });
661
- const payload = createPageRenderEnvelope({
662
- resources: {
663
- ...current.resources,
664
- ...queryCache
665
- },
666
- route: current.route,
667
- framework: current.framework
668
- });
669
- const routeHydration = getRouteHydrationMetadata(payload.framework);
670
- if (routeHydration) validateRouteHydrationData(payload.route, routeHydration.r);
671
- if (isEmptyPageRenderEnvelope(payload)) return "";
672
- return `<script type="application/json" ${SSR_RENDER_DATA_ATTR}="true">${JSON.stringify(payload, hydrationReplacer).replace(/</g, "\\u003C").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029")}<\/script>`;
673
- }
674
- //#endregion
675
- //#region src/ssr/render-keys.ts
676
- /**
677
- * SSR Data Management
678
- *
679
- * Manages render-phase keying for deterministic SSR data lookup.
680
- * All state is stored in the per-render context for concurrency safety.
681
- */
682
- function startRenderPhase(data) {
683
- startRenderPhase$1(data === null ? null : isPageRenderEnvelope(data) ? data : createPageRenderEnvelope({ resources: data }));
684
- }
685
- function stopRenderPhase() {
686
- stopRenderPhase$1();
687
- }
688
- //#endregion
689
- //#region src/ssr/render-sync.ts
690
- const RANGE_START = "<!--askr-range-start-->";
691
- const RANGE_END = "<!--askr-range-end-->";
692
- function isMultiRangeChild(child) {
693
- if (Array.isArray(child)) return true;
694
- if (!child || typeof child !== "object" || !("type" in child)) return false;
695
- return isFragmentType(child.type);
696
- }
697
- function inheritRenderableKey(source, result) {
698
- const inheritedKey = source.key;
699
- if (inheritedKey === void 0 || inheritedKey === null) return result;
700
- if (!result || typeof result !== "object" || !("type" in result)) return result;
701
- const resultVNode = result;
702
- if (resultVNode.key === void 0 || resultVNode.key === null) resultVNode.key = inheritedKey;
703
- if (typeof resultVNode.type === "string") {
704
- if (!resultVNode.props) resultVNode.props = {};
705
- if (resultVNode.props["data-key"] === void 0) resultVNode.props["data-key"] = String(inheritedKey);
706
- if (resultVNode.props["data-askr-key-kind"] === void 0) resultVNode.props["data-askr-key-kind"] = typeof inheritedKey;
707
- }
708
- return result;
709
- }
710
- /**
711
- * Render a value to a string through the streaming renderer.
712
- *
713
- * Portal resolution splices content into the finished document by token, so it
714
- * genuinely needs a string — but it gets one from the same renderer everything
715
- * else uses rather than from a second implementation.
716
- */
717
- function renderRenderableToString(value, ctx) {
718
- const sink = new StringSink();
719
- renderRenderableSyncToSink(value, sink, ctx);
720
- sink.end();
721
- return sink.toString();
722
- }
723
- function resolveSSRPortals(html, ctx) {
724
- let resolved = html;
725
- const renderedHosts = /* @__PURE__ */ new Set();
726
- for (;;) {
727
- let foundHost = false;
728
- for (const slot of ctx.ssrPortals.slots.values()) {
729
- const explicitHosts = slot.hosts.filter((host) => !host.automatic);
730
- const activeHosts = explicitHosts.length > 0 ? new Set(explicitHosts.map((host) => host.token)) : new Set(slot.hosts.map((host) => host.token));
731
- for (const host of slot.hosts) {
732
- if (renderedHosts.has(host.token)) continue;
733
- foundHost = true;
734
- const content = activeHosts.has(host.token) && slot.hasValue ? renderRenderableToString(slot.value, ctx) : "";
735
- resolved = resolved.replace(host.token, () => host.automatic && content === "" && slot.hasValue && explicitHosts.length === 0 ? host.token : content);
736
- renderedHosts.add(host.token);
737
- }
738
- }
739
- if (!foundHost) return resolved;
740
- }
741
- }
742
- var SSRPortalSink = class {
743
- constructor(sink) {
744
- this.sink = sink;
745
- this.bufferedChunks = null;
746
- }
747
- write(html) {
748
- if (!html) return;
749
- if (this.bufferedChunks) {
750
- this.bufferedChunks.push(html);
751
- return;
752
- }
753
- this.sink.write(html);
754
- }
755
- writePortalHost(token) {
756
- this.bufferedChunks ??= [];
757
- this.bufferedChunks.push(token);
758
- }
759
- flush(ctx) {
760
- if (!this.bufferedChunks) return;
761
- this.sink.write(resolveSSRPortals(this.bufferedChunks.join(""), ctx));
762
- }
763
- };
764
- /**
765
- * Collects writes so they can be published to a real sink, or dropped.
766
- *
767
- * An ErrorBoundary is transactional: markup its subtree produced before a
768
- * descendant threw must never reach the response, or the fallback lands inside
769
- * a half-written element. A sink cannot take output back, so the protected
770
- * subtree renders into one of these first.
771
- *
772
- * Portal host writes are recorded rather than flattened, because reaching the
773
- * real sink through `writePortalHost` is what puts it into portal-resolving
774
- * mode; replaying them preserves both that signal and the original order.
775
- */
776
- var BufferedSink = class {
777
- constructor() {
778
- this.operations = [];
779
- }
780
- write(html) {
781
- if (html) this.operations.push({
782
- portalHost: false,
783
- text: html
784
- });
785
- }
786
- writePortalHost(token) {
787
- this.operations.push({
788
- portalHost: true,
789
- text: token
790
- });
791
- }
792
- publishTo(sink) {
793
- for (const operation of this.operations) if (operation.portalHost && sink.writePortalHost) sink.writePortalHost(operation.text);
794
- else sink.write(operation.text);
795
- }
796
- };
797
- function renderRenderableSyncToSink(value, sink, ctx) {
798
- if (value === null || value === void 0 || value === false) return;
799
- if (typeof value === "string") {
800
- sink.write(escapeText(value));
801
- return;
802
- }
803
- if (typeof value === "number") {
804
- sink.write(escapeText(String(value)));
805
- return;
806
- }
807
- if (Array.isArray(value)) {
808
- renderChildrenSyncToSink(value, sink, ctx);
809
- return;
810
- }
811
- if (value && typeof value === "object" && "type" in value) renderNodeSyncToSink(value, sink, ctx);
812
- }
813
- function renderChildSyncToSink(child, sink, ctx) {
814
- renderRenderableSyncToSink(child, sink, ctx);
815
- }
816
- /**
817
- * Write a deferred boundary's pending content inside its resolve wrapper.
818
- *
819
- * The wrapper is known from the node's props, so the children stream into the
820
- * sink between the two markers rather than being rendered to a string first.
821
- */
822
- function renderDeferredBoundaryToSink(node, sink, ctx) {
823
- const id = String(node.props?.["id"] ?? "");
824
- sink.write(`<askr-resolve data-askr-deferred="${id}">`);
825
- renderRenderableSyncToSink(node.props?.["pending"], sink, ctx);
826
- sink.write("</askr-resolve>");
827
- }
828
- /** Write one `For` row, bracketing it with range markers when it spans several nodes. */
829
- function renderForRangeChildToSink(child, sink, ctx) {
830
- if (child && typeof child === "object" && "type" in child && typeof child.type === "function") {
831
- const vnode = child;
832
- renderForRangeChildToSink(inheritRenderableKey(vnode, executeComponentSync(vnode.type, vnode.props, ctx, getVNodeContextFrame(vnode) ?? null)), sink, ctx);
833
- return;
834
- }
835
- const spansRange = isMultiRangeChild(child);
836
- if (spansRange) sink.write(RANGE_START);
837
- renderChildSyncToSink(child, sink, ctx);
838
- if (spansRange) sink.write(RANGE_END);
839
- }
840
- /**
841
- * Write a control boundary's children.
842
- *
843
- * Whether the boundary needs range markers is decided from the children
844
- * themselves, before any of them render, so the decision costs nothing and the
845
- * subtree can stream out as it is produced.
846
- */
847
- function renderControlChildrenToSink(node, sink, ctx) {
848
- const controlState = getControlBoundaryState(node);
849
- withControlBoundaryChildren(node, (children) => {
850
- const values = children ?? [];
851
- if (controlState?.kind === "for") {
852
- for (let index = 0; index < values.length; index += 1) renderForRangeChildToSink(values[index], sink, ctx);
853
- return;
854
- }
855
- const spansRange = values.length !== 1 || isMultiRangeChild(values[0]);
856
- if (spansRange) sink.write(RANGE_START);
857
- renderChildrenSyncToSink(values, sink, ctx);
858
- if (spansRange) sink.write(RANGE_END);
859
- });
860
- }
861
- function renderErrorBoundaryFallbackValueToSink(fallback, error, reset, sink, ctx) {
862
- const nextValue = resolveErrorBoundaryFallbackNode(fallback, error, reset);
863
- if (Array.isArray(nextValue)) {
864
- renderChildrenSyncToSink(normalizeRenderableChildren(nextValue), sink, ctx);
865
- return;
866
- }
867
- renderChildSyncToSink(nextValue, sink, ctx);
868
- }
869
- function renderChildrenSyncToSink(children, sink, ctx) {
870
- if (!children || !Array.isArray(children) || children.length === 0) return;
871
- if (children.length >= 32) {
872
- for (let i = 0; i < children.length; i++) renderRenderableSyncToSink(children[i], sink, ctx);
873
- return;
874
- }
875
- for (let i = 0; i < children.length; i++) renderChildSyncToSink(children[i], sink, ctx);
876
- }
877
- function sinkWrite2(sink, a, b) {
878
- if (typeof sink.write2 === "function") {
879
- sink.write2(a, b);
880
- return;
881
- }
882
- sink.write(a);
883
- sink.write(b);
884
- }
885
- function sinkWrite3(sink, a, b, c) {
886
- if (typeof sink.write3 === "function") {
887
- sink.write3(a, b, c);
888
- return;
889
- }
890
- sink.write(a);
891
- sink.write(b);
892
- sink.write(c);
893
- }
894
- function renderNodeSyncToSink(node, sink, ctx) {
895
- const { type, props } = node;
896
- if (typeof type === "function") {
897
- renderRenderableSyncToSink(inheritRenderableKey(node, executeComponentSync(type, props, ctx, getVNodeContextFrame(node) ?? null)), sink, ctx);
898
- return;
899
- }
900
- if (typeof type === "symbol") {
901
- if (type === SSR_PORTAL_HOST) {
902
- const token = String(props?.token ?? "");
903
- const portalSink = sink;
904
- if (portalSink.writePortalHost) portalSink.writePortalHost(token);
905
- else sink.write(token);
906
- return;
907
- }
908
- if (type === SSR_PORTAL_ANCHOR) {
909
- sink.write(String(props?.token ?? ""));
910
- return;
911
- }
912
- if (isFragmentType(type)) {
913
- renderChildrenSyncToSink(getRenderableChildren(node), sink, ctx);
914
- return;
915
- }
916
- if (type === __CONTROL_BOUNDARY__) {
917
- renderControlChildrenToSink(node, sink, ctx);
918
- return;
919
- }
920
- if (type === DEFERRED_BOUNDARY) {
921
- renderDeferredBoundaryToSink(node, sink, ctx);
922
- return;
923
- }
924
- if (type === __ERROR_BOUNDARY__) {
925
- const boundaryState = getErrorBoundaryState(node);
926
- const fallback = props?.fallback;
927
- const reset = createErrorBoundaryReset(node);
928
- if (boundaryState?.error != null) {
929
- renderErrorBoundaryFallbackValueToSink(fallback, boundaryState.error, reset, sink, ctx);
930
- return;
931
- }
932
- const protectedOutput = new BufferedSink();
933
- try {
934
- renderChildrenSyncToSink(normalizeRenderableChildren(props?.children), protectedOutput, ctx);
935
- } catch (error) {
936
- if (boundaryState) {
937
- boundaryState.error = error;
938
- boundaryState.notified = true;
939
- }
940
- logger.error("[Askr] ErrorBoundary caught render error:", error);
941
- renderErrorBoundaryFallbackValueToSink(fallback, error, reset, sink, ctx);
942
- return;
943
- }
944
- protectedOutput.publishTo(sink);
945
- return;
946
- }
947
- throw new Error(`renderNodeSyncToSink: unsupported VNode symbol type: ${String(type)}`);
948
- }
949
- const typeStr = type;
950
- assertElementName(typeStr);
951
- if (VOID_ELEMENTS.has(typeStr)) {
952
- sinkWrite2(sink, "<", typeStr);
953
- renderAttrsDirect(props, sink);
954
- sink.write(" />");
955
- return;
956
- }
957
- const maybeDangerous = props ? props?.dangerouslySetInnerHTML : void 0;
958
- if (maybeDangerous !== void 0 && maybeDangerous !== null) {
959
- const dangerousHtml = typeof maybeDangerous === "object" && "__html" in maybeDangerous ? String(maybeDangerous.__html) : void 0;
960
- sinkWrite2(sink, "<", typeStr);
961
- renderAttrsDirect(props, sink);
962
- sink.write(">");
963
- if (dangerousHtml !== void 0) sink.write(dangerousHtml);
964
- else renderChildrenSyncToSink(getRenderableChildren(node), sink, ctx);
965
- sinkWrite3(sink, "</", typeStr, ">");
966
- return;
967
- }
968
- const children = getRenderableChildren(node);
969
- if (!children || Array.isArray(children) && children.length === 0) {
970
- sinkWrite2(sink, "<", typeStr);
971
- renderAttrsDirect(props, sink);
972
- sink.write(">");
973
- sinkWrite3(sink, "</", typeStr, ">");
974
- return;
975
- }
976
- if (Array.isArray(children) && children.length === 1) {
977
- const only = children[0];
978
- if (typeof only === "string") {
979
- const content = escapeText(only);
980
- sinkWrite2(sink, "<", typeStr);
981
- renderAttrsDirect(props, sink);
982
- sink.write(">");
983
- sink.write(content);
984
- sinkWrite3(sink, "</", typeStr, ">");
985
- return;
986
- }
987
- if (typeof only === "number") {
988
- const content = escapeText(String(only));
989
- sinkWrite2(sink, "<", typeStr);
990
- renderAttrsDirect(props, sink);
991
- sink.write(">");
992
- sink.write(content);
993
- sinkWrite3(sink, "</", typeStr, ">");
994
- return;
995
- }
996
- }
997
- sinkWrite2(sink, "<", typeStr);
998
- renderAttrsDirect(props, sink);
999
- sink.write(">");
1000
- renderChildrenSyncToSink(children, sink, ctx);
1001
- sinkWrite3(sink, "</", typeStr, ">");
1002
- }
1003
- /** Synchronously render a component to an HTML string, without route resolution. */
1004
- function renderToStringSync(component, props, options) {
1005
- const seed = options?.seed ?? 12345;
1006
- const nonce = validateCspNonce(options?.cspNonce);
1007
- const ctx = createRenderContext(seed, {
1008
- data: options?.data,
1009
- envelope: options?.envelope,
1010
- cspNonce: nonce,
1011
- authContext: options?.authContext
1012
- });
1013
- return withRenderContext(ctx, () => {
1014
- startRenderPhase(ctx.renderData);
1015
- try {
1016
- const node = renderSyncComponentRoot(nonce === void 0 ? component : () => CspNonceScope({
1017
- value: nonce,
1018
- children: () => component(props)
1019
- }), props || {}, ctx);
1020
- if (!node) throw new Error("renderToStringSync: wrapped component returned empty");
1021
- const sink = new StringSink();
1022
- renderNodeSyncToSink(node, sink, ctx);
1023
- sink.end();
1024
- const html = resolveSSRPortals(sink.toString(), ctx);
1025
- options?.onContext?.(ctx);
1026
- return html + serializeHydrationRenderData(ctx.hydrationData ?? void 0, ctx.dataRuntime);
1027
- } finally {
1028
- try {
1029
- stopRenderPhase();
1030
- } finally {
1031
- disposeSSRTemporaryOwners(ctx);
1032
- }
1033
- }
1034
- });
1035
- }
1036
- function renderSSRRouteAppToSink(input) {
1037
- const { ctx, data, route, params, sink } = input;
1038
- withRenderContext(ctx, () => {
1039
- startRenderPhase(ctx.renderData);
1040
- try {
1041
- const app = executeComponentSync(ctx.cspNonce === void 0 ? route.handler : () => CspNonceScope({
1042
- value: ctx.cspNonce,
1043
- children: () => route.handler(params)
1044
- }), params, ctx);
1045
- const appSink = new SSRPortalSink(sink);
1046
- renderRenderableSyncToSink(wrapWithDefaultPortal(app), appSink, ctx);
1047
- appSink.flush(ctx);
1048
- if (ctx.deferredBoundaries.length === 0) sink.write(serializeHydrationRenderData(ctx.hydrationData ?? data, ctx.dataRuntime));
1049
- } finally {
1050
- try {
1051
- stopRenderPhase();
1052
- } finally {
1053
- disposeSSRTemporaryOwners(ctx);
1054
- }
1055
- }
1056
- });
1057
- }
1058
- function assertElementName(name) {
1059
- if (!/^[A-Za-z][A-Za-z0-9._:-]*$/.test(name)) throw new TypeError(`Invalid SSR element name: ${JSON.stringify(name)}`);
1060
- }
1061
- //#endregion
1062
- //#region src/ssr/render-resolved.ts
1063
- function sameRouteParams(left, right) {
1064
- if (!left || !right) return !left && !right;
1065
- const leftKeys = Object.keys(left);
1066
- const rightKeys = Object.keys(right);
1067
- if (leftKeys.length !== rightKeys.length) return false;
1068
- for (const key of leftKeys) if (left[key] !== right[key]) return false;
1069
- return true;
1070
- }
1071
- /** Synchronously render an already-resolved route handler to an HTML string. */
1072
- function renderResolvedToStringSync(opts) {
1073
- return renderResolvedRouteToStringSync(opts);
1074
- }
1075
- function renderResolvedRouteToStringSync(opts, authContext) {
1076
- const { url, registry, handler, params, options } = opts;
1077
- const routes = registry.routes;
1078
- const requestUrl = new URL(url, "http://localhost");
1079
- const logicalTarget = removeRouteBasePath(`${requestUrl.pathname}${requestUrl.search}${requestUrl.hash}`, normalizeRouteBasePath(registry.manifest.basePath));
1080
- const matchedIndex = routes.findIndex((route) => {
1081
- if (logicalTarget === void 0) return false;
1082
- const resolved = resolveRouteFromRoutes(new URL(logicalTarget, "http://localhost").pathname, [route]);
1083
- return resolved !== null && resolved.handler === route.handler && sameRouteParams(resolved.params, params);
1084
- });
1085
- if (matchedIndex < 0) throw new Error(`renderResolvedToStringSync: no route found for url: ${url}`);
1086
- const effectiveRoutes = routes.map((route, index) => index === matchedIndex ? {
1087
- ...route,
1088
- handler
1089
- } : route);
1090
- const matchedRoute = routes[matchedIndex];
1091
- const cspNonce = validateCspNonce(options?.cspNonce);
1092
- const ctx = createRenderContext(options?.seed, {
1093
- url,
1094
- basePath: registry.manifest.basePath,
1095
- data: options?.data,
1096
- params,
1097
- routes: effectiveRoutes,
1098
- routeAuth: registry.manifest.auth,
1099
- authContext,
1100
- dataRuntime: options?.dataRuntime,
1101
- envelope: options?.envelope,
1102
- cspNonce
1103
- });
1104
- const sink = new StringSink();
1105
- renderSSRRouteAppToSink({
1106
- route: {
1107
- ...matchedRoute,
1108
- handler
1109
- },
1110
- params: params ?? {},
1111
- data: options?.data,
1112
- ctx,
1113
- sink
1114
- });
1115
- sink.end();
1116
- return sink.toString();
1117
- }
1118
- /** @internal Render the resolved hydration route with its request-local auth context. */
1119
- function renderResolvedForHydrationSync(opts, authContext) {
1120
- return renderResolvedRouteToStringSync(opts, authContext);
1121
- }
1122
- //#endregion
1123
- export { serializeHydrationRenderData as a, createRenderContext as c, withRenderContext as d, withRenderContextAsync as f, renderToStringSync as i, getRenderContext as l, renderResolvedToStringSync as n, StreamSink as o, renderSSRRouteAppToSink as r, StringSink as s, renderResolvedForHydrationSync as t, throwSSRDataMissing as u };