@dom-expressions/runtime 0.50.0-next.15

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/src/server.js ADDED
@@ -0,0 +1,1302 @@
1
+ import { ChildProperties } from "./constants";
2
+ import { sharedConfig, root, ssrHandleError, getOwner, runWithOwner } from "rxcore";
3
+ import { createSerializer, getLocalHeaderScript } from "./serializer";
4
+
5
+ // `mergeProps` comes from the framework like the client/universal entries —
6
+ // prop-merge semantics (function sources, precedence) belong to the reactive
7
+ // core, and a local copy here drifts from them (it resolved function sources
8
+ // for key enumeration only, dropping their values in SSR output).
9
+ export { createComponent, effect, memo, untrack, mergeProps } from "rxcore";
10
+ export { getOwner };
11
+
12
+ export {
13
+ DOMWithState,
14
+ ChildProperties,
15
+ DOMElements,
16
+ SVGElements,
17
+ MathMLElements,
18
+ VoidElements,
19
+ RawTextElements,
20
+ Namespaces,
21
+ DelegatedEvents
22
+ } from "./constants.js";
23
+
24
+ // ---- Asset Manifest ----
25
+
26
+ function resolveAssets(moduleUrl, manifest) {
27
+ if (!manifest) return null;
28
+ const base = manifest._base || "/";
29
+ const entry = manifest[moduleUrl];
30
+ if (!entry) return null;
31
+ const css = [];
32
+ const js = [];
33
+ const visited = new Set();
34
+ const walk = key => {
35
+ if (visited.has(key)) return;
36
+ visited.add(key);
37
+ const e = manifest[key];
38
+ if (!e) return;
39
+ js.push(base + e.file);
40
+ if (e.css) for (let i = 0; i < e.css.length; i++) css.push(base + e.css[i]);
41
+ if (e.imports) for (let i = 0; i < e.imports.length; i++) walk(e.imports[i]);
42
+ };
43
+ walk(moduleUrl);
44
+ return { js, css };
45
+ }
46
+
47
+ function registerEntryAssets(manifest) {
48
+ if (!manifest) return;
49
+ const ctx = sharedConfig.context;
50
+ if (!ctx?.registerAsset) return;
51
+ for (const key in manifest) {
52
+ if (manifest[key].isEntry) {
53
+ const assets = resolveAssets(key, manifest);
54
+ if (assets) {
55
+ for (let i = 0; i < assets.css.length; i++) ctx.registerAsset("style", assets.css[i]);
56
+ }
57
+ return;
58
+ }
59
+ }
60
+ }
61
+
62
+ // ---- Asset Tracking ----
63
+
64
+ function createAssetTracking() {
65
+ const boundaryModules = new Map();
66
+ const boundaryStyles = new Map();
67
+ const emittedAssets = new Set();
68
+ let currentBoundaryId = null;
69
+ return {
70
+ boundaryModules,
71
+ boundaryStyles,
72
+ emittedAssets,
73
+ get currentBoundaryId() {
74
+ return currentBoundaryId;
75
+ },
76
+ set currentBoundaryId(v) {
77
+ currentBoundaryId = v;
78
+ },
79
+ registerModule(moduleUrl, entryUrl) {
80
+ const id = currentBoundaryId || "";
81
+ let map = boundaryModules.get(id);
82
+ if (!map) {
83
+ map = {};
84
+ boundaryModules.set(id, map);
85
+ }
86
+ map[moduleUrl] = entryUrl;
87
+ },
88
+ getBoundaryModules(id) {
89
+ return boundaryModules.get(id) || null;
90
+ },
91
+ getBoundaryStyles(id) {
92
+ return boundaryStyles.get(id) || null;
93
+ }
94
+ };
95
+ }
96
+
97
+ function applyAssetTracking(context, tracking, manifest) {
98
+ Object.defineProperty(context, "_currentBoundaryId", {
99
+ get() {
100
+ return tracking.currentBoundaryId;
101
+ },
102
+ set(v) {
103
+ tracking.currentBoundaryId = v;
104
+ },
105
+ configurable: true,
106
+ enumerable: true
107
+ });
108
+ context.registerModule = tracking.registerModule;
109
+ context.getBoundaryModules = tracking.getBoundaryModules;
110
+ if (manifest) context.resolveAssets = moduleUrl => resolveAssets(moduleUrl, manifest);
111
+ }
112
+
113
+ // Based on https://github.com/WebReflection/domtagger/blob/master/esm/sanitizer.js
114
+ const VOID_ELEMENTS =
115
+ /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
116
+ // Fragment replacement helpers emitted into stream task scripts:
117
+ // - $df(id): swap template payload into the `pl-*` marker range.
118
+ // - $dfl(id): materialize fallback from `pl-*` template content without resolving.
119
+ // - $dflj(ids): materialize fallback content for every id in the list.
120
+ // - $dfs(id, count, defer): register pending stylesheet count for fragment `id`.
121
+ // - $dfc(id): style completion callback; reveals when the fragment/group is unblocked.
122
+ // - $dfg(id): group-style gate check; reveals a waiting group once all style counts hit zero.
123
+ // - $dfj(ids): reveal a group in registration order, waiting if any member still has pending styles.
124
+ const REPLACE_SCRIPT = `function $df(e,n,o,t){if(!(n=document.getElementById(e))||!(o=document.getElementById("pl-"+e)))return 0;for(;o&&8!==o.nodeType&&o.nodeValue!=="pl-"+e;)t=o.nextSibling,o.remove(),o=t;_$HY.done?o.remove():o.replaceWith(n.content),n.remove(),_$HY.fe(e);return 1}function $dfl(e,o,n){if(!(o=document.getElementById("pl-"+e)))return 0;if(o._$fl)return 1;for(n=o.nextSibling;n;){if(8===n.nodeType&&n.nodeValue==="pl-"+e){o.parentNode&&o.parentNode.insertBefore(o.content.cloneNode(!0),n),o._$fl=1;return 1}n=n.nextSibling}return 0}function $dflj(e,i){for(i=0;i<e.length;i++)$dfl(e[i])}function $dfs(e,c,d){(_$HY.sc=_$HY.sc||{})[e]=c,d&&((_$HY.sd=_$HY.sd||{})[e]=1)}function $dfg(e,g,i,k){if(!(g=_$HY.sg&&_$HY.sg[e]))return;for(i=0;i<g.length;i++)if(_$HY.sc&&_$HY.sc[g[i]]>0)return;for(i=0;i<g.length;i++)k=g[i],delete _$HY.sg[k],$df(k)}function $dfc(e){if(--_$HY.sc[e]<=0){delete _$HY.sc[e],_$HY.sg&&_$HY.sg[e]?$dfg(e):!(_$HY.sd&&_$HY.sd[e])&&$df(e);_$HY.sd&&delete _$HY.sd[e]}}function $dfj(e,i,n){for(i=0;i<e.length;i++)if(_$HY.sc&&_$HY.sc[e[i]]>0){for(n=0;n<e.length;n++)(_$HY.sg=_$HY.sg||{})[e[n]]=e;return}for(i=0;i<e.length;i++)$df(e[i])}`;
125
+
126
+ export function renderToString(code, options = {}) {
127
+ const { renderId = "", nonce, noScripts, manifest } = options;
128
+ let scripts = "";
129
+ const serializer = createSerializer({
130
+ scopeId: renderId,
131
+ plugins: options.plugins,
132
+ onData(script) {
133
+ if (noScripts) return;
134
+ if (!scripts) {
135
+ scripts = getLocalHeaderScript(renderId);
136
+ }
137
+ scripts += script + ";";
138
+ },
139
+ onError: options.onError
140
+ });
141
+ const tracking = createAssetTracking();
142
+ sharedConfig.context = {
143
+ assets: [],
144
+ nonce,
145
+ escape: escape,
146
+ resolve: resolveSSRNode,
147
+ ssr: ssr,
148
+ serialize(id, p) {
149
+ if (sharedConfig.context.noHydrate) return;
150
+ if (
151
+ p != null &&
152
+ typeof p === "object" &&
153
+ (typeof p.then === "function" || typeof p[Symbol.asyncIterator] === "function")
154
+ ) {
155
+ throw new Error(
156
+ "Cannot serialize async value in renderToString (id: " +
157
+ id +
158
+ "). " +
159
+ "Use renderToStream for async data."
160
+ );
161
+ }
162
+ serializer.write(id, p);
163
+ },
164
+ registerAsset(type, url) {
165
+ if (tracking.currentBoundaryId && type === "style") {
166
+ let styles = tracking.boundaryStyles.get(tracking.currentBoundaryId);
167
+ if (!styles) {
168
+ styles = new Set();
169
+ tracking.boundaryStyles.set(tracking.currentBoundaryId, styles);
170
+ }
171
+ styles.add(url);
172
+ }
173
+ tracking.emittedAssets.add(url);
174
+ }
175
+ };
176
+ applyAssetTracking(sharedConfig.context, tracking, manifest);
177
+ registerEntryAssets(manifest);
178
+ let html = root(
179
+ d => {
180
+ setTimeout(d);
181
+ return resolveSSRSync(escape(code()));
182
+ },
183
+ { id: renderId }
184
+ );
185
+ serializeFragmentAssets("", tracking.boundaryModules, sharedConfig.context);
186
+ sharedConfig.context.noHydrate = true;
187
+ serializer.close();
188
+ html = injectAssets(sharedConfig.context.assets, html);
189
+ html = injectPreloadLinks(tracking.emittedAssets, html, nonce);
190
+ if (scripts.length) html = injectScripts(html, scripts, options.nonce);
191
+ return html;
192
+ }
193
+
194
+ export function renderToStream(code, options = {}) {
195
+ let { nonce, onCompleteShell, onCompleteAll, renderId = "", noScripts, manifest } = options;
196
+ let dispose;
197
+ const blockingPromises = new Set();
198
+ let headerEmitted = false;
199
+ const pushTask = task => {
200
+ if (noScripts) return;
201
+ if (!headerEmitted) {
202
+ headerEmitted = true;
203
+ tasks += getLocalHeaderScript(renderId);
204
+ }
205
+ tasks += task + ";";
206
+ if (!timer && firstFlushed) {
207
+ timer = setTimeout(writeTasks);
208
+ }
209
+ };
210
+ const onDone = () => {
211
+ writeTasks();
212
+ doShell();
213
+ onCompleteAll &&
214
+ onCompleteAll({
215
+ write(v) {
216
+ !completed && buffer.write(v);
217
+ }
218
+ });
219
+ writable && writable.end();
220
+ completed = true;
221
+ if (firstFlushed) dispose();
222
+ };
223
+ const serializer = createSerializer({
224
+ scopeId: options.renderId,
225
+ plugins: options.plugins,
226
+ onData: pushTask,
227
+ onDone,
228
+ onError: options.onError
229
+ });
230
+ let rootAssetsSerialized = false;
231
+ const serializeRootAssets = () => {
232
+ if (rootAssetsSerialized) return;
233
+ rootAssetsSerialized = true;
234
+ // Ensure the root boundary's module map is written to the serializer
235
+ // before it flushes. A Loading boundary's resolve path can queue flushEnd
236
+ // while the shell is still pending (cascading root holes), which would
237
+ // otherwise call serializer.flush() before doShell() writes root _assets.
238
+ // Seroval silently drops writes after flush, so the root module mapping
239
+ // would be lost and lazy hydration would fail for root-level lazy modules.
240
+ serializeFragmentAssets("", tracking.boundaryModules, context);
241
+ };
242
+ const flushEnd = () => {
243
+ if (!registry.size) {
244
+ serializeRootAssets();
245
+ queue(() => queue(() => serializer.flush())); // double queue because of elsewhere
246
+ }
247
+ };
248
+ const registry = new Map();
249
+ const writeTasks = () => {
250
+ if (tasks.length && !completed && firstFlushed) {
251
+ buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks}</script>`);
252
+ tasks = "";
253
+ }
254
+ timer && clearTimeout(timer);
255
+ timer = null;
256
+ };
257
+
258
+ let context;
259
+ let writable;
260
+ let tmp = "";
261
+ let tasks = "";
262
+ let firstFlushed = false;
263
+ let completed = false;
264
+ let shellCompleted = false;
265
+ let scriptFlushed = false;
266
+ let headStyles;
267
+ const revealGroups = new Map();
268
+ let timer = null;
269
+ const emitTask = task => {
270
+ pushTask(`${task}${!scriptFlushed ? ";" + REPLACE_SCRIPT : ""}`);
271
+ scriptFlushed = true;
272
+ };
273
+ function resolveRevealKeys(groupOrKeys, release, consume) {
274
+ if (Array.isArray(groupOrKeys)) return groupOrKeys.slice();
275
+ let group = revealGroups.get(groupOrKeys);
276
+ if (!group) {
277
+ if (!release) return;
278
+ group = { order: [], keys: new Set(), released: true };
279
+ revealGroups.set(groupOrKeys, group);
280
+ } else if (release) group.released = true;
281
+ if (!group.order.length) return;
282
+ const keys = group.order.slice();
283
+ if (consume) revealGroups.delete(groupOrKeys);
284
+ return keys;
285
+ }
286
+ let rootHoles = null;
287
+ let nextHoleId = 0;
288
+ let buffer = {
289
+ write(payload) {
290
+ tmp += payload;
291
+ }
292
+ };
293
+ const tracking = createAssetTracking();
294
+
295
+ sharedConfig.context = context = {
296
+ async: true,
297
+ assets: [],
298
+ nonce,
299
+ registerAsset(type, url) {
300
+ if (tracking.currentBoundaryId && type === "style") {
301
+ let styles = tracking.boundaryStyles.get(tracking.currentBoundaryId);
302
+ if (!styles) {
303
+ styles = new Set();
304
+ tracking.boundaryStyles.set(tracking.currentBoundaryId, styles);
305
+ }
306
+ styles.add(url);
307
+ }
308
+ if (!tracking.emittedAssets.has(url)) {
309
+ tracking.emittedAssets.add(url);
310
+ if (firstFlushed && type === "module") {
311
+ buffer.write(`<link rel="modulepreload" href="${url}">`);
312
+ }
313
+ }
314
+ },
315
+ block(p) {
316
+ if (!firstFlushed) blockingPromises.add(p);
317
+ },
318
+ replace(id, payloadFn) {
319
+ if (firstFlushed) return;
320
+ const placeholder = `<!--!$${id}-->`;
321
+ const first = html.indexOf(placeholder);
322
+ if (first === -1) return;
323
+ const last = html.indexOf(`<!--!$/${id}-->`, first + placeholder.length);
324
+ html =
325
+ html.slice(0, first) +
326
+ resolveSSRSync(escape(payloadFn())) +
327
+ html.slice(last + placeholder.length + 1);
328
+ },
329
+ serialize(id, p, deferStream) {
330
+ if (sharedConfig.context.noHydrate) return;
331
+ if (!firstFlushed && deferStream && typeof p === "object" && "then" in p) {
332
+ blockingPromises.add(p);
333
+ p.then(d => serializer.write(id, d)).catch(e => serializer.write(id, e));
334
+ } else serializer.write(id, p);
335
+ },
336
+ escape: escape,
337
+ resolve: resolveSSRNode,
338
+ ssr: ssr,
339
+ registerFragment(key, options) {
340
+ const revealGroup = options && options.revealGroup;
341
+ if (revealGroup) {
342
+ let group = revealGroups.get(revealGroup);
343
+ if (!group) {
344
+ group = { order: [], keys: new Set(), released: false };
345
+ revealGroups.set(revealGroup, group);
346
+ }
347
+ if (!group.keys.has(key)) {
348
+ group.keys.add(key);
349
+ group.order.push(key);
350
+ }
351
+ if (group.released) {
352
+ throw new Error(
353
+ "registerFragment() for reveal group '" +
354
+ revealGroup +
355
+ "' was called after revealFragments(). Ensure template payload is emitted before grouped reveal."
356
+ );
357
+ }
358
+ }
359
+ if (!registry.has(key)) {
360
+ let resolve, reject;
361
+ const p = new Promise((r, rej) => ((resolve = r), (reject = rej)));
362
+ // double queue to ensure that the fragment is last but in same flush
363
+ registry.set(key, {
364
+ resolve: err =>
365
+ queue(() =>
366
+ queue(() => {
367
+ err ? reject(err) : resolve(true);
368
+ queue(flushEnd);
369
+ })
370
+ )
371
+ });
372
+ serializer.write(key + "_fr", p);
373
+ }
374
+ return (value, error) => {
375
+ if (registry.has(key)) {
376
+ const item = registry.get(key);
377
+ registry.delete(key);
378
+
379
+ if (item.children) {
380
+ for (const k in item.children) {
381
+ value = replacePlaceholder(value, k, item.children[k]);
382
+ }
383
+ }
384
+
385
+ const parentKey = waitForFragments(registry, key);
386
+ if (parentKey) {
387
+ const parent = registry.get(parentKey);
388
+ parent.children ||= {};
389
+ parent.children[key] = value !== undefined ? value : "";
390
+ serializeFragmentAssets(key, tracking.boundaryModules, context);
391
+ propagateBoundaryStyles(key, parentKey, tracking);
392
+ item.resolve();
393
+ return;
394
+ }
395
+ if (!completed) {
396
+ if (!firstFlushed) {
397
+ queue(() => (html = replacePlaceholder(html, key, value !== undefined ? value : "")));
398
+ serializeFragmentAssets(key, tracking.boundaryModules, context);
399
+ item.resolve(error);
400
+ } else {
401
+ serializeFragmentAssets(key, tracking.boundaryModules, context);
402
+ const styles = collectStreamStyles(key, tracking, headStyles);
403
+ const deferActivation = !!revealGroup;
404
+ if (styles.length) {
405
+ emitTask(`$dfs("${key}",${styles.length},${deferActivation ? 1 : 0})`);
406
+ writeTasks();
407
+ for (const url of styles) {
408
+ buffer.write(
409
+ `<link rel="stylesheet" href="${url}" onload="$dfc('${key}')" onerror="$dfc('${key}')">`
410
+ );
411
+ }
412
+ buffer.write(
413
+ `<template id="${key}">${value !== undefined ? value : " "}</template>`
414
+ );
415
+ } else {
416
+ buffer.write(
417
+ `<template id="${key}">${value !== undefined ? value : " "}</template>`
418
+ );
419
+ if (!deferActivation) {
420
+ emitTask(`$df("${key}")`);
421
+ }
422
+ }
423
+ item.resolve(error);
424
+ }
425
+ }
426
+ }
427
+ return firstFlushed;
428
+ };
429
+ },
430
+ revealFragments(groupOrKeys) {
431
+ // Group reveal follows fragment registration order so visibility order
432
+ // cannot be changed by resolve timing.
433
+ const keys = resolveRevealKeys(groupOrKeys, true, true);
434
+ if (!keys) return;
435
+ emitTask(`$dfj(${JSON.stringify(keys)})`);
436
+ },
437
+ revealFallbacks(groupOrKeys) {
438
+ const keys = resolveRevealKeys(groupOrKeys, false, false);
439
+ if (!keys) return;
440
+ emitTask(`$dflj(${JSON.stringify(keys)})`);
441
+ }
442
+ };
443
+ applyAssetTracking(context, tracking, manifest);
444
+ registerEntryAssets(manifest);
445
+
446
+ let html = root(
447
+ d => {
448
+ dispose = d;
449
+ const res = resolveSSRNode(escape(code()));
450
+ if (!res.h.length) return res.t[0];
451
+ rootHoles = [];
452
+ let out = res.t[0];
453
+ for (let i = 0; i < res.h.length; i++) {
454
+ const id = nextHoleId++;
455
+ rootHoles.push({ id, fn: res.h[i] });
456
+ out += `<!--rh${id}-->` + res.t[i + 1];
457
+ }
458
+ for (const p of res.p) blockingPromises.add(p);
459
+ return out;
460
+ },
461
+ { id: renderId }
462
+ );
463
+ // Re-pull pending root holes, splicing sync results into `html` and
464
+ // re-queueing still-async ones (their retry promises join
465
+ // `blockingPromises`). Returns true once no holes remain.
466
+ function resolveRootHoles() {
467
+ if (!rootHoles) return true;
468
+ const pending = [];
469
+ for (const { id, fn } of rootHoles) {
470
+ const marker = `<!--rh${id}-->`;
471
+ const res = resolveSSRNode(fn);
472
+ if (!res.h.length) {
473
+ html = html.replace(marker, res.t[0]);
474
+ } else {
475
+ let out = res.t[0];
476
+ for (let j = 0; j < res.h.length; j++) {
477
+ const newId = nextHoleId++;
478
+ pending.push({ id: newId, fn: res.h[j] });
479
+ out += `<!--rh${newId}-->` + res.t[j + 1];
480
+ }
481
+ html = html.replace(marker, out);
482
+ for (const p of res.p) blockingPromises.add(p);
483
+ }
484
+ }
485
+ if (pending.length) {
486
+ rootHoles = pending;
487
+ return false;
488
+ }
489
+ rootHoles = null;
490
+ return true;
491
+ }
492
+ function doShell() {
493
+ if (shellCompleted) return;
494
+ if (!resolveRootHoles()) return;
495
+ sharedConfig.context = context;
496
+ html = injectAssets(context.assets, html);
497
+ headStyles = new Set();
498
+ for (const url of tracking.emittedAssets) {
499
+ if (url.endsWith(".css")) headStyles.add(url);
500
+ }
501
+ html = injectPreloadLinks(tracking.emittedAssets, html, nonce);
502
+ serializeRootAssets();
503
+ if (tasks.length) html = injectScripts(html, tasks, nonce);
504
+ buffer.write(html);
505
+ tasks = "";
506
+ onCompleteShell &&
507
+ onCompleteShell({
508
+ write(v) {
509
+ !completed && buffer.write(v);
510
+ }
511
+ });
512
+ shellCompleted = true;
513
+ }
514
+ return {
515
+ then(fn) {
516
+ function complete() {
517
+ dispose();
518
+ fn(tmp);
519
+ }
520
+ if (onCompleteAll) {
521
+ let ogComplete = onCompleteAll;
522
+ onCompleteAll = options => {
523
+ ogComplete(options);
524
+ complete();
525
+ };
526
+ } else onCompleteAll = complete;
527
+ function flush() {
528
+ allSettled(blockingPromises).then(() => {
529
+ setTimeout(() => {
530
+ if (!resolveRootHoles()) return flush();
531
+ queue(flushEnd);
532
+ });
533
+ });
534
+ }
535
+ flush();
536
+ },
537
+ pipe(w) {
538
+ function flush() {
539
+ allSettled(blockingPromises).then(() => {
540
+ setTimeout(() => {
541
+ doShell();
542
+ if (!shellCompleted) return flush();
543
+ buffer = writable = w;
544
+ buffer.write(tmp);
545
+ firstFlushed = true;
546
+ if (completed) {
547
+ dispose();
548
+ writable.end();
549
+ } else flushEnd();
550
+ });
551
+ });
552
+ }
553
+ flush();
554
+ },
555
+ pipeTo(w) {
556
+ let resolve;
557
+ const p = new Promise(r => (resolve = r));
558
+ function flush() {
559
+ allSettled(blockingPromises).then(() => {
560
+ setTimeout(() => {
561
+ doShell();
562
+ if (!shellCompleted) return flush();
563
+ const encoder = new TextEncoder();
564
+ const writer = w.getWriter();
565
+ writable = {
566
+ end() {
567
+ writer.releaseLock();
568
+ w.close().catch(() => {});
569
+ resolve();
570
+ }
571
+ };
572
+ buffer = {
573
+ write(payload) {
574
+ writer.write(encoder.encode(payload)).catch(() => {});
575
+ }
576
+ };
577
+ buffer.write(tmp);
578
+ firstFlushed = true;
579
+ if (completed) {
580
+ dispose();
581
+ writable.end();
582
+ } else flushEnd();
583
+ });
584
+ });
585
+ }
586
+ flush();
587
+ return p;
588
+ }
589
+ };
590
+ }
591
+
592
+ // components
593
+ export function HydrationScript(props) {
594
+ const { nonce } = sharedConfig.context;
595
+ return ssr(generateHydrationScript({ nonce, ...props }));
596
+ }
597
+
598
+ // Compiler-emitted: tags `fn` so `ssr()` routes it through the grouped
599
+ // fast-path. One grouped fn per element collapses N attribute/textContent
600
+ // closures into one array-returning call.
601
+ export function ssrGroup(fn, n) {
602
+ fn.$g = n;
603
+ return fn;
604
+ }
605
+
606
+ // Cold-path NotReady catch + owner-capture wrap, shared by every site
607
+ // that escalates a sync throw to a streaming retry slot. Returns
608
+ // `{ fn, p }` on `NotReadyError` (with `fn` bound to the original owner
609
+ // so retries see the same id counter / contexts) or `null` for
610
+ // non-NotReady errors so callers can fall back to their contribute-
611
+ // nothing path.
612
+ function buildAsyncWrap(err, node) {
613
+ const p = ssrHandleError(err);
614
+ if (!p) return null;
615
+ const owner = getOwner();
616
+ return { fn: owner ? () => runWithOwner(owner, node) : node, p };
617
+ }
618
+
619
+ // Cold-path helper for the first hit of a group. Isolates `try/catch`
620
+ // from the hot `ssr()` loop. Returns the value array on sync success,
621
+ // `{ fn, p }` on `NotReadyError` escalation, or `null` for non-NotReady
622
+ // errors (matches `tryResolveString`'s "" path).
623
+ function ssrFirstGroupHit(hole) {
624
+ try {
625
+ return hole();
626
+ } catch (err) {
627
+ return buildAsyncWrap(err, hole);
628
+ }
629
+ }
630
+
631
+ function tryResolveFunctionHole(hole) {
632
+ let value;
633
+ try {
634
+ value = hole();
635
+ } catch (err) {
636
+ return buildAsyncWrap(err, hole) || "";
637
+ }
638
+ const t = typeof value;
639
+ if (t === "string") return value;
640
+ if (t === "number") return "" + value;
641
+ if (value == null || t === "boolean") return "";
642
+ return tryResolveString(value);
643
+ }
644
+
645
+ // Cold-path: splice a nested `{ t, h, p }` template into `result` at
646
+ // its current last segment. Used when `tryResolveString` walks into a
647
+ // template object that itself carries async holes.
648
+ function mergeTemplateInto(result, node) {
649
+ result.t[result.t.length - 1] += node.t[0];
650
+ if (node.t.length > 1) {
651
+ result.t.push(...node.t.slice(1));
652
+ result.h.push(...node.h);
653
+ result.p.push(...node.p);
654
+ }
655
+ }
656
+
657
+ function appendResolvedNode(result, node) {
658
+ if (node.fn !== undefined) {
659
+ result.h.push(node.fn);
660
+ result.p.push(node.p);
661
+ result.t.push("");
662
+ } else if (node.merge !== undefined) mergeTemplateInto(result, node.merge);
663
+ else resolveSSRNode(node.bail, result);
664
+ }
665
+
666
+ // Module-scoped cache for grouped retry slots. Slots fire contiguously
667
+ // in queue order, so slot 0 evaluates `fn` once and caches `arr`
668
+ // (success) or `err` (NotReady) on the module slots; slots `1..N-1`
669
+ // short-circuit on `_lastGroupFn === fn`. Cache invalidates on a
670
+ // different fn (next group) or when slot 0 re-fires (next retry pass
671
+ // for the same group). Net: 1 evaluation per group per pass.
672
+ let _lastGroupFn = null;
673
+ let _lastGroupArr = null;
674
+ let _lastGroupErr = null;
675
+
676
+ function ssrGroupSlot(fn, idx) {
677
+ return () => {
678
+ if (idx > 0 && _lastGroupFn === fn) {
679
+ if (_lastGroupArr !== null) return _lastGroupArr[idx];
680
+ throw _lastGroupErr;
681
+ }
682
+ _lastGroupFn = fn;
683
+ _lastGroupArr = null;
684
+ _lastGroupErr = null;
685
+ try {
686
+ _lastGroupArr = fn();
687
+ return _lastGroupArr[idx];
688
+ } catch (err) {
689
+ _lastGroupErr = err;
690
+ throw err;
691
+ }
692
+ };
693
+ }
694
+
695
+ // rendering
696
+ export function ssr(t) {
697
+ // Inlined hole resolution — uses `arguments` instead of a `(t, ...nodes)`
698
+ // rest parameter to avoid the per-call holes-array allocation. Inline
699
+ // string/number/null/bool fast paths skip `tryResolveString` entirely
700
+ // for the typical "all-static-after-eval" hole shape; only the heavy
701
+ // path (async escalation) materializes the `{ t, h, p }` result shape.
702
+ //
703
+ // Group fast-path (`hole.$g` set by compiler `_$ssrGroup`): one call
704
+ // returns an array of values for >=N hole positions. The check is at
705
+ // the END of the typeof chain so non-function holes don't pay for it.
706
+ const len = arguments.length;
707
+ if (len === 1) return { t };
708
+ let s = t[0];
709
+ let result = null;
710
+ let lastGroup = null;
711
+ // Array on sync success, `{ fn, p }` on escalation, null otherwise.
712
+ let lastGroupVal = null;
713
+ let lastGroupIdx = 0;
714
+ for (let i = 1; i < len; i++) {
715
+ const hole = arguments[i];
716
+ const ht = typeof hole;
717
+ if (ht === "string") {
718
+ if (result === null) s += hole;
719
+ else result.t[result.t.length - 1] += hole;
720
+ } else if (ht === "number") {
721
+ if (result === null) s += hole;
722
+ else result.t[result.t.length - 1] += hole;
723
+ } else if (hole == null || ht === "boolean") {
724
+ // skip
725
+ } else if (ht === "function" && hole.$g) {
726
+ let value;
727
+ let hasValue = false;
728
+ if (lastGroup !== hole) {
729
+ const r = ssrFirstGroupHit(hole);
730
+ if (r !== null) {
731
+ lastGroup = hole;
732
+ lastGroupVal = r;
733
+ lastGroupIdx = 0;
734
+ if (!Array.isArray(r) && result === null) {
735
+ result = { t: [s], h: [], p: [] };
736
+ s = "";
737
+ }
738
+ }
739
+ // r === null: non-NotReady error, contribute nothing — matches
740
+ // the `return ""` path in `tryResolveString`.
741
+ }
742
+ if (lastGroup === hole) {
743
+ if (Array.isArray(lastGroupVal)) {
744
+ value = lastGroupVal[lastGroupIdx++];
745
+ hasValue = true;
746
+ } else {
747
+ result.h.push(ssrGroupSlot(lastGroupVal.fn, lastGroupIdx++));
748
+ result.p.push(lastGroupVal.p);
749
+ result.t.push("");
750
+ }
751
+ }
752
+ if (hasValue) {
753
+ // Type dispatch on the dequeued value. textContent expressions
754
+ // (e.g., `_$escape(item().title)`) can return arrays when the
755
+ // input is an array, so we cannot assume strings here.
756
+ const vt = typeof value;
757
+ if (vt === "string" || vt === "number") {
758
+ if (result === null) s += value;
759
+ else result.t[result.t.length - 1] += value;
760
+ } else if (value == null || vt === "boolean") {
761
+ // skip
762
+ } else if (result !== null) {
763
+ resolveSSRNode(value, result);
764
+ } else {
765
+ const rs = tryResolveString(value);
766
+ if (typeof rs === "string") {
767
+ s += rs;
768
+ } else {
769
+ result = { t: [s], h: [], p: [] };
770
+ s = "";
771
+ if (rs.merge !== undefined) mergeTemplateInto(result, rs.merge);
772
+ else resolveSSRNode(rs.bail, result);
773
+ }
774
+ }
775
+ }
776
+ } else if (result !== null) {
777
+ resolveSSRNode(hole, result);
778
+ } else if (ht === "function") {
779
+ const r = tryResolveFunctionHole(hole);
780
+ if (typeof r === "string") s += r;
781
+ else {
782
+ result = { t: [s], h: [], p: [] };
783
+ s = "";
784
+ appendResolvedNode(result, r);
785
+ }
786
+ } else {
787
+ const r = tryResolveString(hole);
788
+ if (typeof r === "string") {
789
+ s += r;
790
+ } else {
791
+ // Escalation: allocate the heavy `{ t, h, p }` result shape and
792
+ // splice in the sync prefix we accumulated.
793
+ result = { t: [s], h: [], p: [] };
794
+ s = "";
795
+ appendResolvedNode(result, r);
796
+ }
797
+ }
798
+ const next = t[i];
799
+ if (result === null) s += next;
800
+ else result.t[result.t.length - 1] += next;
801
+ }
802
+ if (result === null) return { t: s };
803
+ return result;
804
+ }
805
+
806
+ export function ssrClassName(value) {
807
+ if (!value) return "";
808
+ if (typeof value === "string") return escape(value, true);
809
+ value = classListToObject(value);
810
+ let classKeys = Object.keys(value),
811
+ result = "";
812
+ for (let i = 0, len = classKeys.length; i < len; i++) {
813
+ const key = classKeys[i],
814
+ classValue = !!value[key];
815
+ if (!key || key === "undefined" || !classValue) continue;
816
+ i && (result += " ");
817
+ // Object keys land inside class="..." so they must be attribute-escaped.
818
+ result += escape(key, true);
819
+ }
820
+ return result;
821
+ }
822
+
823
+ export function ssrStyle(value) {
824
+ if (!value) return "";
825
+ if (typeof value === "string") return escape(value, true);
826
+
827
+ let result = "";
828
+ const k = Object.keys(value);
829
+ for (let i = 0; i < k.length; i++) {
830
+ // Object keys land inside style="..." so they must be attribute-escaped
831
+ // to prevent breaking out via `"`.
832
+ const s = escape(k[i], true);
833
+ const v = value[k[i]];
834
+ if (v != undefined) {
835
+ if (i) result += ";";
836
+ const r = escape(v, true);
837
+ if (r != undefined && r !== "undefined") {
838
+ result += `${s}:${r}`;
839
+ }
840
+ }
841
+ }
842
+ return result;
843
+ }
844
+
845
+ export function ssrStyleProperty(name, value) {
846
+ // Compiler contract: for literal-key `style={{ color: v }}` the compiler
847
+ // passes a fixed string like `"color:"`; for computed-key
848
+ // `style={{ [k]: v }}` the compiler wraps the key with `_$escape(k, true)`
849
+ // before concatenating the `:` suffix. Either way `name` is safe to splice
850
+ // into style="..." without further escaping.
851
+ return value != null ? name + value : "";
852
+ }
853
+
854
+ // review with new ssr
855
+ export function ssrElement(tag, props, children, needsId) {
856
+ if (props == null) props = {};
857
+ else if (typeof props === "function") props = props();
858
+ const skipChildren = VOID_ELEMENTS.test(tag);
859
+ const keys = Object.keys(props);
860
+ let result = `<${tag}${needsId ? ssrHydrationKey() : ""} `;
861
+ for (let i = 0; i < keys.length; i++) {
862
+ const prop = keys[i];
863
+ if (ChildProperties.has(prop)) {
864
+ if (children === undefined && !skipChildren)
865
+ children =
866
+ tag === "script" || tag === "style" || prop === "innerHTML"
867
+ ? props[prop]
868
+ : escape(props[prop]);
869
+ continue;
870
+ }
871
+ const value = props[prop];
872
+ if (prop === "style") {
873
+ result += `style="${ssrStyle(value)}"`;
874
+ } else if (prop === "class") {
875
+ result += `class="${ssrClassName(value)}"`;
876
+ } else if (
877
+ value == undefined ||
878
+ prop === "ref" ||
879
+ prop.slice(0, 2) === "on" ||
880
+ prop.slice(0, 5) === "prop:"
881
+ ) {
882
+ continue;
883
+ } else if (typeof value === "boolean") {
884
+ if (!value) continue;
885
+ result += escape(prop);
886
+ } else {
887
+ result += value === "" ? escape(prop) : `${escape(prop)}="${escape(value, true)}"`;
888
+ }
889
+ if (i !== keys.length - 1) result += " ";
890
+ }
891
+
892
+ if (skipChildren) return { t: result + "/>" };
893
+ if (typeof children === "function") children = children();
894
+ return ssr([result + ">", `</${tag}>`], resolveSSRNode(children, undefined, true));
895
+ }
896
+
897
+ export function ssrAttribute(key, value) {
898
+ // Compiler contract: `key` is always a compile-time string literal emitted
899
+ // from a JSX attribute name (see setAttr in babel-plugin/src/ssr/element.js)
900
+ // which can never contain `"`, `<`, `&`, or `>`. `value` is already
901
+ // attribute-escaped by the compiler via `_$escape(..., true)`. Both are
902
+ // trusted here so this hot path stays a pure string concatenation.
903
+ return value == null || value === false ? "" : value === true ? ` ${key}` : ` ${key}="${value}"`;
904
+ }
905
+
906
+ export function ssrHydrationKey() {
907
+ const hk = getHydrationKey();
908
+ return hk ? ` _hk=${hk}` : "";
909
+ }
910
+
911
+ export function escape(s, attr) {
912
+ const t = typeof s;
913
+ if (t !== "string") {
914
+ if (!attr && Array.isArray(s)) {
915
+ const joined = tryJoinPlainSSRArray(s);
916
+ if (joined !== undefined) return joined;
917
+ s = s.slice(); // avoids double escaping - https://github.com/ryansolid/dom-expressions/issues/393
918
+ for (let i = 0; i < s.length; i++) s[i] = escape(s[i]);
919
+ return s;
920
+ }
921
+ if (attr && t === "boolean") return s;
922
+ return s;
923
+ }
924
+ // Fast path: single forward pass over the string. Most values (color
925
+ // names, ids, prop strings, plain text) contain none of `&`, `<`, or
926
+ // `"`, so we bail without allocating. Slow path resumes from the first
927
+ // hit so we don't re-scan the clean prefix.
928
+ // Char codes: `&` = 38, `<` = 60, `"` = 34.
929
+ const delimCode = attr ? 34 : 60;
930
+ const len = s.length;
931
+ for (let i = 0; i < len; i++) {
932
+ const c = s.charCodeAt(i);
933
+ if (c === 38 || c === delimCode) return escapeSlow(s, attr, i);
934
+ }
935
+ return s;
936
+ }
937
+
938
+ // Slow path: at least one of `&`, `<`/`"` was found at position `start`.
939
+ // Kept separate so `escape()` stays small and inlinable in the hot path.
940
+ function escapeSlow(s, attr, start) {
941
+ const delim = attr ? '"' : "<";
942
+ const delimCode = attr ? 34 : 60;
943
+ const escDelim = attr ? "&quot;" : "&lt;";
944
+ // Seed iDelim/iAmp from the first hit we already found, so we don't
945
+ // re-scan the prefix we just proved is clean.
946
+ const c0 = s.charCodeAt(start);
947
+ let iDelim = c0 === delimCode ? start : s.indexOf(delim, start);
948
+ let iAmp = c0 === 38 ? start : s.indexOf("&", start);
949
+
950
+ let left = 0,
951
+ out = "";
952
+
953
+ while (iDelim >= 0 && iAmp >= 0) {
954
+ if (iDelim < iAmp) {
955
+ if (left < iDelim) out += s.substring(left, iDelim);
956
+ out += escDelim;
957
+ left = iDelim + 1;
958
+ iDelim = s.indexOf(delim, left);
959
+ } else {
960
+ if (left < iAmp) out += s.substring(left, iAmp);
961
+ out += "&amp;";
962
+ left = iAmp + 1;
963
+ iAmp = s.indexOf("&", left);
964
+ }
965
+ }
966
+
967
+ if (iDelim >= 0) {
968
+ do {
969
+ if (left < iDelim) out += s.substring(left, iDelim);
970
+ out += escDelim;
971
+ left = iDelim + 1;
972
+ iDelim = s.indexOf(delim, left);
973
+ } while (iDelim >= 0);
974
+ } else
975
+ while (iAmp >= 0) {
976
+ if (left < iAmp) out += s.substring(left, iAmp);
977
+ out += "&amp;";
978
+ left = iAmp + 1;
979
+ iAmp = s.indexOf("&", left);
980
+ }
981
+
982
+ return left < s.length ? out + s.substring(left) : out;
983
+ }
984
+
985
+ function tryJoinPlainSSRArray(nodes) {
986
+ if (nodes.length === 0) return undefined;
987
+ let out = "";
988
+ for (let i = 0, len = nodes.length; i < len; i++) {
989
+ const node = nodes[i];
990
+ if (node == null || typeof node !== "object" || node.h || typeof node.t !== "string") {
991
+ return undefined;
992
+ }
993
+ out += node.t;
994
+ }
995
+ return out;
996
+ }
997
+
998
+ export function getHydrationKey() {
999
+ const hydrate = sharedConfig.context;
1000
+ return hydrate && sharedConfig.getNextContextId();
1001
+ }
1002
+
1003
+ export function applyRef(r, element) {
1004
+ Array.isArray(r) ? r.flat(Infinity).forEach(f => f && f(element)) : r(element);
1005
+ }
1006
+
1007
+ export function useAssets(fn) {
1008
+ sharedConfig.context.assets.push(() => resolveSSRSync(escape(fn())));
1009
+ }
1010
+
1011
+ export function getAssets() {
1012
+ const assets = sharedConfig.context.assets;
1013
+ let out = "";
1014
+ for (let i = 0, len = assets.length; i < len; i++) out += assets[i]();
1015
+ return out;
1016
+ }
1017
+
1018
+ // consider deprecating
1019
+ export function Assets(props) {
1020
+ useAssets(() => props.children);
1021
+ }
1022
+
1023
+ export function generateHydrationScript({ eventNames = ["click", "input"], nonce } = {}) {
1024
+ return `<script${
1025
+ nonce ? ` nonce="${nonce}"` : ""
1026
+ }>window._$HY||(e=>{let t=e=>e&&e.hasAttribute&&(e.hasAttribute("_hk")?e:t(e.host&&e.host.nodeType?e.host:e.parentNode));["${eventNames.join(
1027
+ '","'
1028
+ )}"].forEach((o=>document.addEventListener(o,(o=>{if(!e.events)return;let s=t(o.composedPath&&o.composedPath()[0]||o.target);s&&!e.completed.has(s)&&e.events.push([s,o])}))))})(_$HY={events:[],completed:new WeakSet,r:{},fe(){}});</script><!--xs-->`;
1029
+ }
1030
+
1031
+ function queue(fn) {
1032
+ return Promise.resolve().then(fn);
1033
+ }
1034
+
1035
+ function allSettled(promises) {
1036
+ let size = promises.size;
1037
+ return Promise.allSettled(promises).then(() => {
1038
+ if (promises.size !== size) return allSettled(promises);
1039
+ return;
1040
+ });
1041
+ }
1042
+
1043
+ function injectAssets(assets, html) {
1044
+ if (!assets || !assets.length) return html;
1045
+ let out = "";
1046
+ for (let i = 0, len = assets.length; i < len; i++) out += assets[i]();
1047
+ const index = html.indexOf("</head>");
1048
+ if (index === -1) return html;
1049
+ return html.slice(0, index) + out + html.slice(index);
1050
+ }
1051
+
1052
+ function injectPreloadLinks(emittedAssets, html, nonce) {
1053
+ if (!emittedAssets.size) return html;
1054
+ let links = "";
1055
+ for (const url of emittedAssets) {
1056
+ if (url.endsWith(".css")) {
1057
+ links += `<link rel="stylesheet" href="${url}">`;
1058
+ } else {
1059
+ links += `<link rel="modulepreload" href="${url}">`;
1060
+ }
1061
+ }
1062
+ const index = html.indexOf("</head>");
1063
+ if (index === -1) return html;
1064
+ return html.slice(0, index) + links + html.slice(index);
1065
+ }
1066
+
1067
+ function serializeFragmentAssets(key, boundaryModules, context) {
1068
+ const map = boundaryModules.get(key);
1069
+ if (!map || !Object.keys(map).length) return;
1070
+ context.serialize(key + "_assets", map);
1071
+ }
1072
+
1073
+ function propagateBoundaryStyles(childKey, parentKey, tracking) {
1074
+ const childStyles = tracking.getBoundaryStyles(childKey);
1075
+ if (!childStyles) return;
1076
+ let parentStyles = tracking.boundaryStyles.get(parentKey);
1077
+ if (!parentStyles) {
1078
+ parentStyles = new Set();
1079
+ tracking.boundaryStyles.set(parentKey, parentStyles);
1080
+ }
1081
+ for (const url of childStyles) {
1082
+ parentStyles.add(url);
1083
+ }
1084
+ }
1085
+
1086
+ function collectStreamStyles(key, tracking, headStyles) {
1087
+ const styles = tracking.getBoundaryStyles(key);
1088
+ if (!styles) return [];
1089
+ const result = [];
1090
+ for (const url of styles) {
1091
+ if (!headStyles || !headStyles.has(url)) {
1092
+ result.push(url);
1093
+ }
1094
+ }
1095
+ return result;
1096
+ }
1097
+
1098
+ function injectScripts(html, scripts, nonce) {
1099
+ const tag = `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>`;
1100
+ const index = html.indexOf("<!--xs-->");
1101
+ if (index > -1) {
1102
+ return html.slice(0, index) + tag + html.slice(index);
1103
+ }
1104
+ return html + tag;
1105
+ }
1106
+
1107
+ function waitForFragments(registry, key) {
1108
+ for (const k of [...registry.keys()].reverse()) {
1109
+ if (key.startsWith(k)) return k;
1110
+ }
1111
+ return false;
1112
+ }
1113
+
1114
+ function replacePlaceholder(html, key, value) {
1115
+ const marker = `<template id="pl-${key}">`;
1116
+ const close = `<!--pl-${key}-->`;
1117
+
1118
+ const first = html.indexOf(marker);
1119
+ if (first === -1) return html;
1120
+ const last = html.indexOf(close, first + marker.length);
1121
+
1122
+ return html.slice(0, first) + value + html.slice(last + close.length);
1123
+ }
1124
+
1125
+ function classListToObject(classList) {
1126
+ if (Array.isArray(classList)) {
1127
+ const result = {};
1128
+ flattenClassList(classList, result);
1129
+ return result;
1130
+ }
1131
+ return classList;
1132
+ }
1133
+
1134
+ function flattenClassList(list, result) {
1135
+ for (let i = 0, len = list.length; i < len; i++) {
1136
+ const item = list[i];
1137
+ if (Array.isArray(item)) flattenClassList(item, result);
1138
+ else if (typeof item === "object" && item != null) Object.assign(result, item);
1139
+ else if (item || item === 0) result[item] = true;
1140
+ }
1141
+ }
1142
+
1143
+ // Best-effort sync resolution. Returns a string when the entire `node`
1144
+ // resolves synchronously to text. Otherwise returns one of three shapes
1145
+ // shared with `ssrFirstGroupHit`:
1146
+ // `{ fn, p }` — function hole that threw `NotReadyError`; `fn` is
1147
+ // wrapped in `runWithOwner(owner, ...)` so the streaming
1148
+ // engine's retry sees the same context the original sync
1149
+ // call did.
1150
+ // `{ merge }` — template object with non-empty `h`.
1151
+ // `{ bail }` — interior contains async; `bail` carries the evaluated
1152
+ // form (typically the array we walked) so the caller can
1153
+ // hand it to `resolveSSRNode` without re-invoking the
1154
+ // original closure. Re-invocation is unsafe — a hole may
1155
+ // read stateful getters such as JSX `props.children`
1156
+ // whose backing component rebuilds an owner subtree on
1157
+ // each access, producing a divergent hydration tree.
1158
+ function tryResolveString(node) {
1159
+ const t = typeof node;
1160
+ if (t === "string") return node;
1161
+ if (t === "number") return "" + node;
1162
+ if (node == null || t === "boolean") return "";
1163
+ if (t === "object") {
1164
+ if (Array.isArray(node)) {
1165
+ const joined = tryJoinPlainSSRArray(node);
1166
+ if (joined !== undefined) return joined;
1167
+ let s = "";
1168
+ let prevNonObj = false;
1169
+ for (let i = 0, len = node.length; i < len; i++) {
1170
+ const item = node[i];
1171
+ const itemNonObj = item !== null && typeof item !== "object";
1172
+ if (prevNonObj && itemNonObj) s += "<!--!$-->";
1173
+ prevNonObj = itemNonObj;
1174
+ const r = tryResolveString(item);
1175
+ if (typeof r !== "string") return { bail: node };
1176
+ s += r;
1177
+ }
1178
+ return s;
1179
+ }
1180
+ if (node.h && node.h.length > 0) return { merge: node };
1181
+ return Array.isArray(node.t) ? node.t[0] : node.t;
1182
+ }
1183
+ if (t === "function") {
1184
+ let v;
1185
+ try {
1186
+ v = node();
1187
+ } catch (err) {
1188
+ return buildAsyncWrap(err, node) || "";
1189
+ }
1190
+ // Recurse on the evaluated value. If recursion bails, propagate the
1191
+ // bail object unchanged — its `bail` field already carries the
1192
+ // deepest evaluated form, so the caller never re-invokes `node`.
1193
+ return tryResolveString(v);
1194
+ }
1195
+ return "";
1196
+ }
1197
+
1198
+ function resolveSSRNode(
1199
+ node,
1200
+ result = {
1201
+ t: [""],
1202
+ h: [],
1203
+ p: []
1204
+ },
1205
+ top
1206
+ ) {
1207
+ const t = typeof node;
1208
+ if (t === "string" || t === "number") {
1209
+ result.t[result.t.length - 1] += node;
1210
+ } else if (node == null || t === "boolean") {
1211
+ } else if (Array.isArray(node)) {
1212
+ let prevNonObj = false;
1213
+ for (let i = 0, len = node.length; i < len; i++) {
1214
+ const item = node[i];
1215
+ const itemNonObj = item !== null && typeof item !== "object";
1216
+ if (!top && prevNonObj && itemNonObj) result.t[result.t.length - 1] += `<!--!$-->`;
1217
+ prevNonObj = itemNonObj;
1218
+ resolveSSRNode(item, result);
1219
+ }
1220
+ } else if (t === "object") {
1221
+ if (node.h) {
1222
+ result.t[result.t.length - 1] += node.t[0];
1223
+ if (node.t.length > 1) {
1224
+ result.t.push(...node.t.slice(1));
1225
+ result.h.push(...node.h);
1226
+ result.p.push(...node.p);
1227
+ }
1228
+ } else result.t[result.t.length - 1] += node.t;
1229
+ } else if (t === "function") {
1230
+ try {
1231
+ resolveSSRNode(node(), result);
1232
+ } catch (err) {
1233
+ const wrap = buildAsyncWrap(err, node);
1234
+ if (wrap) {
1235
+ result.h.push(wrap.fn);
1236
+ result.p.push(wrap.p);
1237
+ result.t.push("");
1238
+ }
1239
+ }
1240
+ }
1241
+ return result;
1242
+ }
1243
+
1244
+ function resolveSSRSync(node) {
1245
+ const res = resolveSSRNode(node);
1246
+ if (!res.h.length) return res.t[0];
1247
+ throw new Error("This value cannot be rendered synchronously. Are you missing a boundary?");
1248
+ }
1249
+
1250
+ // experimental
1251
+ export const RequestContext = Symbol();
1252
+
1253
+ export function getRequestEvent() {
1254
+ return globalThis[RequestContext]
1255
+ ? globalThis[RequestContext].getStore() ||
1256
+ (sharedConfig.context && sharedConfig.context.event) ||
1257
+ console.warn(
1258
+ "RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls."
1259
+ )
1260
+ : undefined;
1261
+ }
1262
+
1263
+ /** @deprecated use renderToStream which also returns a promise */
1264
+ export function renderToStringAsync(code, options = {}) {
1265
+ return new Promise(resolve => renderToStream(code, options).then(resolve));
1266
+ }
1267
+
1268
+ // client-only APIs
1269
+
1270
+ export {
1271
+ notSup as style,
1272
+ notSup as insert,
1273
+ notSup as spread,
1274
+ notSup as delegateEvents,
1275
+ notSup as registerDelegatedRoot,
1276
+ notSup as unregisterDelegatedRoot,
1277
+ notSup as registerDelegatedContainer,
1278
+ notSup as unregisterDelegatedContainer,
1279
+ notSup as getDelegatedRoot,
1280
+ notSup as dynamicProperty,
1281
+ notSup as setAttribute,
1282
+ notSup as setAttributeNS,
1283
+ notSup as addEvent,
1284
+ notSup as render,
1285
+ notSup as template,
1286
+ notSup as setProperty,
1287
+ notSup as className,
1288
+ notSup as assign,
1289
+ notSup as hydrate,
1290
+ notSup as getNextElement,
1291
+ notSup as getNextMatch,
1292
+ notSup as getNextMarker,
1293
+ notSup as runHydrationEvents,
1294
+ notSup as ref,
1295
+ notSup as setStyleProperty
1296
+ };
1297
+
1298
+ function notSup() {
1299
+ throw new Error(
1300
+ "Client-only API called on the server side. Run client-only code in onMount, or conditionally run client-only component with <Show>."
1301
+ );
1302
+ }