@solidjs/web 2.0.0-beta.31 → 2.0.0-beta.33

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 (73) hide show
  1. package/README.md +1 -6
  2. package/dist/dev.cjs +285 -60
  3. package/dist/dev.js +267 -57
  4. package/dist/server.cjs +1148 -176
  5. package/dist/server.js +1133 -175
  6. package/dist/web.cjs +285 -60
  7. package/dist/web.js +267 -57
  8. package/frames/dist/client.cjs +397 -175
  9. package/frames/dist/client.dev.cjs +401 -175
  10. package/frames/dist/client.dev.js +399 -173
  11. package/frames/dist/client.js +395 -173
  12. package/frames/dist/server.cjs +1424 -277
  13. package/frames/dist/server.js +1426 -280
  14. package/package.json +69 -7
  15. package/serialization/decode/package.json +20 -0
  16. package/serialization/dist/decode.cjs +110 -0
  17. package/serialization/dist/decode.js +104 -0
  18. package/serialization/dist/serialization.cjs +106 -43
  19. package/serialization/dist/serialization.js +100 -44
  20. package/serialization/types/index.d.ts +85 -60
  21. package/serialization/types/serializer-decode.d.ts +182 -0
  22. package/serialization/types-cjs/index.d.cts +85 -60
  23. package/serialization/types-cjs/serializer-decode.d.cts +182 -0
  24. package/server-functions/dist/client.cjs +131 -98
  25. package/server-functions/dist/client.js +131 -99
  26. package/server-functions/dist/rich-args.cjs +11 -0
  27. package/server-functions/dist/rich-args.js +9 -0
  28. package/server-functions/dist/server.cjs +367 -194
  29. package/server-functions/dist/server.dev.cjs +1077 -0
  30. package/server-functions/dist/server.dev.js +1045 -0
  31. package/server-functions/dist/server.js +365 -195
  32. package/server-functions/package.json +10 -0
  33. package/server-functions/rich-args/package.json +20 -0
  34. package/storage/types/index.d.ts +1 -1
  35. package/storage/types-cjs/index.d.cts +1 -1
  36. package/types/client.d.ts +149 -6
  37. package/types/cookies.d.ts +93 -0
  38. package/types/core.d.ts +4 -2
  39. package/types/frames/client.d.ts +15 -1
  40. package/types/frames/frame-client.d.ts +63 -7
  41. package/types/frames/frame-sink.d.ts +26 -3
  42. package/types/frames/frame-transport.d.ts +40 -8
  43. package/types/frames/serializer.d.ts +85 -60
  44. package/types/frames/server.d.ts +22 -0
  45. package/types/index.d.ts +2 -3
  46. package/types/response.d.ts +45 -0
  47. package/types/serializer-decode.d.ts +182 -0
  48. package/types/serializer.d.ts +85 -60
  49. package/types/server-functions/client.d.ts +2 -1
  50. package/types/server-functions/rich-args.d.ts +10 -0
  51. package/types/server-functions/server.d.ts +99 -1
  52. package/types/server-functions/shared.d.ts +79 -1
  53. package/types/server-mock.d.ts +171 -59
  54. package/types/server.d.ts +209 -37
  55. package/types-cjs/client.d.cts +149 -6
  56. package/types-cjs/cookies.d.cts +93 -0
  57. package/types-cjs/core.d.cts +4 -2
  58. package/types-cjs/frames/client.d.cts +15 -1
  59. package/types-cjs/frames/frame-client.d.cts +63 -7
  60. package/types-cjs/frames/frame-sink.d.cts +26 -3
  61. package/types-cjs/frames/frame-transport.d.cts +40 -8
  62. package/types-cjs/frames/serializer.d.cts +85 -60
  63. package/types-cjs/frames/server.d.cts +22 -0
  64. package/types-cjs/index.d.cts +2 -3
  65. package/types-cjs/response.d.cts +45 -0
  66. package/types-cjs/serializer-decode.d.cts +182 -0
  67. package/types-cjs/serializer.d.cts +85 -60
  68. package/types-cjs/server-functions/client.d.cts +2 -1
  69. package/types-cjs/server-functions/rich-args.d.cts +10 -0
  70. package/types-cjs/server-functions/server.d.cts +99 -1
  71. package/types-cjs/server-functions/shared.d.cts +79 -1
  72. package/types-cjs/server-mock.d.cts +171 -59
  73. package/types-cjs/server.d.cts +209 -37
@@ -1,20 +1,130 @@
1
- import { runWithOwner, createOwner, sharedConfig, createRoot, ssrHandleError, getOwner, NoHydration, runInServerComponentScope, Hydration } from 'solid-js';
2
- import { toCrossJSONStream, Feature, Serializer, getCrossReferenceHeader, createPlugin } from 'seroval';
1
+ import { createMemo, runWithOwner, createOwner, sharedConfig, createRoot, ssrHandleError, getOwner, inServerComponentScope, creationStamp, NoHydration, runInServerComponentScope, Hydration, getProjectionTrace } from 'solid-js';
2
+ import { Feature, toCrossJSONStream, Serializer, getCrossReferenceHeader } from 'seroval';
3
3
  import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
4
4
 
5
+ const STATE = Symbol.for("dom-expressions.container-trace-state");
6
+ const state = globalThis[STATE] || (globalThis[STATE] = {
7
+ materialized: new WeakMap(),
8
+ materializedValues: new WeakSet()
9
+ });
10
+ function setContainerTraceResolver(fn) {
11
+ state.resolveTrace = fn;
12
+ }
13
+ function isContainerTraced(value) {
14
+ const resolve = state.resolveTrace;
15
+ return !!(resolve && value && typeof value === "object" && resolve(value));
16
+ }
17
+ const TRACE = Symbol.for("dom-expressions.container-trace");
18
+ function envelopeContainerTraces(value) {
19
+ if (!state.resolveTrace || value == null || typeof value !== "object") return value;
20
+ const trace = state.resolveTrace(value);
21
+ if (trace) return {
22
+ [TRACE]: trace
23
+ };
24
+ if (Array.isArray(value)) {
25
+ let out = value;
26
+ for (let i = 0; i < value.length; i++) {
27
+ const next = envelopeContainerTraces(value[i]);
28
+ if (next !== value[i]) {
29
+ if (out === value) out = value.slice();
30
+ out[i] = next;
31
+ }
32
+ }
33
+ return out;
34
+ }
35
+ if (Object.getPrototypeOf(value) === Object.prototype) {
36
+ let out = value;
37
+ for (const key of Object.keys(value)) {
38
+ const next = envelopeContainerTraces(value[key]);
39
+ if (next !== value[key]) {
40
+ if (out === value) out = {
41
+ ...value
42
+ };
43
+ out[key] = next;
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+ return value;
49
+ }
50
+ function materialize(marker) {
51
+ let value = state.materialized.get(marker.$tr);
52
+ if (value === undefined) {
53
+ value = state.materializeTrace(marker);
54
+ state.materialized.set(marker.$tr, value);
55
+ if (value !== null && typeof value === "object") state.materializedValues.add(value);
56
+ }
57
+ return value;
58
+ }
59
+ function parseTrace(value, ctx) {
60
+ const trace = value[TRACE];
61
+ return {
62
+ a: trace.array ? 1 : 0,
63
+ i: ctx.parse(trace.subscribe())
64
+ };
65
+ }
66
+ const ContainerTracePlugin = {
67
+ tag: "dom-expressions/container-trace",
68
+ test(value) {
69
+ return value != null && typeof value === "object" && TRACE in value;
70
+ },
71
+ parse: {
72
+ sync() {
73
+ throw new Error("A reactive container can only be serialized by a streaming serializer.");
74
+ },
75
+ async async(value, ctx) {
76
+ const trace = value[TRACE];
77
+ return {
78
+ a: trace.array ? 1 : 0,
79
+ i: await ctx.parse(trace.subscribe())
80
+ };
81
+ },
82
+ stream: parseTrace
83
+ },
84
+ serialize(node, ctx) {
85
+ return "{$tr:" + ctx.serialize(node.i) + ",$ta:" + node.a + "}";
86
+ },
87
+ deserialize(node, ctx) {
88
+ const iterable = ctx.deserialize(node.i);
89
+ const marker = {
90
+ $tr: iterable,
91
+ $ta: node.a
92
+ };
93
+ return state.materializeTrace ? materialize(marker) : marker;
94
+ }
95
+ };
96
+
5
97
  const runWithHydrationScope = (id, fn) => runWithOwner(createOwner({
6
98
  id
7
99
  }), fn);
100
+ const ssrAsyncValue = value => createMemo(() => value, {
101
+ serialize: false
102
+ });
8
103
 
9
- const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
10
- const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
11
- const HYDRATION_GLOBAL = "_$HY.r";
12
104
  const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
13
105
  CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
14
- FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
106
+ FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin,
107
+ ContainerTracePlugin]);
15
108
  function resolveSerializerPlugins(customPlugins) {
16
109
  return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
17
110
  }
111
+ const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
112
+ const JSON_CODEC_DEPTH_LIMIT = 64;
113
+ function resolveCodecOptions({
114
+ plugins,
115
+ disabledFeatures,
116
+ depthLimit
117
+ } = {}) {
118
+ return {
119
+ plugins: resolveSerializerPlugins(plugins),
120
+ disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
121
+ depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
122
+ };
123
+ }
124
+
125
+ const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
126
+ const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
127
+ const HYDRATION_GLOBAL = "_$HY.r";
18
128
  function createSerializer(options) {
19
129
  return new Serializer({
20
130
  ...options,
@@ -41,34 +151,6 @@ function createHydrationSerializer({
41
151
  function getLocalHeaderScript(id) {
42
152
  return getCrossReferenceHeader(id) + ";";
43
153
  }
44
- const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
45
- const JSON_CODEC_DEPTH_LIMIT = 64;
46
- function resolveCodecOptions({
47
- plugins,
48
- disabledFeatures,
49
- depthLimit
50
- } = {}) {
51
- return {
52
- plugins: resolveSerializerPlugins(plugins),
53
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
54
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
55
- };
56
- }
57
- function serializeJSON(value, {
58
- onParse,
59
- onDone,
60
- onError,
61
- ...codecOptions
62
- }) {
63
- const resolved = resolveCodecOptions(codecOptions);
64
- return toCrossJSONStream(value, {
65
- onParse,
66
- onDone,
67
- onError,
68
- ...resolved,
69
- disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
70
- });
71
- }
72
154
  function createJSONSerializer({
73
155
  onData,
74
156
  onDone,
@@ -135,6 +217,146 @@ function createJSONSerializer({
135
217
  };
136
218
  }
137
219
 
220
+ const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
221
+ function isResponseEnvelope(value) {
222
+ return !!(value && typeof value === "object" && value[ENVELOPE]);
223
+ }
224
+ const REVALIDATE_HEADER = "X-Revalidate";
225
+
226
+ function frameAddress(id, args) {
227
+ return args && args.length ? id + ":" + hashArguments(args) : id;
228
+ }
229
+ function hashArguments(args) {
230
+ let hash = 0;
231
+ const text = stableString(args);
232
+ for (let i = 0; i < text.length; i++) {
233
+ hash = (hash << 5) - hash + text.charCodeAt(i);
234
+ hash |= 0;
235
+ }
236
+ return (hash >>> 0).toString(36);
237
+ }
238
+ function stableString(value, seen) {
239
+ if (value === null || typeof value !== "object") {
240
+ return typeof value === "bigint" ? value + "n" : String(value);
241
+ }
242
+ if (value instanceof Date) return "Date:" + value.getTime();
243
+ seen || (seen = new Set());
244
+ if (seen.has(value)) return "~";
245
+ seen.add(value);
246
+ if (value instanceof Map) {
247
+ const entries = [];
248
+ for (const [k, v] of value) {
249
+ entries.push(stableString(k, seen) + "=>" + stableString(v, seen));
250
+ }
251
+ return "Map{" + entries.sort().join(",") + "}";
252
+ }
253
+ if (value instanceof Set) {
254
+ const members = [];
255
+ for (const v of value) members.push(stableString(v, seen));
256
+ return "Set{" + members.sort().join(",") + "}";
257
+ }
258
+ if (Array.isArray(value)) {
259
+ let out = "[";
260
+ for (let i = 0; i < value.length; i++) out += (i ? "," : "") + stableString(value[i], seen);
261
+ return out + "]";
262
+ }
263
+ const keys = Object.keys(value).sort();
264
+ let out = "{";
265
+ for (let i = 0; i < keys.length; i++) {
266
+ out += (i ? "," : "") + keys[i] + ":" + stableString(value[keys[i]], seen);
267
+ }
268
+ return out + "}";
269
+ }
270
+ const ERROR_HEADER = "X-Server-Function-Error";
271
+ const BODY_FORMAT_HEADER = "X-Server-Function-Format";
272
+ const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
273
+ function createChunk(data) {
274
+ const encoder = new TextEncoder();
275
+ const encodeData = encoder.encode(data);
276
+ const bytes = encodeData.length;
277
+ const chunk = new Uint8Array(12 + bytes);
278
+ chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
279
+ chunk.set(encodeData, 12);
280
+ return chunk;
281
+ }
282
+ class ChunkReader {
283
+ constructor(stream) {
284
+ this.reader = stream.getReader();
285
+ this.buffer = new Uint8Array(0);
286
+ this.done = false;
287
+ }
288
+ async readChunk() {
289
+ const chunk = await this.reader.read();
290
+ if (!chunk.done) {
291
+ const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
292
+ newBuffer.set(this.buffer);
293
+ newBuffer.set(chunk.value, this.buffer.length);
294
+ this.buffer = newBuffer;
295
+ } else {
296
+ this.done = true;
297
+ }
298
+ }
299
+ async next() {
300
+ while (this.buffer.length < 12) {
301
+ if (this.done) {
302
+ if (this.buffer.length === 0) return {
303
+ done: true,
304
+ value: undefined
305
+ };
306
+ throw new Error("Malformed server function stream.");
307
+ }
308
+ await this.readChunk();
309
+ }
310
+ const decoder = new TextDecoder();
311
+ const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
312
+ if (Number.isNaN(bytes)) {
313
+ throw new Error("Malformed server function stream.");
314
+ }
315
+ while (bytes > this.buffer.length - 12) {
316
+ if (this.done) {
317
+ throw new Error("Malformed server function stream.");
318
+ }
319
+ await this.readChunk();
320
+ }
321
+ const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
322
+ this.buffer = this.buffer.subarray(12 + bytes);
323
+ return {
324
+ done: false,
325
+ value: partial
326
+ };
327
+ }
328
+ async drain(interpret) {
329
+ while (true) {
330
+ const result = await this.next();
331
+ if (result.done) {
332
+ break;
333
+ }
334
+ interpret(result.value);
335
+ }
336
+ }
337
+ }
338
+ function serializeStream(value, codecOptions) {
339
+ return new ReadableStream({
340
+ async start(controller) {
341
+ const {
342
+ serializeJSON
343
+ } = await import('@solidjs/web/serialization');
344
+ serializeJSON(value, {
345
+ ...codecOptions,
346
+ onParse(node) {
347
+ controller.enqueue(createChunk(JSON.stringify(node)));
348
+ },
349
+ onDone() {
350
+ controller.close();
351
+ },
352
+ onError(error) {
353
+ controller.error(error);
354
+ }
355
+ });
356
+ }
357
+ });
358
+ }
359
+
138
360
  const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
139
361
  const HEAD_ATTR_NAME = /^[a-zA-Z_][a-zA-Z0-9_:.-]*$/;
140
362
  const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "stylesheet"]);
@@ -205,7 +427,6 @@ function resolveHead(groups) {
205
427
  }
206
428
  for (const [identity, tags] of byIdentity) {
207
429
  if (identity === "title") {
208
- if (tags.length > 1) console.warn("Multiple <title> tags in one head group; the last one wins.");
209
430
  winners.set(identity, {
210
431
  seq: group.seq,
211
432
  tags: [tags[tags.length - 1]]
@@ -375,7 +596,8 @@ function createHeadRegistry() {
375
596
  resources: new Set(),
376
597
  eagerHtml: "",
377
598
  flushed: null,
378
- shellFlushed: false
599
+ shellFlushed: false,
600
+ parkedResources: []
379
601
  };
380
602
  }
381
603
  function registerHeadTags(registry, context, tracking, emitResource, nonce, tags) {
@@ -389,14 +611,24 @@ function registerHeadTags(registry, context, tracking, emitResource, nonce, tags
389
611
  return;
390
612
  }
391
613
  if (!Array.isArray(tags)) tags = [tags];
614
+ const probe = sharedConfig.context && sharedConfig.context._loadingPhase;
392
615
  let replaceable = null;
393
616
  for (let i = 0; i < tags.length; i++) {
394
617
  const desc = tags[i];
395
618
  if (!desc || !HEAD_ELIGIBLE_TAGS.has(desc.tag)) {
396
- console.warn(`useHead: ignoring non-head tag`, desc);
397
619
  continue;
398
620
  }
399
621
  const cls = classifyHeadTag(desc);
622
+ if (probe && !cls.resource) {
623
+ try {
624
+ evalHeadProps(desc.props || {}, cls.rel !== undefined ? {
625
+ rel: cls.rel
626
+ } : undefined);
627
+ evalHeadValue(desc.key);
628
+ } catch (err) {
629
+ if (ssrHandleError(err)) throw err;
630
+ }
631
+ }
400
632
  if (cls.resource) {
401
633
  emitHeadResource(registry, context, tracking, emitResource, nonce, desc, cls.rel);
402
634
  } else {
@@ -413,6 +645,51 @@ function registerHeadTags(registry, context, tracking, emitResource, nonce, tags
413
645
  tags: replaceable
414
646
  });
415
647
  }
648
+ function headShellReady(registry, block) {
649
+ let ready = true;
650
+ const pends = err => {
651
+ const source = ssrHandleError(err, true);
652
+ if (!source) return false;
653
+ block(source);
654
+ ready = false;
655
+ return true;
656
+ };
657
+ const parked = registry.parkedResources;
658
+ for (let i = parked.length - 1; i >= 0; i--) {
659
+ const {
660
+ desc,
661
+ rel,
662
+ emit
663
+ } = parked[i];
664
+ try {
665
+ evalHeadProps(desc.props || {}, rel !== undefined ? {
666
+ rel
667
+ } : undefined);
668
+ } catch (err) {
669
+ if (pends(err)) continue;
670
+ parked.splice(i, 1);
671
+ continue;
672
+ }
673
+ parked.splice(i, 1);
674
+ emit();
675
+ }
676
+ for (let i = 0; i < registry.pending.length; i++) {
677
+ const reg = registry.pending[i];
678
+ if (reg.boundary !== "" || reg.list) continue;
679
+ for (let j = 0; j < reg.tags.length; j++) {
680
+ const desc = reg.tags[j];
681
+ try {
682
+ evalHeadProps(desc.props || {}, desc.rel !== undefined ? {
683
+ rel: desc.rel
684
+ } : undefined);
685
+ evalHeadValue(desc.key);
686
+ } catch (err) {
687
+ pends(err);
688
+ }
689
+ }
690
+ }
691
+ return ready;
692
+ }
416
693
  function emitHeadResource(registry, context, tracking, emitResource, nonce, desc, rel) {
417
694
  let props;
418
695
  try {
@@ -420,7 +697,16 @@ function emitHeadResource(registry, context, tracking, emitResource, nonce, desc
420
697
  rel
421
698
  } : undefined);
422
699
  } catch (err) {
423
- console.warn(`useHead: error evaluating resource tag props`, err);
700
+ const loadingPhase = sharedConfig.context && sharedConfig.context._loadingPhase;
701
+ if (loadingPhase && ssrHandleError(err)) throw err;
702
+ if (!loadingPhase && !context._currentBoundaryId && !registry.shellFlushed && typeof context.block === "function" && ssrHandleError(err, true)) {
703
+ registry.parkedResources.push({
704
+ desc,
705
+ rel,
706
+ emit: () => emitHeadResource(registry, context, tracking, emitResource, nonce, desc, rel)
707
+ });
708
+ return;
709
+ }
424
710
  return;
425
711
  }
426
712
  const identity = resourceIdentity(desc.tag, props);
@@ -482,7 +768,6 @@ function commitHeadBoundary(registry, boundary, isPendingFragment) {
482
768
  try {
483
769
  resolved = reg.list();
484
770
  } catch (err) {
485
- console.warn(`useHead: error evaluating head group membership`, err);
486
771
  continue;
487
772
  }
488
773
  if (!Array.isArray(resolved)) resolved = [resolved];
@@ -490,7 +775,6 @@ function commitHeadBoundary(registry, boundary, isPendingFragment) {
490
775
  for (let j = 0; j < resolved.length; j++) {
491
776
  const desc = resolved[j];
492
777
  if (!desc || !HEAD_ELIGIBLE_TAGS.has(desc.tag)) {
493
- console.warn(`useHead: ignoring non-head tag`, desc);
494
778
  continue;
495
779
  }
496
780
  const cls = classifyHeadTag(desc);
@@ -516,12 +800,10 @@ function commitHeadBoundary(registry, boundary, isPendingFragment) {
516
800
  } : undefined);
517
801
  key = evalHeadValue(desc.key);
518
802
  } catch (err) {
519
- console.warn(`useHead: error evaluating tag props`, err);
520
803
  continue;
521
804
  }
522
805
  const identity = replaceableIdentity(desc.tag, props, key, "u:" + registry.uniq++);
523
806
  if ((identity === "base" || identity === "charset") && registry.shellFlushed) {
524
- console.warn(`useHead: <${desc.tag}> (${identity}) registered after shell flush is ignored`);
525
807
  continue;
526
808
  }
527
809
  tags.push({
@@ -603,7 +885,6 @@ function flushHeadFragment(registry, boundary) {
603
885
  for (const name in t.props) {
604
886
  if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
605
887
  if (!HEAD_ATTR_NAME.test(name)) {
606
- console.warn(`useHead: ignoring invalid attribute name "${name}"`);
607
888
  continue;
608
889
  }
609
890
  const v = t.props[name];
@@ -621,7 +902,6 @@ function renderHeadAttrHtml(props) {
621
902
  for (const name in props) {
622
903
  if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
623
904
  if (!HEAD_ATTR_NAME.test(name)) {
624
- console.warn(`useHead: ignoring invalid attribute name "${name}"`);
625
905
  continue;
626
906
  }
627
907
  const v = props[name];
@@ -681,6 +961,12 @@ function renderToStream(code, options = {}) {
681
961
  d();
682
962
  }
683
963
  };
964
+ const failRender = err => {
965
+ try {
966
+ options.onError ? options.onError(err) : console.error(err);
967
+ } catch (_) {}
968
+ abandon();
969
+ };
684
970
  const guardSink = w => ({
685
971
  write(payload) {
686
972
  if (dead) return;
@@ -762,7 +1048,7 @@ function renderToStream(code, options = {}) {
762
1048
  }
763
1049
  },
764
1050
  shell(shellHtml, meta) {
765
- buffer.write(assembleDocument(shellHtml, meta.assets, meta.preloads, meta.inlineStyles, meta.tasks.length ? meta.tasks : "", nonce, meta.head, onHead));
1051
+ buffer.write(assembleDocument(shellHtml, meta.preloads, meta.inlineStyles, meta.tasks.length ? meta.tasks : "", nonce, meta.head, onHead));
766
1052
  },
767
1053
  ...options.sink
768
1054
  };
@@ -779,10 +1065,18 @@ function renderToStream(code, options = {}) {
779
1065
  rootAssetsSerialized = true;
780
1066
  serializeFragmentAssets("", tracking.boundaryModules, context);
781
1067
  };
1068
+ let holds = 0;
782
1069
  const flushEnd = () => {
783
- if (!registry.size) {
1070
+ if (!registry.size && !holds) {
784
1071
  serializeRootAssets();
785
- queue(() => queue(() => serializer.flush()));
1072
+ queue(() => queue(() => {
1073
+ if (context.live.end) {
1074
+ const end = context.live.end;
1075
+ context.live.end = null;
1076
+ end();
1077
+ }
1078
+ serializer.flush();
1079
+ }));
786
1080
  }
787
1081
  };
788
1082
  const registry = new Map();
@@ -842,8 +1136,8 @@ function renderToStream(code, options = {}) {
842
1136
  };
843
1137
  sharedConfig.context = context = {
844
1138
  async: true,
845
- assets: [],
846
1139
  nonce,
1140
+ live: {},
847
1141
  registerHeadTags(tags) {
848
1142
  registerHeadTags(headRegistry, context, tracking,
849
1143
  (markup, gateEntry) => {
@@ -880,6 +1174,16 @@ function renderToStream(code, options = {}) {
880
1174
  block(p) {
881
1175
  if (!firstFlushed) blockingPromises.add(p);
882
1176
  },
1177
+ hold() {
1178
+ holds++;
1179
+ let released = false;
1180
+ return () => {
1181
+ if (released) return;
1182
+ released = true;
1183
+ holds--;
1184
+ if (!holds) queue(flushEnd);
1185
+ };
1186
+ },
883
1187
  replace(id, payloadFn) {
884
1188
  if (firstFlushed) return;
885
1189
  const placeholder = `<!--!$${id}-->`;
@@ -988,6 +1292,7 @@ function renderToStream(code, options = {}) {
988
1292
  }
989
1293
  };
990
1294
  applyAssetTracking(context, tracking, manifest, noScripts);
1295
+ context.failRender = failRender;
991
1296
  registerEntryAssets(manifest);
992
1297
  let html = createRoot(d => {
993
1298
  dispose = d;
@@ -1044,7 +1349,7 @@ function renderToStream(code, options = {}) {
1044
1349
  if (shellCompleted) return;
1045
1350
  sharedConfig.context = context;
1046
1351
  if (!resolveRootHoles()) return;
1047
- const assetsHtml = resolveAssetsHtml(context.assets);
1352
+ if (!headShellReady(headRegistry, p => blockingPromises.add(p))) return;
1048
1353
  headStyles = new Set();
1049
1354
  for (const url of tracking.emittedAssets) {
1050
1355
  if (isCssUrl(url)) headStyles.add(url);
@@ -1052,7 +1357,6 @@ function renderToStream(code, options = {}) {
1052
1357
  serializeRootAssets();
1053
1358
  const head = renderShellHead(headRegistry, nonce, k => registry.has(k));
1054
1359
  sink.shell(html, {
1055
- assets: assetsHtml,
1056
1360
  preloads: tracking.emittedAssets,
1057
1361
  inlineStyles: tracking.inlineStyles,
1058
1362
  tasks,
@@ -1101,7 +1405,12 @@ function renderToStream(code, options = {}) {
1101
1405
  allSettled(blockingPromises).then(() => {
1102
1406
  scheduleFlush(() => {
1103
1407
  if (dead) return resolve();
1104
- doShell();
1408
+ try {
1409
+ doShell();
1410
+ } catch (err) {
1411
+ failRender(err);
1412
+ return resolve();
1413
+ }
1105
1414
  if (!shellCompleted) return flush();
1106
1415
  const encoder = new TextEncoder();
1107
1416
  const writer = w.getWriter();
@@ -1142,27 +1451,35 @@ function renderToStream(code, options = {}) {
1142
1451
  return p;
1143
1452
  };
1144
1453
  return {
1145
- then(fn) {
1146
- function complete() {
1147
- dispose();
1148
- fn(tmp);
1149
- }
1150
- if (onCompleteAll) {
1151
- let ogComplete = onCompleteAll;
1152
- onCompleteAll = options => {
1153
- ogComplete(options);
1154
- complete();
1155
- };
1156
- } else onCompleteAll = complete;
1157
- function flush() {
1158
- allSettled(blockingPromises).then(() => {
1159
- scheduleFlush(() => {
1160
- if (!resolveRootHoles()) return flush();
1161
- queue(flushEnd);
1454
+ then(onFulfilled, onRejected) {
1455
+ const p = new Promise(resolve => {
1456
+ function complete() {
1457
+ dispose();
1458
+ resolve(tmp);
1459
+ }
1460
+ if (onCompleteAll) {
1461
+ let ogComplete = onCompleteAll;
1462
+ onCompleteAll = options => {
1463
+ ogComplete(options);
1464
+ complete();
1465
+ };
1466
+ } else onCompleteAll = complete;
1467
+ function flush() {
1468
+ allSettled(blockingPromises).then(() => {
1469
+ scheduleFlush(() => {
1470
+ try {
1471
+ if (!resolveRootHoles() || !headShellReady(headRegistry, p => blockingPromises.add(p))) return flush();
1472
+ } catch (err) {
1473
+ failRender(err);
1474
+ return resolve(tmp);
1475
+ }
1476
+ queue(flushEnd);
1477
+ });
1162
1478
  });
1163
- });
1164
- }
1165
- flush();
1479
+ }
1480
+ flush();
1481
+ });
1482
+ return p.then(onFulfilled, onRejected);
1166
1483
  },
1167
1484
  pipe(w) {
1168
1485
  claimConsumer("pipe");
@@ -1170,7 +1487,15 @@ function renderToStream(code, options = {}) {
1170
1487
  allSettled(blockingPromises).then(() => {
1171
1488
  scheduleFlush(() => {
1172
1489
  if (dead) return;
1173
- doShell();
1490
+ try {
1491
+ doShell();
1492
+ } catch (err) {
1493
+ failRender(err);
1494
+ try {
1495
+ w.end();
1496
+ } catch (_) {}
1497
+ return;
1498
+ }
1174
1499
  if (!shellCompleted) return flush();
1175
1500
  buffer = writable = guardSink(w);
1176
1501
  buffer.write(tmp);
@@ -1202,9 +1527,27 @@ function renderToStream(code, options = {}) {
1202
1527
  function buildAsyncWrap(err, node) {
1203
1528
  const p = ssrHandleError(err);
1204
1529
  if (!p) return null;
1530
+ if (node.$rw) return {
1531
+ fn: node,
1532
+ p
1533
+ };
1205
1534
  const owner = getOwner();
1535
+ const live = sharedConfig.context && sharedConfig.context.liveHoles;
1536
+ const suppress = node.$lhSuppress || live && (live.suppressed || live.sweeping);
1537
+ if (!owner) {
1538
+ if (suppress) node.$lhSuppress = true;
1539
+ return {
1540
+ fn: node,
1541
+ p
1542
+ };
1543
+ }
1544
+ const fn = () => runWithOwner(owner, node);
1545
+ fn.$rw = true;
1546
+ if (suppress) fn.$lhSuppress = true;
1547
+ if (node.$lhSkip) fn.$lhSkip = true;
1548
+ if (node.$lhBinding) fn.$lhBinding = node.$lhBinding;
1206
1549
  return {
1207
- fn: owner ? () => runWithOwner(owner, node) : node,
1550
+ fn,
1208
1551
  p
1209
1552
  };
1210
1553
  }
@@ -1264,6 +1607,326 @@ function ssrGroupSlot(fn, idx) {
1264
1607
  }
1265
1608
  };
1266
1609
  }
1610
+ const holePositionCache = new WeakMap();
1611
+ function holeContentPositions(t) {
1612
+ let cached = holePositionCache.get(t);
1613
+ if (cached) return cached;
1614
+ const pos = [];
1615
+ const openOff = [];
1616
+ const closeOff = [];
1617
+ let inTag = false;
1618
+ let quote = "";
1619
+ let curOpen = null;
1620
+ let scanningName = false;
1621
+ for (let i = 0; i < t.length; i++) {
1622
+ const seg = t[i];
1623
+ let close = -1;
1624
+ const openAtStart = inTag;
1625
+ let reopened = false;
1626
+ for (let j = 0; j < seg.length; j++) {
1627
+ const ch = seg[j];
1628
+ if (quote) {
1629
+ if (ch === quote) quote = "";
1630
+ } else if (inTag) {
1631
+ if (scanningName && (ch === " " || ch === "\t" || ch === "\n" || ch === ">" || ch === "/")) {
1632
+ scanningName = false;
1633
+ curOpen = {
1634
+ seg: i,
1635
+ off: j
1636
+ };
1637
+ }
1638
+ if (ch === '"' || ch === "'") quote = ch;else if (ch === ">") {
1639
+ inTag = false;
1640
+ if (openAtStart && !reopened && close === -1) close = j;
1641
+ curOpen = null;
1642
+ }
1643
+ } else if (ch === "<") {
1644
+ inTag = true;
1645
+ scanningName = true;
1646
+ reopened = true;
1647
+ curOpen = null;
1648
+ }
1649
+ }
1650
+ if (inTag && scanningName) {
1651
+ scanningName = false;
1652
+ curOpen = {
1653
+ seg: i,
1654
+ off: seg.length
1655
+ };
1656
+ }
1657
+ closeOff.push(close);
1658
+ openOff.push(inTag && curOpen && curOpen.seg === i ? curOpen.off : -1);
1659
+ if (i < t.length - 1) pos.push(!inTag);
1660
+ }
1661
+ cached = {
1662
+ pos,
1663
+ openOff,
1664
+ closeOff
1665
+ };
1666
+ holePositionCache.set(t, cached);
1667
+ return cached;
1668
+ }
1669
+ function createLiveHoles(sink, scoped) {
1670
+ let nextId = 0;
1671
+ const stamp = typeof creationStamp === "function" ? creationStamp : () => 0;
1672
+ const inScope = scoped && typeof inServerComponentScope === "function" ? inServerComponentScope : null;
1673
+ const stripMarkers = html => html.replace(/<!--lh:\/?\d+-->/g, "");
1674
+ function resolveHoleValue(hole) {
1675
+ const result = {
1676
+ t: [""],
1677
+ h: [],
1678
+ p: []
1679
+ };
1680
+ try {
1681
+ resolveSSRNode(hole(), result);
1682
+ } catch (err) {
1683
+ const wrap = buildAsyncWrap(err, hole);
1684
+ if (!wrap) throw err;
1685
+ result.h.push(wrap.fn);
1686
+ result.p.push(wrap.p);
1687
+ result.t.push("");
1688
+ }
1689
+ return result;
1690
+ }
1691
+ function closeChildren(b) {
1692
+ for (const c of b.children) {
1693
+ c.closed = true;
1694
+ if (c.key) sink.closeBinding(c.key);
1695
+ closeChildren(c);
1696
+ }
1697
+ b.children.length = 0;
1698
+ }
1699
+ const engine = {
1700
+ suppressed: 0,
1701
+ sweeping: false,
1702
+ parent: null,
1703
+ recordStamp: 0,
1704
+ gateHit: false,
1705
+ content(hole) {
1706
+ if (hole.$lhSuppress) {
1707
+ const r = {
1708
+ t: [""],
1709
+ h: [],
1710
+ p: []
1711
+ };
1712
+ engine.suppressed++;
1713
+ try {
1714
+ try {
1715
+ resolveSSRNode(hole(), r);
1716
+ } catch (err) {
1717
+ const wrap = buildAsyncWrap(err, hole);
1718
+ if (wrap) {
1719
+ r.h.push(wrap.fn);
1720
+ r.p.push(wrap.p);
1721
+ r.t.push("");
1722
+ }
1723
+ }
1724
+ } finally {
1725
+ engine.suppressed--;
1726
+ }
1727
+ const b = hole.$lhBinding;
1728
+ if (b && !b.closed && !r.h.length && !engine.sweeping) {
1729
+ b.last = stripMarkers(r.t[0]);
1730
+ }
1731
+ return r.h.length ? r : r.t[0];
1732
+ }
1733
+ if (engine.suppressed || engine.sweeping) return null;
1734
+ if (hole.$lhSkip) return null;
1735
+ if (inScope && !inScope()) return null;
1736
+ const recordsBefore = engine.recordStamp;
1737
+ const ownersBefore = stamp();
1738
+ const owner = getOwner();
1739
+ const b = {
1740
+ key: null,
1741
+ children: [],
1742
+ last: null,
1743
+ closed: false,
1744
+ sweep() {
1745
+ if (b.closed) return;
1746
+ const prevSweeping = engine.sweeping;
1747
+ engine.sweeping = true;
1748
+ engine.gateHit = false;
1749
+ const sweepOwnersBefore = stamp();
1750
+ let res;
1751
+ try {
1752
+ res = owner ? runWithOwner(owner, () => resolveHoleValue(hole)) : resolveHoleValue(hole);
1753
+ } catch (err) {
1754
+ b.closed = true;
1755
+ sink.closeBinding(b.key);
1756
+ sink.error(b.key, String(err && err.message || err));
1757
+ return;
1758
+ } finally {
1759
+ engine.sweeping = prevSweeping;
1760
+ }
1761
+ if (engine.gateHit || stamp() !== sweepOwnersBefore) {
1762
+ b.closed = true;
1763
+ sink.closeBinding(b.key);
1764
+ return;
1765
+ }
1766
+ if (res.h.length) return;
1767
+ const html = res.t[0];
1768
+ if (b.last === null || html === b.last) return;
1769
+ b.last = html;
1770
+ closeChildren(b);
1771
+ sink.hole(b.key, html);
1772
+ }
1773
+ };
1774
+ if (engine.parent) engine.parent.children.push(b);
1775
+ const prevParent = engine.parent;
1776
+ engine.parent = b;
1777
+ let value;
1778
+ let escalated = null;
1779
+ try {
1780
+ value = hole();
1781
+ } catch (err) {
1782
+ const wrap = buildAsyncWrap(err, hole);
1783
+ if (!wrap) {
1784
+ engine.parent = prevParent;
1785
+ closeChildren(b);
1786
+ return "";
1787
+ }
1788
+ escalated = wrap;
1789
+ }
1790
+ if (!escalated && (typeof value === "function" && value.$lhSkip || value !== null && typeof value === "object" && value.$slot)) {
1791
+ engine.parent = prevParent;
1792
+ const r = {
1793
+ t: [""],
1794
+ h: [],
1795
+ p: []
1796
+ };
1797
+ engine.suppressed++;
1798
+ try {
1799
+ resolveSSRNode(value, r);
1800
+ } finally {
1801
+ engine.suppressed--;
1802
+ }
1803
+ return r.h.length ? r : r.t[0];
1804
+ }
1805
+ let res;
1806
+ if (escalated) {
1807
+ closeChildren(b);
1808
+ escalated.fn.$lhSuppress = true;
1809
+ escalated.fn.$lhBinding = b;
1810
+ res = {
1811
+ t: ["", ""],
1812
+ h: [escalated.fn],
1813
+ p: [escalated.p]
1814
+ };
1815
+ } else {
1816
+ res = {
1817
+ t: [""],
1818
+ h: [],
1819
+ p: []
1820
+ };
1821
+ try {
1822
+ resolveSSRNode(value, res);
1823
+ } catch (_) {
1824
+ engine.parent = prevParent;
1825
+ closeChildren(b);
1826
+ return "";
1827
+ }
1828
+ }
1829
+ engine.parent = prevParent;
1830
+ if (engine.recordStamp !== recordsBefore || stamp() !== ownersBefore) {
1831
+ return res.h.length ? res : res.t[0];
1832
+ }
1833
+ const id = nextId++;
1834
+ const key = b.key = "lh:" + id;
1835
+ const open = `<!--lh:${id}-->`;
1836
+ const close = `<!--lh:/${id}-->`;
1837
+ if (!res.h.length) {
1838
+ b.last = stripMarkers(res.t[0]);
1839
+ sink.openBinding(key, b);
1840
+ return open + res.t[0] + close;
1841
+ }
1842
+ const t = res.t.slice();
1843
+ t[0] = open + t[0];
1844
+ t[t.length - 1] += close;
1845
+ sink.openBinding(key, b);
1846
+ return {
1847
+ t,
1848
+ h: res.h,
1849
+ p: res.p
1850
+ };
1851
+ },
1852
+ mint() {
1853
+ return nextId++;
1854
+ },
1855
+ active() {
1856
+ return !inScope || inScope();
1857
+ },
1858
+ attr(cap) {
1859
+ const owner = getOwner();
1860
+ const b = {
1861
+ key: "lha:" + cap.id,
1862
+ children: [],
1863
+ last: cap.base,
1864
+ closed: false,
1865
+ sweep() {
1866
+ if (b.closed) return;
1867
+ const prevSweeping = engine.sweeping;
1868
+ engine.sweeping = true;
1869
+ engine.gateHit = false;
1870
+ const sweepOwnersBefore = stamp();
1871
+ let html = "";
1872
+ try {
1873
+ let group = null;
1874
+ let groupVal = null;
1875
+ const run = () => {
1876
+ for (const part of cap.parts) {
1877
+ if (typeof part === "string") {
1878
+ html += part;
1879
+ continue;
1880
+ }
1881
+ let v;
1882
+ if (part.g) {
1883
+ if (group !== part.g) {
1884
+ group = part.g;
1885
+ groupVal = part.g();
1886
+ }
1887
+ v = groupVal[part.i];
1888
+ } else {
1889
+ v = part.f();
1890
+ }
1891
+ const vt = typeof v;
1892
+ if (vt === "string" || vt === "number") html += v;
1893
+ }
1894
+ };
1895
+ owner ? runWithOwner(owner, run) : run();
1896
+ } catch (err) {
1897
+ if (ssrHandleError(err, true)) return;
1898
+ b.closed = true;
1899
+ sink.closeBinding(b.key);
1900
+ sink.error("lha:" + cap.id, String(err && err.message || err));
1901
+ return;
1902
+ } finally {
1903
+ engine.sweeping = prevSweeping;
1904
+ }
1905
+ if (engine.gateHit || stamp() !== sweepOwnersBefore) {
1906
+ b.closed = true;
1907
+ sink.closeBinding(b.key);
1908
+ return;
1909
+ }
1910
+ if (html === b.last) return;
1911
+ const before = attrNames(b.last);
1912
+ const after = attrNames(html);
1913
+ const removed = before.filter(n => !after.includes(n));
1914
+ b.last = html;
1915
+ sink.attr(String(cap.id), html, removed);
1916
+ }
1917
+ };
1918
+ sink.openBinding(b.key, b);
1919
+ }
1920
+ };
1921
+ return engine;
1922
+ }
1923
+ function attrNames(text) {
1924
+ const names = [];
1925
+ const re = /(?:^|\s)([^\s=/>"']+)(?:="[^"]*")?/g;
1926
+ let m;
1927
+ while (m = re.exec(text)) names.push(m[1]);
1928
+ return names;
1929
+ }
1267
1930
  function ssr(t) {
1268
1931
  const len = arguments.length;
1269
1932
  if (len === 1) return {
@@ -1274,13 +1937,54 @@ function ssr(t) {
1274
1937
  let lastGroup = null;
1275
1938
  let lastGroupVal = null;
1276
1939
  let lastGroupIdx = 0;
1940
+ const live = sharedConfig.context && sharedConfig.context.liveHoles;
1941
+ const hp = live ? holeContentPositions(t) : null;
1942
+ let lastOpen = null;
1943
+ let cap = null;
1944
+ if (live && hp.openOff[0] >= 0) lastOpen = {
1945
+ r: null,
1946
+ seg: -1,
1947
+ off: hp.openOff[0]
1948
+ };
1949
+ const captureStart = () => {
1950
+ if (cap) return true;
1951
+ if (!lastOpen || live.suppressed || live.sweeping || !live.active()) return false;
1952
+ if (lastOpen.r !== result || result !== null && lastOpen.seg !== result.t.length - 1) {
1953
+ return false;
1954
+ }
1955
+ const id = live.mint();
1956
+ const inject = ` data-lha="${id}"`;
1957
+ let prefix;
1958
+ if (result === null) {
1959
+ s = s.slice(0, lastOpen.off) + inject + s.slice(lastOpen.off);
1960
+ prefix = s.slice(lastOpen.off + inject.length);
1961
+ } else {
1962
+ const seg = result.t[lastOpen.seg];
1963
+ result.t[lastOpen.seg] = seg.slice(0, lastOpen.off) + inject + seg.slice(lastOpen.off);
1964
+ prefix = result.t[lastOpen.seg].slice(lastOpen.off + inject.length);
1965
+ }
1966
+ cap = {
1967
+ id,
1968
+ parts: [prefix],
1969
+ base: prefix
1970
+ };
1971
+ return true;
1972
+ };
1277
1973
  for (let i = 1; i < len; i++) {
1278
1974
  const hole = arguments[i];
1279
1975
  const ht = typeof hole;
1280
1976
  if (ht === "string") {
1281
1977
  if (result === null) s += hole;else result.t[result.t.length - 1] += hole;
1978
+ if (cap) {
1979
+ cap.parts.push(hole);
1980
+ cap.base += hole;
1981
+ }
1282
1982
  } else if (ht === "number") {
1283
1983
  if (result === null) s += hole;else result.t[result.t.length - 1] += hole;
1984
+ if (cap) {
1985
+ cap.parts.push("" + hole);
1986
+ cap.base += hole;
1987
+ }
1284
1988
  } else if (hole == null || ht === "boolean") ; else if (ht === "function" && hole.$g) {
1285
1989
  let value;
1286
1990
  let hasValue = false;
@@ -1304,7 +2008,14 @@ function ssr(t) {
1304
2008
  if (Array.isArray(lastGroupVal)) {
1305
2009
  value = lastGroupVal[lastGroupIdx++];
1306
2010
  hasValue = true;
2011
+ if (live && !hp.pos[i - 1] && captureStart()) {
2012
+ cap.parts.push({
2013
+ g: hole,
2014
+ i: lastGroupIdx - 1
2015
+ });
2016
+ }
1307
2017
  } else {
2018
+ cap = null;
1308
2019
  result.h.push(ssrGroupSlot(lastGroupVal.fn, lastGroupIdx++));
1309
2020
  result.p.push(lastGroupVal.p);
1310
2021
  result.t.push("");
@@ -1314,6 +2025,7 @@ function ssr(t) {
1314
2025
  const vt = typeof value;
1315
2026
  if (vt === "string" || vt === "number") {
1316
2027
  if (result === null) s += value;else result.t[result.t.length - 1] += value;
2028
+ if (cap && !hp.pos[i - 1]) cap.base += value;
1317
2029
  } else if (value == null || vt === "boolean") ; else if (result !== null) {
1318
2030
  resolveSSRNode(value, result);
1319
2031
  } else {
@@ -1331,19 +2043,67 @@ function ssr(t) {
1331
2043
  }
1332
2044
  }
1333
2045
  }
1334
- } else if (result !== null) {
1335
- resolveSSRNode(hole, result);
1336
2046
  } else if (ht === "function") {
1337
- const r = tryResolveFunctionHole(hole);
1338
- if (typeof r === "string") s += r;else {
1339
- result = {
1340
- t: [s],
1341
- h: [],
1342
- p: []
1343
- };
1344
- s = "";
1345
- appendResolvedNode(result, r);
2047
+ let liveNode = null;
2048
+ if (live && hp.pos[i - 1] && (liveNode = live.content(hole)) !== null) {
2049
+ if (typeof liveNode === "string") {
2050
+ if (result === null) s += liveNode;else result.t[result.t.length - 1] += liveNode;
2051
+ } else {
2052
+ if (result === null) {
2053
+ result = {
2054
+ t: [s],
2055
+ h: [],
2056
+ p: []
2057
+ };
2058
+ s = "";
2059
+ }
2060
+ resolveSSRNode(liveNode, result);
2061
+ }
2062
+ } else if (result !== null) {
2063
+ const inTag = live && !hp.pos[i - 1];
2064
+ const capturing = inTag && captureStart();
2065
+ const li = capturing ? result.t.length - 1 : 0;
2066
+ const before = capturing ? result.t[li].length : 0;
2067
+ if (live) live.suppressed++;
2068
+ try {
2069
+ resolveSSRNode(hole, result);
2070
+ } finally {
2071
+ if (live) live.suppressed--;
2072
+ }
2073
+ if (capturing) {
2074
+ if (result.t.length - 1 === li) {
2075
+ cap.base += result.t[li].slice(before);
2076
+ cap.parts.push({
2077
+ f: hole
2078
+ });
2079
+ } else {
2080
+ cap = null;
2081
+ }
2082
+ }
2083
+ } else {
2084
+ const capturing = live && !hp.pos[i - 1] && captureStart();
2085
+ const r = tryResolveFunctionHole(hole);
2086
+ if (typeof r === "string") {
2087
+ s += r;
2088
+ if (capturing) {
2089
+ cap.base += r;
2090
+ cap.parts.push({
2091
+ f: hole
2092
+ });
2093
+ }
2094
+ } else {
2095
+ if (capturing) cap = null;
2096
+ result = {
2097
+ t: [s],
2098
+ h: [],
2099
+ p: []
2100
+ };
2101
+ s = "";
2102
+ appendResolvedNode(result, r);
2103
+ }
1346
2104
  }
2105
+ } else if (result !== null) {
2106
+ resolveSSRNode(hole, result);
1347
2107
  } else {
1348
2108
  const r = tryResolveString(hole);
1349
2109
  if (typeof r === "string") {
@@ -1359,6 +2119,32 @@ function ssr(t) {
1359
2119
  }
1360
2120
  }
1361
2121
  const next = t[i];
2122
+ if (live) {
2123
+ if (cap) {
2124
+ const co = hp.closeOff[i];
2125
+ if (co >= 0) {
2126
+ const tail = next.slice(0, co);
2127
+ cap.parts.push(tail);
2128
+ cap.base += tail;
2129
+ live.attr(cap);
2130
+ cap = null;
2131
+ } else {
2132
+ cap.parts.push(next);
2133
+ cap.base += next;
2134
+ }
2135
+ }
2136
+ if (hp.openOff[i] >= 0) {
2137
+ lastOpen = result === null ? {
2138
+ r: null,
2139
+ seg: -1,
2140
+ off: s.length + hp.openOff[i]
2141
+ } : {
2142
+ r: result,
2143
+ seg: result.t.length - 1,
2144
+ off: result.t[result.t.length - 1].length + hp.openOff[i]
2145
+ };
2146
+ }
2147
+ }
1362
2148
  if (result === null) s += next;else result.t[result.t.length - 1] += next;
1363
2149
  }
1364
2150
  if (result === null) return {
@@ -1447,17 +2233,11 @@ function allSettled(promises) {
1447
2233
  return;
1448
2234
  });
1449
2235
  }
1450
- function resolveAssetsHtml(assets) {
1451
- if (!assets || !assets.length) return "";
1452
- let out = "";
1453
- for (let i = 0, len = assets.length; i < len; i++) out += assets[i]();
1454
- return out;
1455
- }
1456
- function assembleDocument(html, assetsHtml, emittedAssets, inlineStyles, scripts, nonce, headTags, onHead) {
2236
+ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, headTags, onHead) {
1457
2237
  const scriptTag = scripts ? `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>` : "";
1458
2238
  const headTagsHtml = headTags ? headTags.html : "";
1459
2239
  const headPrelude = headTags ? headTags.prelude : "";
1460
- if (!onHead && !assetsHtml && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2240
+ if (!onHead && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
1461
2241
  if (!scriptTag) return html;
1462
2242
  const xs = html.indexOf("<!--xs-->");
1463
2243
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
@@ -1472,13 +2252,13 @@ function assembleDocument(html, assetsHtml, emittedAssets, inlineStyles, scripts
1472
2252
  const headIdx = html.indexOf("</head>");
1473
2253
  if (headIdx === -1) {
1474
2254
  if (onHead) {
1475
- onHead(headPrelude + headTagsHtml + (assetsHtml || "") + renderHeadAssets(emittedAssets, inlineStyles, nonce));
2255
+ onHead(headPrelude + headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce));
1476
2256
  }
1477
2257
  if (!scriptTag) return html;
1478
2258
  const xs = html.indexOf("<!--xs-->");
1479
2259
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
1480
2260
  }
1481
- const head = headTagsHtml + (assetsHtml || "") + renderHeadAssets(emittedAssets, inlineStyles, nonce);
2261
+ const head = headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce);
1482
2262
  if (!scriptTag) return html.slice(0, headIdx) + head + html.slice(headIdx);
1483
2263
  const xsIdx = html.indexOf("<!--xs-->");
1484
2264
  if (xsIdx === -1) return html.slice(0, headIdx) + head + html.slice(headIdx) + scriptTag;
@@ -1597,7 +2377,6 @@ function tryResolveString(node) {
1597
2377
  merge: node
1598
2378
  };
1599
2379
  if (node.t === undefined) {
1600
- console.warn(`Unrecognized value. Skipped inserting`, node);
1601
2380
  return "";
1602
2381
  }
1603
2382
  return Array.isArray(node.t) ? node.t[0] : node.t;
@@ -1622,13 +2401,19 @@ function resolveSSRNode(node, result = {
1622
2401
  if (t === "string" || t === "number") {
1623
2402
  result.t[result.t.length - 1] += node;
1624
2403
  } else if (node == null || t === "boolean") ; else if (Array.isArray(node)) {
1625
- let prevNonObj = false;
1626
- for (let i = 0, len = node.length; i < len; i++) {
1627
- const item = node[i];
1628
- const itemNonObj = item !== null && typeof item !== "object";
1629
- if (!top && prevNonObj && itemNonObj) result.t[result.t.length - 1] += `<!--!$-->`;
1630
- prevNonObj = itemNonObj;
1631
- resolveSSRNode(item, result);
2404
+ const slotLive = node.$slot && sharedConfig.context && sharedConfig.context.liveHoles;
2405
+ if (slotLive) slotLive.suppressed++;
2406
+ try {
2407
+ let prevNonObj = false;
2408
+ for (let i = 0, len = node.length; i < len; i++) {
2409
+ const item = node[i];
2410
+ const itemNonObj = item !== null && typeof item !== "object";
2411
+ if (!top && prevNonObj && itemNonObj) result.t[result.t.length - 1] += `<!--!$-->`;
2412
+ prevNonObj = itemNonObj;
2413
+ resolveSSRNode(item, result);
2414
+ }
2415
+ } finally {
2416
+ if (slotLive) slotLive.suppressed--;
1632
2417
  }
1633
2418
  } else if (t === "object") {
1634
2419
  if (node.h) {
@@ -1640,16 +2425,22 @@ function resolveSSRNode(node, result = {
1640
2425
  }
1641
2426
  } else if (node.t !== undefined) {
1642
2427
  result.t[result.t.length - 1] += node.t;
1643
- } else console.warn(`Unrecognized value. Skipped inserting`, node);
2428
+ } else ;
1644
2429
  } else if (t === "function") {
1645
- try {
1646
- resolveSSRNode(node(), result);
1647
- } catch (err) {
1648
- const wrap = buildAsyncWrap(err, node);
1649
- if (wrap) {
1650
- result.h.push(wrap.fn);
1651
- result.p.push(wrap.p);
1652
- result.t.push("");
2430
+ const live = sharedConfig.context && sharedConfig.context.liveHoles;
2431
+ let liveNode = null;
2432
+ if (live && (liveNode = live.content(node)) !== null) {
2433
+ if (typeof liveNode === "string") result.t[result.t.length - 1] += liveNode;else resolveSSRNode(liveNode, result);
2434
+ } else {
2435
+ try {
2436
+ resolveSSRNode(node(), result);
2437
+ } catch (err) {
2438
+ const wrap = buildAsyncWrap(err, node);
2439
+ if (wrap) {
2440
+ result.h.push(wrap.fn);
2441
+ result.p.push(wrap.p);
2442
+ result.t.push("");
2443
+ }
1653
2444
  }
1654
2445
  }
1655
2446
  }
@@ -1660,142 +2451,7 @@ function resolveSSRSync(node) {
1660
2451
  if (!res.h.length) return res.t[0];
1661
2452
  throw new Error("This value cannot be rendered synchronously. Are you missing a boundary?");
1662
2453
  }
1663
-
1664
- function frameAddress(id, args) {
1665
- return args && args.length ? id + ":" + hashArguments(args) : id;
1666
- }
1667
- function hashArguments(args) {
1668
- let hash = 0;
1669
- const text = stableString(args);
1670
- for (let i = 0; i < text.length; i++) {
1671
- hash = (hash << 5) - hash + text.charCodeAt(i);
1672
- hash |= 0;
1673
- }
1674
- return (hash >>> 0).toString(36);
1675
- }
1676
- function stableString(value, seen) {
1677
- if (value === null || typeof value !== "object") {
1678
- return typeof value === "bigint" ? value + "n" : String(value);
1679
- }
1680
- if (value instanceof Date) return "Date:" + value.getTime();
1681
- seen || (seen = new Set());
1682
- if (seen.has(value)) return "~";
1683
- seen.add(value);
1684
- if (value instanceof Map) {
1685
- const entries = [];
1686
- for (const [k, v] of value) {
1687
- entries.push(stableString(k, seen) + "=>" + stableString(v, seen));
1688
- }
1689
- return "Map{" + entries.sort().join(",") + "}";
1690
- }
1691
- if (value instanceof Set) {
1692
- const members = [];
1693
- for (const v of value) members.push(stableString(v, seen));
1694
- return "Set{" + members.sort().join(",") + "}";
1695
- }
1696
- if (Array.isArray(value)) {
1697
- let out = "[";
1698
- for (let i = 0; i < value.length; i++) out += (i ? "," : "") + stableString(value[i], seen);
1699
- return out + "]";
1700
- }
1701
- const keys = Object.keys(value).sort();
1702
- let out = "{";
1703
- for (let i = 0; i < keys.length; i++) {
1704
- out += (i ? "," : "") + keys[i] + ":" + stableString(value[keys[i]], seen);
1705
- }
1706
- return out + "}";
1707
- }
1708
- const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
1709
- function createChunk(data) {
1710
- const encodeData = new TextEncoder().encode(data);
1711
- const bytes = encodeData.length;
1712
- const baseHex = bytes.toString(16);
1713
- const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
1714
- const head = new TextEncoder().encode(`;0x${totalHex};`);
1715
- const chunk = new Uint8Array(12 + bytes);
1716
- chunk.set(head);
1717
- chunk.set(encodeData, 12);
1718
- return chunk;
1719
- }
1720
- class ChunkReader {
1721
- constructor(stream) {
1722
- this.reader = stream.getReader();
1723
- this.buffer = new Uint8Array(0);
1724
- this.done = false;
1725
- }
1726
- async readChunk() {
1727
- const chunk = await this.reader.read();
1728
- if (!chunk.done) {
1729
- const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
1730
- newBuffer.set(this.buffer);
1731
- newBuffer.set(chunk.value, this.buffer.length);
1732
- this.buffer = newBuffer;
1733
- } else {
1734
- this.done = true;
1735
- }
1736
- }
1737
- async next() {
1738
- while (this.buffer.length < 12) {
1739
- if (this.done) {
1740
- if (this.buffer.length === 0) return {
1741
- done: true,
1742
- value: undefined
1743
- };
1744
- throw new Error("Malformed server function stream.");
1745
- }
1746
- await this.readChunk();
1747
- }
1748
- const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
1749
- const bytes = Number.parseInt(head, 16);
1750
- if (Number.isNaN(bytes)) {
1751
- throw new Error("Malformed server function stream.");
1752
- }
1753
- while (bytes > this.buffer.length - 12) {
1754
- if (this.done) {
1755
- throw new Error("Malformed server function stream.");
1756
- }
1757
- await this.readChunk();
1758
- }
1759
- const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
1760
- this.buffer = this.buffer.subarray(12 + bytes);
1761
- return {
1762
- done: false,
1763
- value: partial
1764
- };
1765
- }
1766
- async drain(interpret) {
1767
- while (true) {
1768
- const result = await this.next();
1769
- if (result.done) {
1770
- break;
1771
- }
1772
- interpret(result.value);
1773
- }
1774
- }
1775
- }
1776
- function serializeStream(value, codecOptions) {
1777
- return new ReadableStream({
1778
- start(controller) {
1779
- serializeJSON(value, {
1780
- ...codecOptions,
1781
- onParse(node) {
1782
- controller.enqueue(createChunk(JSON.stringify(node)));
1783
- },
1784
- onDone() {
1785
- controller.close();
1786
- },
1787
- onError(error) {
1788
- controller.error(error);
1789
- }
1790
- });
1791
- }
1792
- });
1793
- }
1794
-
1795
- const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
1796
- function isResponseEnvelope(value) {
1797
- return !!(value && typeof value === "object" && value[ENVELOPE]);
1798
- }
2454
+ /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
1799
2455
 
1800
2456
  const INVOCATIONS = new WeakMap();
1801
2457
  function getEventServerFunctionInvocation(event) {
@@ -1819,7 +2475,7 @@ function parseServerComponent(value, ctx) {
1819
2475
  address: ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1820
2476
  };
1821
2477
  }
1822
- const ServerComponentPlugin = /*#__PURE__*/createPlugin({
2478
+ const ServerComponentPlugin = {
1823
2479
  tag: "dom-expressions/server-component",
1824
2480
  test(value) {
1825
2481
  return typeof value === "function" && SERVER_COMPONENT in value;
@@ -1843,18 +2499,17 @@ const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1843
2499
  ctx.deserialize(node.address);
1844
2500
  return globalThis._$SC.r(id);
1845
2501
  }
1846
- });
2502
+ };
1847
2503
  function flightCodec(codec) {
1848
2504
  const plugins = codec && codec.plugins || [];
1849
- for (const plugin of plugins) {
1850
- if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
1851
- }
2505
+ if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
1852
2506
  return {
1853
2507
  ...codec,
1854
2508
  plugins: [...plugins, ServerComponentPlugin]
1855
2509
  };
1856
2510
  }
1857
2511
 
2512
+ const scopeStamp = typeof creationStamp === "function" ? creationStamp : () => 0;
1858
2513
  function serverOwned(render) {
1859
2514
  return NoHydration ? NoHydration({
1860
2515
  get children() {
@@ -1879,6 +2534,29 @@ function createFrameSink(emit, frame) {
1879
2534
  version
1880
2535
  } = frame;
1881
2536
  const styledKeys = new Set();
2537
+ const bindings = new Map();
2538
+ const sweepMinted = new Set();
2539
+ const argRefVersions = new Map();
2540
+ let sweepScheduled = false;
2541
+ let closed = false;
2542
+ let epoch = 0;
2543
+ const sweep = () => {
2544
+ epoch++;
2545
+ for (const b of [...bindings.values()]) {
2546
+ try {
2547
+ b.sweep();
2548
+ } catch (_) {
2549
+ }
2550
+ }
2551
+ };
2552
+ const scheduleSweep = () => {
2553
+ if (closed || sweepScheduled || !bindings.size) return;
2554
+ sweepScheduled = true;
2555
+ queueMicrotask(() => {
2556
+ sweepScheduled = false;
2557
+ if (!closed) sweep();
2558
+ });
2559
+ };
1882
2560
  const regionKeys = new Map();
1883
2561
  const frameOf = key => regionKeys.get(key) || id;
1884
2562
  return {
@@ -1920,6 +2598,7 @@ function createFrameSink(emit, frame) {
1920
2598
  version,
1921
2599
  payload: record
1922
2600
  });
2601
+ scheduleSweep();
1923
2602
  } else {
1924
2603
  emit({
1925
2604
  type: "data",
@@ -1929,6 +2608,7 @@ function createFrameSink(emit, frame) {
1929
2608
  node: record.node,
1930
2609
  initial: record.initial
1931
2610
  });
2611
+ if (!sweepMinted.has(record.key)) scheduleSweep();
1932
2612
  }
1933
2613
  },
1934
2614
  fragment(key, value, meta = {}) {
@@ -1965,6 +2645,7 @@ function createFrameSink(emit, frame) {
1965
2645
  key,
1966
2646
  html: value
1967
2647
  });
2648
+ scheduleSweep();
1968
2649
  if (meta.error) {
1969
2650
  emit({
1970
2651
  type: "error",
@@ -2019,6 +2700,8 @@ function createFrameSink(emit, frame) {
2019
2700
  });
2020
2701
  },
2021
2702
  end() {
2703
+ if (bindings.size) sweep();
2704
+ closed = true;
2022
2705
  emit({
2023
2706
  type: "complete",
2024
2707
  id,
@@ -2050,6 +2733,44 @@ function createFrameSink(emit, frame) {
2050
2733
  version,
2051
2734
  html
2052
2735
  });
2736
+ },
2737
+ hole(key, html) {
2738
+ emit({
2739
+ type: "hole",
2740
+ id,
2741
+ version,
2742
+ key,
2743
+ html
2744
+ });
2745
+ },
2746
+ attr(key, attrs, removed) {
2747
+ const chunk = {
2748
+ type: "attr",
2749
+ id,
2750
+ version,
2751
+ key,
2752
+ attrs
2753
+ };
2754
+ if (removed && removed.length) chunk.removed = removed;
2755
+ emit(chunk);
2756
+ },
2757
+ openBinding(key, b) {
2758
+ bindings.set(key, b);
2759
+ },
2760
+ closeBinding(key) {
2761
+ bindings.delete(key);
2762
+ },
2763
+ mintRef(ref) {
2764
+ sweepMinted.add(ref);
2765
+ },
2766
+ nextArgRef(ledgerKey) {
2767
+ const n = (argRefVersions.get(ledgerKey) || 0) + 1;
2768
+ argRefVersions.set(ledgerKey, n);
2769
+ return n;
2770
+ },
2771
+ commit: scheduleSweep,
2772
+ get epoch() {
2773
+ return epoch;
2053
2774
  }
2054
2775
  };
2055
2776
  }
@@ -2059,7 +2780,15 @@ function renderToFrameStream(code, options = {}) {
2059
2780
  function renderServerComponent(component, options = {}) {
2060
2781
  return frameStream((sink, frame) => {
2061
2782
  const props = createSlotProps(sink, frame);
2062
- return () => serverComponentScope(() => component(props));
2783
+ return () => {
2784
+ const ctx = sharedConfig.context;
2785
+ if (ctx) {
2786
+ ctx.commit = sink.commit;
2787
+ ctx.commitEpoch = () => sink.epoch;
2788
+ ctx.liveHoles = createLiveHoles(sink);
2789
+ }
2790
+ return serverComponentScope(() => component(props));
2791
+ };
2063
2792
  }, options);
2064
2793
  }
2065
2794
  function frameStream(makeCode, options) {
@@ -2117,7 +2846,8 @@ function frameStream(makeCode, options) {
2117
2846
  }
2118
2847
  function slotRange(occurrence) {
2119
2848
  return {
2120
- t: `<!--slot:${occurrence}:start--><!--slot:${occurrence}:end-->`
2849
+ t: `<!--slot:${occurrence}:start--><!--slot:${occurrence}:end-->`,
2850
+ $slot: true
2121
2851
  };
2122
2852
  }
2123
2853
  const OCCURRENCE_UNSAFE = /[^A-Za-z0-9_.-]/g;
@@ -2136,14 +2866,57 @@ function occurrenceId(prop, raw, counts) {
2136
2866
  counts[prop] = n + 1;
2137
2867
  return `${prop}#${n}`;
2138
2868
  }
2869
+ function isAsyncValue(v) {
2870
+ return !!v && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
2871
+ }
2872
+ function tapFirstYield(iterable) {
2873
+ const iter = iterable[Symbol.asyncIterator]();
2874
+ const firstStep = Promise.resolve(iter.next());
2875
+ return {
2876
+ first: firstStep.then(r => r.done ? undefined : r.value),
2877
+ rest: {
2878
+ [Symbol.asyncIterator]() {
2879
+ let replayed = false;
2880
+ return {
2881
+ next: () => {
2882
+ if (!replayed) {
2883
+ replayed = true;
2884
+ return firstStep;
2885
+ }
2886
+ return iter.next();
2887
+ },
2888
+ return: v => iter.return ? iter.return(v) : Promise.resolve({
2889
+ done: true,
2890
+ value: v
2891
+ }),
2892
+ throw: e => iter.throw ? iter.throw(e) : Promise.reject(e)
2893
+ };
2894
+ }
2895
+ }
2896
+ };
2897
+ }
2898
+ function suppressedFill(render) {
2899
+ const live = sharedConfig.context && sharedConfig.context.liveHoles;
2900
+ if (!live) return render();
2901
+ live.suppressed++;
2902
+ try {
2903
+ return render();
2904
+ } finally {
2905
+ live.suppressed--;
2906
+ }
2907
+ }
2139
2908
  function createDocumentSlotProps(clientProps, frameId) {
2140
2909
  const counts = Object.create(null);
2141
2910
  const getters = new Map();
2142
- const range = (occurrence, content) => [{
2143
- t: `<!--slot:${occurrence}:start-->`
2144
- }, content, {
2145
- t: `<!--slot:${occurrence}:end-->`
2146
- }];
2911
+ const range = (occurrence, content) => {
2912
+ const r = [{
2913
+ t: `<!--slot:${occurrence}:start-->`
2914
+ }, content, {
2915
+ t: `<!--slot:${occurrence}:end-->`
2916
+ }];
2917
+ r.$slot = true;
2918
+ return r;
2919
+ };
2147
2920
  const zoneOwner = getOwner ? getOwner() : null;
2148
2921
  const scoped = (occurrence, render) => {
2149
2922
  const id = `sc-${frameId}-${occurrence}-`;
@@ -2166,27 +2939,68 @@ function createDocumentSlotProps(clientProps, frameId) {
2166
2939
  if (!fn) {
2167
2940
  fn = (...callArgs) => {
2168
2941
  if (callArgs.length === 0 || callArgs[0] === undefined) {
2169
- return scoped(prop, () => {
2942
+ return suppressedFill(() => scoped(prop, () => {
2170
2943
  const value = clientProps[prop];
2171
2944
  return range(prop, typeof value === "function" ? value() : value);
2172
- });
2945
+ }));
2173
2946
  }
2174
2947
  const raw = callArgs[0];
2175
2948
  const occurrence = occurrenceId(prop, raw, counts);
2176
2949
  const slot = clientProps[prop];
2177
2950
  if (typeof slot !== "function") return range(occurrence, undefined);
2178
2951
  const resolved = {};
2952
+ const liveArgs = sharedConfig.context && sharedConfig.context.live && sharedConfig.context.live.args;
2179
2953
  const vals = {};
2954
+ const evals = {};
2955
+ const states = {};
2956
+ const minted = {};
2180
2957
  for (const key of Object.keys(raw)) {
2181
2958
  if (key === "$key") continue;
2182
- let value = raw[key];
2183
- for (let d = 0; typeof value === "function" && d < 16; d++) value = value();
2959
+ const desc = Object.getOwnPropertyDescriptor(raw, key);
2960
+ let evaluate = null;
2961
+ let value;
2962
+ if (desc.get) {
2963
+ const get = desc.get;
2964
+ evaluate = () => unwrapThunks(get.call(raw));
2965
+ } else {
2966
+ value = desc.value;
2967
+ if (typeof value === "function") {
2968
+ const fn = value;
2969
+ evaluate = () => unwrapThunks(fn);
2970
+ }
2971
+ }
2972
+ if (evaluate) {
2973
+ evals[key] = evaluate;
2974
+ const stampBefore = scopeStamp();
2975
+ try {
2976
+ value = evaluate();
2977
+ states[key] = {
2978
+ settled: true,
2979
+ last: value
2980
+ };
2981
+ } catch (err) {
2982
+ const blocked = ssrHandleError && ssrHandleError(err);
2983
+ if (!blocked) throw err;
2984
+ const state = states[key] = {
2985
+ settled: false,
2986
+ last: undefined
2987
+ };
2988
+ value = retryArgUntilSettled(evaluate, blocked, key, occurrence, v => {
2989
+ state.settled = true;
2990
+ state.last = v;
2991
+ if (liveArgs) liveArgs.commit();
2992
+ });
2993
+ }
2994
+ if (scopeStamp() !== stampBefore) minted[key] = true;
2995
+ }
2184
2996
  vals[key] = value;
2185
2997
  }
2186
2998
  const regions = [];
2187
2999
  for (const key of Object.keys(vals)) {
2188
3000
  const value = vals[key];
2189
- if (isServerContent(value)) {
3001
+ if (isContainerTraced(value)) {
3002
+ resolved[key] = value;
3003
+ } else if (isServerContent(value)) {
2190
3004
  const childId = `${frameId}.${occurrence}.${key}`;
2191
3005
  const region = {
2192
3006
  key,
@@ -2205,11 +3019,27 @@ function createDocumentSlotProps(clientProps, frameId) {
2205
3019
  t: FRAME_ELEMENT_CLOSE
2206
3020
  }];
2207
3021
  };
3022
+ } else if (ssrAsyncValue && isAsyncValue(value)) {
3023
+ let readable = value;
3024
+ if (typeof value.then !== "function") {
3025
+ const {
3026
+ first,
3027
+ rest
3028
+ } = tapFirstYield(value);
3029
+ readable = first;
3030
+ vals[key] = rest;
3031
+ }
3032
+ const read = ssrAsyncValue(readable);
3033
+ Object.defineProperty(resolved, key, {
3034
+ get: read,
3035
+ enumerable: true,
3036
+ configurable: true
3037
+ });
2208
3038
  } else {
2209
3039
  resolved[key] = value;
2210
3040
  }
2211
3041
  }
2212
- const out = scoped(occurrence, () => range(occurrence, slot(resolved)));
3042
+ const out = suppressedFill(() => scoped(occurrence, () => range(occurrence, slot(resolved))));
2213
3043
  const unused = regions.filter(r => !r.used);
2214
3044
  if (sharedConfig.context) {
2215
3045
  const args = {};
@@ -2222,17 +3052,33 @@ function createDocumentSlotProps(clientProps, frameId) {
2222
3052
  };
2223
3053
  continue;
2224
3054
  }
2225
- if (isServerContent(value)) continue;
2226
- args[key] = value;
3055
+ if (!isContainerTraced(value) && isServerContent(value)) continue;
3056
+ args[key] = envelopeContainerTraces(value);
2227
3057
  }
2228
- sharedConfig.context.serialize(`sc:slot:${frameId}:${occurrence}`, args);
3058
+ sharedConfig.context.serialize(`sc:slot:${frameId}:${occurrence}`, {
3059
+ ...args
3060
+ });
2229
3061
  for (const region of unused) {
2230
3062
  region.locked = true;
2231
3063
  sharedConfig.context.serialize(`sc:region:${region.childId}`, resolveRegionHtml(sharedConfig.context, region.value));
2232
3064
  }
3065
+ if (liveArgs) {
3066
+ for (const key of Object.keys(evals)) {
3067
+ if (regions.some(r => r.key === key)) continue;
3068
+ if (minted[key]) continue;
3069
+ const ledgerKey = `${frameId}:${occurrence}:${key}`;
3070
+ openArgBinding(liveArgs, ledgerKey, occurrence, key, evals[key], states[key], value => {
3071
+ args[key] = envelopeContainerTraces(value);
3072
+ liveArgs.slot(frameId, occurrence, {
3073
+ ...args
3074
+ });
3075
+ });
3076
+ }
3077
+ }
2233
3078
  }
2234
3079
  return out;
2235
3080
  };
3081
+ fn.$lhSkip = true;
2236
3082
  getters.set(prop, fn);
2237
3083
  }
2238
3084
  return fn;
@@ -2246,6 +3092,113 @@ function frameElementOpen(id) {
2246
3092
  return `<${FRAME_TAG} ${FRAME_ID_ATTR}="${escaped}" style="display:contents">`;
2247
3093
  }
2248
3094
  const FRAME_ELEMENT_CLOSE = `</${FRAME_TAG}>`;
3095
+ function armDocumentLiveHoles(ctx) {
3096
+ if (!ctx || ctx.liveHoles !== undefined) return;
3097
+ const live = ctx.live;
3098
+ if (live && live.holes !== undefined) {
3099
+ ctx.liveHoles = live.holes;
3100
+ if (live.holes) {
3101
+ ctx.commit = live.commit;
3102
+ ctx.commitEpoch = live.commitEpoch;
3103
+ }
3104
+ return;
3105
+ }
3106
+ if (!live || !ctx.async || ctx.noHydrate || !ctx.serialize || typeof ReadableStream !== "function") {
3107
+ ctx.liveHoles = null;
3108
+ if (live) live.holes = null;
3109
+ return;
3110
+ }
3111
+ const bindings = new Map();
3112
+ let epoch = 0;
3113
+ let sweepScheduled = false;
3114
+ let closed = false;
3115
+ const sweep = () => {
3116
+ epoch++;
3117
+ for (const b of [...bindings.values()]) {
3118
+ try {
3119
+ b.sweep();
3120
+ } catch (_) {
3121
+ }
3122
+ }
3123
+ };
3124
+ const scheduleSweep = () => {
3125
+ if (closed || sweepScheduled || !bindings.size) return;
3126
+ sweepScheduled = true;
3127
+ queueMicrotask(() => {
3128
+ sweepScheduled = false;
3129
+ if (!closed) sweep();
3130
+ });
3131
+ };
3132
+ let channel;
3133
+ ctx.serialize("sc:live", new ReadableStream({
3134
+ start(c) {
3135
+ channel = c;
3136
+ }
3137
+ }));
3138
+ const push = op => {
3139
+ if (!closed) channel.enqueue(op);
3140
+ };
3141
+ ctx.liveHoles = live.holes = createLiveHoles({
3142
+ openBinding(key, b) {
3143
+ bindings.set(key, b);
3144
+ },
3145
+ closeBinding(key) {
3146
+ bindings.delete(key);
3147
+ },
3148
+ hole(key, html) {
3149
+ push({
3150
+ type: "hole",
3151
+ key,
3152
+ html
3153
+ });
3154
+ },
3155
+ attr(key, attrs, removed) {
3156
+ const op = {
3157
+ type: "attr",
3158
+ key,
3159
+ attrs
3160
+ };
3161
+ if (removed && removed.length) op.removed = removed;
3162
+ push(op);
3163
+ },
3164
+ error(key, error) {
3165
+ push({
3166
+ type: "error",
3167
+ key,
3168
+ error
3169
+ });
3170
+ },
3171
+ commit: scheduleSweep,
3172
+ get epoch() {
3173
+ return epoch;
3174
+ }
3175
+ }, true);
3176
+ ctx.commit = live.commit = scheduleSweep;
3177
+ ctx.commitEpoch = live.commitEpoch = () => epoch;
3178
+ live.args = {
3179
+ openBinding(key, b) {
3180
+ bindings.set(key, b);
3181
+ },
3182
+ closeBinding(key) {
3183
+ bindings.delete(key);
3184
+ },
3185
+ slot(fid, occurrence, args) {
3186
+ push({
3187
+ type: "slot",
3188
+ fid,
3189
+ key: occurrence,
3190
+ args
3191
+ });
3192
+ },
3193
+ commit: scheduleSweep
3194
+ };
3195
+ live.end = () => {
3196
+ if (closed) return;
3197
+ if (bindings.size) sweep();
3198
+ closed = true;
3199
+ channel.close();
3200
+ };
3201
+ }
2249
3202
  function frameTransformDirectResult(value, {
2250
3203
  id,
2251
3204
  args
@@ -2256,6 +3209,7 @@ function frameTransformDirectResult(value, {
2256
3209
  t: frameElementOpen(id)
2257
3210
  },
2258
3211
  serverOwned(() => {
3212
+ armDocumentLiveHoles(sharedConfig.context);
2259
3213
  const slotProps = createDocumentSlotProps(props, id);
2260
3214
  return serverComponentScope(() => component(slotProps));
2261
3215
  }), {
@@ -2292,6 +3246,76 @@ function isServerContent(value) {
2292
3246
  }
2293
3247
  return false;
2294
3248
  }
3249
+ function unwrapThunks(value) {
3250
+ for (let d = 0; typeof value === "function" && d < 16; d++) value = value();
3251
+ return value;
3252
+ }
3253
+ function contentArgError(key, occurrence) {
3254
+ return new Error("Async slot arg resolved to JSX (arg '" + key + "' of " + occurrence + "). Async args must resolve to serializable values; render async content through a boundary instead.");
3255
+ }
3256
+ function retryArgUntilSettled(evaluate, blocked, key, occurrence, onSettle) {
3257
+ const owner = getOwner();
3258
+ return new Promise((resolve, reject) => {
3259
+ const retry = () => {
3260
+ try {
3261
+ const value = owner ? runWithOwner(owner, evaluate) : evaluate();
3262
+ if (isServerContent(value)) {
3263
+ reject(contentArgError(key, occurrence));
3264
+ return;
3265
+ }
3266
+ if (onSettle) onSettle(value);
3267
+ resolve(value);
3268
+ } catch (err) {
3269
+ const next = ssrHandleError && ssrHandleError(err);
3270
+ if (next) next.then(retry, retry);else reject(err);
3271
+ }
3272
+ };
3273
+ blocked.then(retry, retry);
3274
+ });
3275
+ }
3276
+ function openArgBinding(sink, ledgerKey, occurrence, key, evaluate, state, emit) {
3277
+ const owner = getOwner();
3278
+ sink.openBinding(ledgerKey, {
3279
+ sweep() {
3280
+ if (!state.settled) return;
3281
+ let value;
3282
+ const stampBefore = scopeStamp();
3283
+ try {
3284
+ value = owner ? runWithOwner(owner, evaluate) : evaluate();
3285
+ } catch (err) {
3286
+ const blocked = ssrHandleError && ssrHandleError(err);
3287
+ if (!blocked) {
3288
+ sink.closeBinding(ledgerKey);
3289
+ emit(Promise.reject(err instanceof Error ? err : new Error(String(err))));
3290
+ return;
3291
+ }
3292
+ if (scopeStamp() !== stampBefore) {
3293
+ sink.closeBinding(ledgerKey);
3294
+ return;
3295
+ }
3296
+ state.settled = false;
3297
+ emit(retryArgUntilSettled(evaluate, blocked, key, occurrence, v => {
3298
+ state.settled = true;
3299
+ state.last = v;
3300
+ sink.commit();
3301
+ }));
3302
+ return;
3303
+ }
3304
+ if (scopeStamp() !== stampBefore) {
3305
+ sink.closeBinding(ledgerKey);
3306
+ return;
3307
+ }
3308
+ if (value === state.last) return;
3309
+ state.last = value;
3310
+ if (isServerContent(value)) {
3311
+ sink.closeBinding(ledgerKey);
3312
+ emit(Promise.reject(contentArgError(key, occurrence)));
3313
+ return;
3314
+ }
3315
+ emit(value);
3316
+ }
3317
+ });
3318
+ }
2295
3319
  function createSlotProps(sink, frame) {
2296
3320
  const counts = Object.create(null);
2297
3321
  const getters = new Map();
@@ -2308,9 +3332,21 @@ function createSlotProps(sink, frame) {
2308
3332
  if (callArgs.length === 0 || callArgs[0] === undefined) {
2309
3333
  return slotRange(prop);
2310
3334
  }
3335
+ const live = sharedConfig.context && sharedConfig.context.liveHoles;
3336
+ if (live) {
3337
+ if (live.sweeping) {
3338
+ live.gateHit = true;
3339
+ return {
3340
+ t: "",
3341
+ $slot: true
3342
+ };
3343
+ }
3344
+ live.recordStamp++;
3345
+ }
2311
3346
  const raw = callArgs[0];
2312
3347
  const occurrence = occurrenceId(prop, raw, counts);
2313
3348
  const args = {};
3349
+ const opened = [];
2314
3350
  for (const key of Object.keys(raw)) {
2315
3351
  if (key === "$key") continue;
2316
3352
  const childId = `${frame.id}.${occurrence}.${key}`;
@@ -2321,12 +3357,47 @@ function createSlotProps(sink, frame) {
2321
3357
  return origRegister.call(ctx, fragKey, fragOptions);
2322
3358
  };
2323
3359
  try {
2324
- let value = raw[key];
2325
- for (let d = 0; typeof value === "function" && d < 16; d++) value = value();
3360
+ const desc = Object.getOwnPropertyDescriptor(raw, key);
3361
+ let evaluate = null;
3362
+ let value;
3363
+ let state = null;
3364
+ const stampBefore = scopeStamp();
3365
+ try {
3366
+ if (desc.get) {
3367
+ const get = desc.get;
3368
+ evaluate = () => unwrapThunks(get.call(raw));
3369
+ value = evaluate();
3370
+ } else {
3371
+ value = desc.value;
3372
+ if (typeof value === "function") {
3373
+ const fn = value;
3374
+ evaluate = () => unwrapThunks(fn);
3375
+ value = evaluate();
3376
+ }
3377
+ }
3378
+ } catch (err) {
3379
+ const blocked = ssrHandleError && ssrHandleError(err);
3380
+ if (!blocked) throw err;
3381
+ state = {
3382
+ settled: false,
3383
+ last: undefined
3384
+ };
3385
+ const pendingState = state;
3386
+ value = retryArgUntilSettled(evaluate, blocked, key, occurrence, v => {
3387
+ pendingState.settled = true;
3388
+ pendingState.last = v;
3389
+ sink.commit();
3390
+ });
3391
+ }
3392
+ const mintedEval = scopeStamp() !== stampBefore;
2326
3393
  const t = typeof value;
2327
3394
  if (value == null || t === "string" || t === "number" || t === "boolean") {
2328
3395
  args[key] = value;
2329
- } else if (isServerContent(value)) {
3396
+ if (evaluate) state = {
3397
+ settled: true,
3398
+ last: value
3399
+ };
3400
+ } else if (!isContainerTraced(value) && isServerContent(value)) {
2330
3401
  const resolved = ctx.resolve(value);
2331
3402
  if (resolved.h.length) {
2332
3403
  throw new Error("Async server content in a slot arg needs a boundary (arg '" + key + "' of " + occurrence + "). Wrap the async read in a <Suspense>, or move it above the slot.");
@@ -2337,30 +3408,72 @@ function createSlotProps(sink, frame) {
2337
3408
  };
2338
3409
  } else {
2339
3410
  const ref = `arg:${occurrence}:${key}`;
2340
- ctx.serialize(ref, value);
3411
+ ctx.serialize(ref, envelopeContainerTraces(value));
2341
3412
  args[key] = {
2342
3413
  $ref: ref
2343
3414
  };
3415
+ if (evaluate && !state) state = {
3416
+ settled: true,
3417
+ last: value
3418
+ };
2344
3419
  }
3420
+ if (evaluate && state && !mintedEval) opened.push({
3421
+ key,
3422
+ evaluate,
3423
+ state
3424
+ });
2345
3425
  } finally {
2346
3426
  ctx.registerFragment = origRegister;
2347
3427
  }
2348
3428
  }
2349
- sink.slot(occurrence, args);
3429
+ sink.slot(occurrence, {
3430
+ ...args
3431
+ });
3432
+ const ctx = sharedConfig.context;
3433
+ for (const b of opened) {
3434
+ const key = b.key;
3435
+ const ledgerKey = `${occurrence}:${key}`;
3436
+ openArgBinding(sink, ledgerKey, occurrence, key, b.evaluate, b.state, value => {
3437
+ const t = typeof value;
3438
+ if (value == null || t === "string" || t === "number" || t === "boolean") {
3439
+ args[key] = value;
3440
+ } else {
3441
+ const ref = `arg:${occurrence}:${key}@${sink.nextArgRef(ledgerKey)}`;
3442
+ sink.mintRef(ref);
3443
+ ctx.serialize(ref, envelopeContainerTraces(value));
3444
+ args[key] = {
3445
+ $ref: ref
3446
+ };
3447
+ }
3448
+ sink.slot(occurrence, {
3449
+ ...args
3450
+ });
3451
+ });
3452
+ }
2350
3453
  return slotRange(occurrence);
2351
3454
  };
3455
+ fn.$lhSkip = true;
2352
3456
  getters.set(prop, fn);
2353
3457
  }
2354
3458
  return fn;
2355
3459
  }
2356
3460
  });
2357
3461
  }
3462
+ function copyInitHeaders(init) {
3463
+ if (!init || !init.getSetCookie) return new Headers(init);
3464
+ const headers = new Headers();
3465
+ init.forEach((value, key) => {
3466
+ if (key !== "set-cookie") headers.append(key, value);
3467
+ });
3468
+ for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
3469
+ return headers;
3470
+ }
2358
3471
  function serverComponentResponse(component, options = {}, init = {}) {
2359
3472
  const {
2360
3473
  id = "",
2361
3474
  version = 1
2362
3475
  } = options.frame || {};
2363
- const headers = new Headers(init.headers);
3476
+ const headers = copyInitHeaders(init.headers);
2364
3477
  headers.set("Content-Type", "application/x-frame-stream");
2365
3478
  headers.set(FRAME_STREAM_HEADER, id);
2366
3479
  headers.set("X-Content-Raw", "1");
@@ -2371,16 +3484,27 @@ function serverComponentResponse(component, options = {}, init = {}) {
2371
3484
  version
2372
3485
  }
2373
3486
  });
3487
+ let closed = false;
2374
3488
  const body = new ReadableStream({
2375
3489
  start(controller) {
2376
3490
  stream.pipe({
2377
3491
  write(chunk) {
2378
- controller.enqueue(createChunk(JSON.stringify(chunk)));
3492
+ if (closed) return;
3493
+ try {
3494
+ controller.enqueue(createChunk(JSON.stringify(chunk)));
3495
+ } catch (_) {
3496
+ closed = true;
3497
+ }
2379
3498
  },
2380
3499
  end() {
3500
+ if (closed) return;
3501
+ closed = true;
2381
3502
  controller.close();
2382
3503
  }
2383
3504
  });
3505
+ },
3506
+ cancel() {
3507
+ closed = true;
2384
3508
  }
2385
3509
  });
2386
3510
  return new Response(body, {
@@ -2459,14 +3583,22 @@ function frameFlightResponse({
2459
3583
  codec
2460
3584
  }, init = {}) {
2461
3585
  const frames = primary ? [primary, ...regions] : regions;
2462
- const headers = new Headers(init.headers);
3586
+ const headers = copyInitHeaders(init.headers);
2463
3587
  headers.set("Content-Type", "application/x-frame-stream");
2464
3588
  headers.set(FRAME_STREAM_HEADER, primary ? primary.id : "");
2465
3589
  headers.set("X-Content-Raw", "1");
2466
3590
  headers.set(SINGLE_FLIGHT_HEADER, "true");
3591
+ let closed = false;
2467
3592
  const body = new ReadableStream({
2468
3593
  async start(controller) {
2469
- const write = chunk => controller.enqueue(createChunk(JSON.stringify(chunk)));
3594
+ const write = chunk => {
3595
+ if (closed) return;
3596
+ try {
3597
+ controller.enqueue(createChunk(JSON.stringify(chunk)));
3598
+ } catch (_) {
3599
+ closed = true;
3600
+ }
3601
+ };
2470
3602
  try {
2471
3603
  for (const {
2472
3604
  id,
@@ -2493,10 +3625,19 @@ function frameFlightResponse({
2493
3625
  });
2494
3626
  }
2495
3627
  }
2496
- controller.close();
3628
+ if (!closed) {
3629
+ closed = true;
3630
+ controller.close();
3631
+ }
2497
3632
  } catch (err) {
2498
- controller.error(err);
3633
+ if (!closed) {
3634
+ closed = true;
3635
+ controller.error(err);
3636
+ }
2499
3637
  }
3638
+ },
3639
+ cancel() {
3640
+ closed = true;
2500
3641
  }
2501
3642
  });
2502
3643
  return new Response(body, {
@@ -2505,4 +3646,9 @@ function frameFlightResponse({
2505
3646
  });
2506
3647
  }
2507
3648
 
2508
- export { FRAME_STREAM_HEADER, SERVER_COMPONENT_BOOTSTRAP, ServerComponentPlugin, createFrameSink, frameTransformDirectResult, frameTransformFlightResult, frameTransformResult, isFrameStreamResponse, renderServerComponent, renderToFrameStream, serverComponentResponse };
3649
+ setContainerTraceResolver(getProjectionTrace);
3650
+ function asyncArg(value) {
3651
+ return value;
3652
+ }
3653
+
3654
+ export { FRAME_STREAM_HEADER, SERVER_COMPONENT_BOOTSTRAP, ServerComponentPlugin, asyncArg, createFrameSink, frameTransformDirectResult, frameTransformFlightResult, frameTransformResult, isFrameStreamResponse, renderServerComponent, renderToFrameStream, serverComponentResponse };