@torpor/view 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -87,5 +87,24 @@ declare function fromServer(url: string | (() => string)): StreamSource<MessageE
87
87
  */
88
88
  declare function fromWebSocket(url: string | (() => string)): StreamSource<MessageEvent>;
89
89
  //#endregion
90
- export { buildStyles as a, ClassValue as c, StreamSource as i, fromServer as n, StyleValue as o, fromElement as r, buildClasses as s, fromWebSocket as t };
91
- //# sourceMappingURL=fromWebSocket-DeB35Q4w.d.cts.map
90
+ //#region src/types/AsyncOptions.d.ts
91
+ /**
92
+ * Options for the `$async` getter primitive.
93
+ */
94
+ interface AsyncOptions {
95
+ /**
96
+ * Where the value comes from. The default (`"client"`) starts the fetch
97
+ * after hydration; `"server"` fetches during SSR and ships the resolved
98
+ * value embedded in the HTML (see ASYNC.md → "Server rendering").
99
+ */
100
+ source?: "client" | "server";
101
+ /**
102
+ * With `source: "server"`, how long (ms) the server waits for this
103
+ * getter's promise before the enclosing `@await` boundary degrades to its
104
+ * `with` branch and the client fetches instead. Defaults to 5000.
105
+ */
106
+ timeout?: number;
107
+ }
108
+ //#endregion
109
+ export { StreamSource as a, buildClasses as c, fromElement as i, ClassValue as l, fromWebSocket as n, buildStyles as o, fromServer as r, StyleValue as s, AsyncOptions as t };
110
+ //# sourceMappingURL=AsyncOptions-BtMe2PAd.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsyncOptions-BtMe2PAd.d.cts","names":[],"sources":["../src/types/ClassValue.ts","../src/render/buildClasses.ts","../src/types/StyleValue.ts","../src/render/buildStyles.ts","../src/types/StreamSource.ts","../src/stream/fromElement.ts","../src/stream/fromServer.ts","../src/stream/fromWebSocket.ts","../src/types/AsyncOptions.ts"],"mappings":";;KAAK,sBAEF,6CACA,MAAM;;;iBCAe,aAAa,OAAO,YAAY;;;KCHnD,sBAEF,4CACA,MAAM;;;iBCCe,YAAY,OAAO;;;;;;;;;;;;;;;;;KCYtC,aAAa,MAAM,OAAO,OAAO,eAAe;;;;;;;;;;;;;;KCHhD,mBAAmB,qBAAqB;;;;;;;;;;;iBAYrB,YAAY,UAAU,QAAQ,OACrD,QAAQ,kBACR,eACE,aAAa;;;;;;;;;;;;;;;;;iBCZQ,WAAW,+BAA+B,aAAa;;;;;;;;;;;;;;;;;iBCAvD,cAAc,+BAA+B,aAAa;;;;;;UCbzD;;;;;;EAMxB;;;;;;EAOA"}
@@ -87,5 +87,24 @@ declare function fromServer(url: string | (() => string)): StreamSource<MessageE
87
87
  */
88
88
  declare function fromWebSocket(url: string | (() => string)): StreamSource<MessageEvent>;
89
89
  //#endregion
90
- export { buildStyles as a, ClassValue as c, StreamSource as i, fromServer as n, StyleValue as o, fromElement as r, buildClasses as s, fromWebSocket as t };
91
- //# sourceMappingURL=fromWebSocket-B52kN3-W.d.mts.map
90
+ //#region src/types/AsyncOptions.d.ts
91
+ /**
92
+ * Options for the `$async` getter primitive.
93
+ */
94
+ interface AsyncOptions {
95
+ /**
96
+ * Where the value comes from. The default (`"client"`) starts the fetch
97
+ * after hydration; `"server"` fetches during SSR and ships the resolved
98
+ * value embedded in the HTML (see ASYNC.md → "Server rendering").
99
+ */
100
+ source?: "client" | "server";
101
+ /**
102
+ * With `source: "server"`, how long (ms) the server waits for this
103
+ * getter's promise before the enclosing `@await` boundary degrades to its
104
+ * `with` branch and the client fetches instead. Defaults to 5000.
105
+ */
106
+ timeout?: number;
107
+ }
108
+ //#endregion
109
+ export { StreamSource as a, buildClasses as c, fromElement as i, ClassValue as l, fromWebSocket as n, buildStyles as o, fromServer as r, StyleValue as s, AsyncOptions as t };
110
+ //# sourceMappingURL=AsyncOptions-ClS1_ERX.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsyncOptions-ClS1_ERX.d.mts","names":[],"sources":["../src/types/ClassValue.ts","../src/render/buildClasses.ts","../src/types/StyleValue.ts","../src/render/buildStyles.ts","../src/types/StreamSource.ts","../src/stream/fromElement.ts","../src/stream/fromServer.ts","../src/stream/fromWebSocket.ts","../src/types/AsyncOptions.ts"],"mappings":";;KAAK,sBAEF,6CACA,MAAM;;;iBCAe,aAAa,OAAO,YAAY;;;KCHnD,sBAEF,4CACA,MAAM;;;iBCCe,YAAY,OAAO;;;;;;;;;;;;;;;;;KCYtC,aAAa,MAAM,OAAO,OAAO,eAAe;;;;;;;;;;;;;;KCHhD,mBAAmB,qBAAqB;;;;;;;;;;;iBAYrB,YAAY,UAAU,QAAQ,OACrD,QAAQ,kBACR,eACE,aAAa;;;;;;;;;;;;;;;;;iBCZQ,WAAW,+BAA+B,aAAa;;;;;;;;;;;;;;;;;iBCAvD,cAAc,+BAA+B,aAAa;;;;;;UCbzD;;;;;;EAMxB;;;;;;EAOA"}
@@ -168,7 +168,7 @@ interface Computed<T = any> {
168
168
  /**
169
169
  * True when the current suspend is a "bare refresh" — a re-fetch with no
170
170
  * tracked dependency change, i.e. a silent `$refresh(fn, { silent: true })`
171
- * (background revalidation), per ASYNC.md §7.4's quiet-on-refresh rule.
171
+ * (background revalidation), per ASYNC.md → "Loud vs quiet")
172
172
  * `$pending` reads this to stay quiet (return `false`) on silent refreshes,
173
173
  * matching Solid's stale-while-revalidate default.
174
174
  *
@@ -182,7 +182,7 @@ interface Computed<T = any> {
182
182
  suspendQuiet: boolean;
183
183
  /**
184
184
  * The previously resolved value, retained across a refresh suspend for
185
- * stale-while-revalidate (ASYNC.md §6.2). Maintained by `$async`'s
185
+ * stale-while-revalidate (ASYNC.md → "Stale-while-revalidate"). Maintained by `$async`'s
186
186
  * generation-guarded settle handlers — set on resolve, cleared on
187
187
  * rejection — so it always holds the last RESOLVED value, never a
188
188
  * superseded run's in-flight promise (rapid prop changes would otherwise
@@ -378,4 +378,4 @@ interface Effect {
378
378
  }
379
379
  //#endregion
380
380
  export { ProxySignal as i, Region as n, Computed as r, Effect as t };
381
- //# sourceMappingURL=Effect-BTKurOpr.d.mts.map
381
+ //# sourceMappingURL=Effect-DazmAdyC.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Effect-BTKurOpr.d.mts","names":[],"sources":["../src/types/constants.ts","../src/types/ProxySignal.ts","../src/types/Subscription.ts","../src/types/Computed.ts","../src/types/ErrorBoundary.ts","../src/types/Region.ts","../src/types/Effect.ts"],"mappings":";;;;;cAGa;;;;cAKA;;;;cAKA;;;;;;;UCNY;;;;EAIxB,aAAa;;;;EAKb,aAAa;;;;EAKb,oBAAoB;;;;EAKpB;;;;;;;;UClBwB;;;;EAIxB,QAAQ,cAAc;;;;EAKtB,QAAQ,WAAW;;;;;EAMnB,YAAY;;;;;EAMZ,YAAY;;;;;;;EAQZ,gBAAgB;;;;;;EAOhB;;;;;EAMA;;;;EAKA;;;;;;;;;UC/CwB,SAAS;;;;EAIjC,aAAa;;;;;;;;EASb;;;;EAKA,OAAO;;;;EAKP,WAAW;;;;EAKX,aAAa;;;;EAKb,aAAa;;;;;;;EAQb;;;;EAKA;;;;EAKA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;;;EAUA;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;EAcA;;;;EAKA,UAAU;;;;EAKV;;;;;;;;;;UC3HwB;;;;;;EAMxB;;;;;EAMA;;;;;EAMA,QAAQ;;;;;;;;;EAUR,cAAc,cAAc;;;;UCnCJ;EACxB,WAAW;EACX,SAAS;EAET,gBAAgB;EAChB,YAAY;EACZ;;;;;EAMA,YAAY,IAAI;;;;EAKhB;;;;EAKA,SAAS;;;;;;;EAQT,gBAAgB;;;;;;;EAQhB;;;;;;EAOA;;;;;;;UCtCwB;;;;EAIxB,aAAa;;;;;EAMb,WAAW;;;;EAKX,SAAS;;;;EAKT,aAAa;EAKb,YAAY;;;;EAKZ;;;;;;EAOA;;;;EAKA;;;;;;;EAQA;;;;;;;;;;EAWA,iBAAiB,IAAI;;;;EAKrB;;;;;;EAOA,SAAS;;;;;;EAOT,gBAAgB,cAAc;;;;;;;;;EAU9B;;;;;;;;;;;;;;;;EAiBA"}
1
+ {"version":3,"file":"Effect-DazmAdyC.d.mts","names":[],"sources":["../src/types/constants.ts","../src/types/ProxySignal.ts","../src/types/Subscription.ts","../src/types/Computed.ts","../src/types/ErrorBoundary.ts","../src/types/Region.ts","../src/types/Effect.ts"],"mappings":";;;;;cAGa;;;;cAKA;;;;cAKA;;;;;;;UCNY;;;;EAIxB,aAAa;;;;EAKb,aAAa;;;;EAKb,oBAAoB;;;;EAKpB;;;;;;;;UClBwB;;;;EAIxB,QAAQ,cAAc;;;;EAKtB,QAAQ,WAAW;;;;;EAMnB,YAAY;;;;;EAMZ,YAAY;;;;;;;EAQZ,gBAAgB;;;;;;EAOhB;;;;;EAMA;;;;EAKA;;;;;;;;;UC/CwB,SAAS;;;;EAIjC,aAAa;;;;;;;;EASb;;;;EAKA,OAAO;;;;EAKP,WAAW;;;;EAKX,aAAa;;;;EAKb,aAAa;;;;;;;EAQb;;;;EAKA;;;;EAKA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;;;EAUA;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;EAcA;;;;EAKA,UAAU;;;;EAKV;;;;;;;;;;UC3HwB;;;;;;EAMxB;;;;;EAMA;;;;;EAMA,QAAQ;;;;;;;;;EAUR,cAAc,cAAc;;;;UCnCJ;EACxB,WAAW;EACX,SAAS;EAET,gBAAgB;EAChB,YAAY;EACZ;;;;;EAMA,YAAY,IAAI;;;;EAKhB;;;;EAKA,SAAS;;;;;;;EAQT,gBAAgB;;;;;;;EAQhB;;;;;;EAOA;;;;;;;UCtCwB;;;;EAIxB,aAAa;;;;;EAMb,WAAW;;;;EAKX,SAAS;;;;EAKT,aAAa;EAKb,YAAY;;;;EAKZ;;;;;;EAOA;;;;EAKA;;;;;;;EAQA;;;;;;;;;;EAWA,iBAAiB,IAAI;;;;EAKrB;;;;;;EAOA,SAAS;;;;;;EAOT,gBAAgB,cAAc;;;;;;;;;EAU9B;;;;;;;;;;;;;;;;EAiBA"}
@@ -168,7 +168,7 @@ interface Computed<T = any> {
168
168
  /**
169
169
  * True when the current suspend is a "bare refresh" — a re-fetch with no
170
170
  * tracked dependency change, i.e. a silent `$refresh(fn, { silent: true })`
171
- * (background revalidation), per ASYNC.md §7.4's quiet-on-refresh rule.
171
+ * (background revalidation), per ASYNC.md → "Loud vs quiet")
172
172
  * `$pending` reads this to stay quiet (return `false`) on silent refreshes,
173
173
  * matching Solid's stale-while-revalidate default.
174
174
  *
@@ -182,7 +182,7 @@ interface Computed<T = any> {
182
182
  suspendQuiet: boolean;
183
183
  /**
184
184
  * The previously resolved value, retained across a refresh suspend for
185
- * stale-while-revalidate (ASYNC.md §6.2). Maintained by `$async`'s
185
+ * stale-while-revalidate (ASYNC.md → "Stale-while-revalidate"). Maintained by `$async`'s
186
186
  * generation-guarded settle handlers — set on resolve, cleared on
187
187
  * rejection — so it always holds the last RESOLVED value, never a
188
188
  * superseded run's in-flight promise (rapid prop changes would otherwise
@@ -378,4 +378,4 @@ interface Effect {
378
378
  }
379
379
  //#endregion
380
380
  export { ProxySignal as i, Region as n, Computed as r, Effect as t };
381
- //# sourceMappingURL=Effect-COl3aRJV.d.cts.map
381
+ //# sourceMappingURL=Effect-Dewn7MLc.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Effect-COl3aRJV.d.cts","names":[],"sources":["../src/types/constants.ts","../src/types/ProxySignal.ts","../src/types/Subscription.ts","../src/types/Computed.ts","../src/types/ErrorBoundary.ts","../src/types/Region.ts","../src/types/Effect.ts"],"mappings":";;;;;cAGa;;;;cAKA;;;;cAKA;;;;;;;UCNY;;;;EAIxB,aAAa;;;;EAKb,aAAa;;;;EAKb,oBAAoB;;;;EAKpB;;;;;;;;UClBwB;;;;EAIxB,QAAQ,cAAc;;;;EAKtB,QAAQ,WAAW;;;;;EAMnB,YAAY;;;;;EAMZ,YAAY;;;;;;;EAQZ,gBAAgB;;;;;;EAOhB;;;;;EAMA;;;;EAKA;;;;;;;;;UC/CwB,SAAS;;;;EAIjC,aAAa;;;;;;;;EASb;;;;EAKA,OAAO;;;;EAKP,WAAW;;;;EAKX,aAAa;;;;EAKb,aAAa;;;;;;;EAQb;;;;EAKA;;;;EAKA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;;;EAUA;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;EAcA;;;;EAKA,UAAU;;;;EAKV;;;;;;;;;;UC3HwB;;;;;;EAMxB;;;;;EAMA;;;;;EAMA,QAAQ;;;;;;;;;EAUR,cAAc,cAAc;;;;UCnCJ;EACxB,WAAW;EACX,SAAS;EAET,gBAAgB;EAChB,YAAY;EACZ;;;;;EAMA,YAAY,IAAI;;;;EAKhB;;;;EAKA,SAAS;;;;;;;EAQT,gBAAgB;;;;;;;EAQhB;;;;;;EAOA;;;;;;;UCtCwB;;;;EAIxB,aAAa;;;;;EAMb,WAAW;;;;EAKX,SAAS;;;;EAKT,aAAa;EAKb,YAAY;;;;EAKZ;;;;;;EAOA;;;;EAKA;;;;;;;EAQA;;;;;;;;;;EAWA,iBAAiB,IAAI;;;;EAKrB;;;;;;EAOA,SAAS;;;;;;EAOT,gBAAgB,cAAc;;;;;;;;;EAU9B;;;;;;;;;;;;;;;;EAiBA"}
1
+ {"version":3,"file":"Effect-Dewn7MLc.d.cts","names":[],"sources":["../src/types/constants.ts","../src/types/ProxySignal.ts","../src/types/Subscription.ts","../src/types/Computed.ts","../src/types/ErrorBoundary.ts","../src/types/Region.ts","../src/types/Effect.ts"],"mappings":";;;;;cAGa;;;;cAKA;;;;cAKA;;;;;;;UCNY;;;;EAIxB,aAAa;;;;EAKb,aAAa;;;;EAKb,oBAAoB;;;;EAKpB;;;;;;;;UClBwB;;;;EAIxB,QAAQ,cAAc;;;;EAKtB,QAAQ,WAAW;;;;;EAMnB,YAAY;;;;;EAMZ,YAAY;;;;;;;EAQZ,gBAAgB;;;;;;EAOhB;;;;;EAMA;;;;EAKA;;;;;;;;;UC/CwB,SAAS;;;;EAIjC,aAAa;;;;;;;;EASb;;;;EAKA,OAAO;;;;EAKP,WAAW;;;;EAKX,aAAa;;;;EAKb,aAAa;;;;;;;EAQb;;;;EAKA;;;;EAKA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;EAQA;;;;;;;;;EAUA;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;EAcA;;;;EAKA,UAAU;;;;EAKV;;;;;;;;;;UC3HwB;;;;;;EAMxB;;;;;EAMA;;;;;EAMA,QAAQ;;;;;;;;;EAUR,cAAc,cAAc;;;;UCnCJ;EACxB,WAAW;EACX,SAAS;EAET,gBAAgB;EAChB,YAAY;EACZ;;;;;EAMA,YAAY,IAAI;;;;EAKhB;;;;EAKA,SAAS;;;;;;;EAQT,gBAAgB;;;;;;;EAQhB;;;;;;EAOA;;;;;;;UCtCwB;;;;EAIxB,aAAa;;;;;EAMb,WAAW;;;;EAKX,SAAS;;;;EAKT,aAAa;EAKb,YAAY;;;;EAKZ;;;;;;EAOA;;;;EAKA;;;;;;;EAQA;;;;;;;;;;EAWA,iBAAiB,IAAI;;;;EAKrB;;;;;;EAOA,SAAS;;;;;;EAOT,gBAAgB,cAAc;;;;;;;;;EAU9B;;;;;;;;;;;;;;;;EAiBA"}
package/dist/compile.cjs CHANGED
@@ -2800,7 +2800,7 @@ function buildServerComponentNode(node, status, b) {
2800
2800
  const nameAttribute = slot.attributes.find((a) => a.name === "name");
2801
2801
  const slotName = nameAttribute?.value ? trimQuotes(nameAttribute.value) : "_";
2802
2802
  const slotParams = [slot.hasSlotProps ? "$slot: Record<PropertyKey, any>" : "_$slot?: Record<PropertyKey, any>", "// @ts-ignore\n// eslint-disable-next-line no-unused-vars\n$context?: Record<PropertyKey, any>"];
2803
- b.append(`${slot.hasSlotProps ? "// @ts-ignore\n" : ""}${slotsName}["${slotName}"] = (\n${slotParams.join(",\n")}\n) => {`);
2803
+ b.append(`${slot.hasSlotProps ? "// @ts-ignore\n" : ""}${slotsName}["${slotName}"] = async (\n${slotParams.join(",\n")}\n) => {`);
2804
2804
  b.append(`let t_body = "";`);
2805
2805
  for (let child of slot.children) buildServerNode(child, status, b);
2806
2806
  if (status.output) {
@@ -2819,7 +2819,7 @@ function buildServerComponentNode(node, status, b) {
2819
2819
  let renderParams = `${propsName}, $context`;
2820
2820
  if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
2821
2821
  const componentResult = nextVarName("comp", status);
2822
- b.append(`const ${componentResult} = ${componentName}(${renderParams});`);
2822
+ b.append(`const ${componentResult} = await ${componentName}(${renderParams});`);
2823
2823
  b.append(`${status.inHead ? "t_head" : "t_body"} += ${componentResult}.body;`);
2824
2824
  b.append(`t_head += ${componentResult}.head;`);
2825
2825
  status.output += HYDRATION_END_COMMENT;
@@ -2827,16 +2827,37 @@ function buildServerComponentNode(node, status, b) {
2827
2827
  }
2828
2828
  //#endregion
2829
2829
  //#region src/compile/build/server/buildServerAwaitNode.ts
2830
+ /**
2831
+ * Builds an `@await` boundary for the server (ASYNC.md → "Server rendering").
2832
+ *
2833
+ * Rather than inlining the `with` branch (the old shape), the boundary is
2834
+ * handed to the `t_await_server` runtime helper: content and `with` branches
2835
+ * become closures, and the helper decides at render time whether resolved
2836
+ * content can ship (all `source: "server"` reads settled) or the `with`
2837
+ * branch + client fetch is the honest output. The runtime emits the
2838
+ * hydration markers and anchor itself, including the `<!--t-await:...-->`
2839
+ * values payload for resolved boundaries.
2840
+ */
2830
2841
  function buildServerAwaitNode(node, status, b) {
2831
- status.output += HYDRATION_START_COMMENT;
2832
2842
  flushOutput(status, b);
2833
- for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") buildServerAwaitBranch(branch, status, b);
2834
- status.output += HYDRATION_END_COMMENT;
2835
- status.output += "<!>";
2836
- }
2837
- function buildServerAwaitBranch(node, status, b) {
2838
- for (let child of node.children) buildServerNode(child, status, b);
2843
+ status.imports.add("t_await_server");
2844
+ const id = status.awaitCount++;
2845
+ const resultName = nextVarName("await", status);
2846
+ b.append(`const ${resultName} = await t_await_server("${id}", async () => {`);
2847
+ b.append(`let t_body = "";`);
2848
+ b.append(`let t_head = "";`);
2849
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@await") for (let child of branch.children) buildServerNode(child, status, b);
2839
2850
  flushOutput(status, b);
2851
+ b.append(`return { body: t_body, head: t_head };`);
2852
+ b.append(`}, async () => {`);
2853
+ b.append(`let t_body = "";`);
2854
+ b.append(`let t_head = "";`);
2855
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") for (let child of branch.children) buildServerNode(child, status, b);
2856
+ flushOutput(status, b);
2857
+ b.append(`return { body: t_body, head: t_head };`);
2858
+ b.append(`});`);
2859
+ b.append(`${status.inHead ? "t_head" : "t_body"} += ${resultName}.body;`);
2860
+ b.append(`t_head += ${resultName}.head;`);
2840
2861
  }
2841
2862
  //#endregion
2842
2863
  //#region src/compile/build/server/buildServerForNode.ts
@@ -3091,7 +3112,7 @@ function buildServerSlotNode(node, status, b) {
3091
3112
  else b.append(`${propsName}["${name}"] = true;`);
3092
3113
  }
3093
3114
  b.append(`if ($slots && $slots["${slotName}"]) {`);
3094
- b.append(`${status.inHead ? "t_head" : "t_body"} += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3115
+ b.append(`${status.inHead ? "t_head" : "t_body"} += await $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3095
3116
  const fill = node.children.find((c) => isSpecialNode(c) && (c.tagName === "fill" || c.tagName === "filldef"));
3096
3117
  if (fill && isSpecialNode(fill) && fill.children.length) {
3097
3118
  b.append(`} else {`);
@@ -3194,6 +3215,8 @@ const importsMap = {
3194
3215
  $unwrap: "import { $unwrap } from \"${folder}\";",
3195
3216
  $peek: "import { $peek } from \"${folder}\";",
3196
3217
  $batch: "import { $batch } from \"${folder}\";",
3218
+ t_server_flush: "import { t_server_flush } from \"${folder}\";",
3219
+ t_await_server: "import { t_await_server } from \"${folder}\";",
3197
3220
  t_fmt: "import { t_fmt } from \"${folder}\";",
3198
3221
  t_attr: "import { t_attr } from \"${folder}\";",
3199
3222
  t_class: "import { t_class } from \"${folder}\";",
@@ -3236,18 +3259,24 @@ function buildServerTemplate(template, imports, b, options) {
3236
3259
  if (/\$unwrap\b/.test(scriptCode)) imports.add("$unwrap");
3237
3260
  if (/\$peek\b/.test(scriptCode)) imports.add("$peek");
3238
3261
  if (/\$batch\b/.test(scriptCode)) imports.add("$batch");
3262
+ const asyncChunks = /* @__PURE__ */ new Set();
3263
+ for (let [i, chunk] of template.script.entries()) if (chunk.script === "/* @params */" && i > 0) asyncChunks.add(i - 1);
3239
3264
  let currentIndex = 0;
3240
3265
  let current = template.components[0];
3241
- for (let chunk of template.script) if (chunk.script === "/* @params */") {
3266
+ for (let [chunkIndex, chunk] of template.script.entries()) if (chunk.script === "/* @params */") {
3242
3267
  let params = [
3243
3268
  current.params ?? `${current.props?.length ? "$props: Record<PropertyKey, any>" : "_$props?: Record<PropertyKey, any>"}`,
3244
3269
  `${current.contextProps?.length || current.markup && markupRendersComponent(current.markup) || current.error && markupRendersComponent(current.error) ? "$context" : "_$context"}?: Record<PropertyKey, any>`,
3245
3270
  `${current.slotProps?.length ? "$slots" : "_$slots"}?: Record<string, ServerSlotRender>`
3246
3271
  ];
3247
3272
  b.append(params.join(",\n") + ",");
3248
- } else if (chunk.script === ") /* @return_type */ {") b.append("): { body: string; head: string } {");
3273
+ } else if (chunk.script === ") /* @return_type */ {") b.append("): Promise<{ body: string; head: string }> {");
3249
3274
  else if (chunk.script === "/* @start */") {
3250
3275
  if (current.contextProps?.length) b.append(`$context = Object.assign({}, $context);`);
3276
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) {
3277
+ imports.add("t_server_flush");
3278
+ b.append(`return t_server_flush(async () => {`);
3279
+ }
3251
3280
  b.append(`let t_body = "";`);
3252
3281
  b.append(`let t_head = "";`);
3253
3282
  } else if (chunk.script === "/* @render */") {
@@ -3258,6 +3287,7 @@ function buildServerTemplate(template, imports, b, options) {
3258
3287
  styleHash: current.style?.hash || "",
3259
3288
  varNames: {},
3260
3289
  preserveWhitespace: false,
3290
+ awaitCount: 0,
3261
3291
  options
3262
3292
  };
3263
3293
  b.append("");
@@ -3281,6 +3311,7 @@ function buildServerTemplate(template, imports, b, options) {
3281
3311
  styleHash: current.style?.hash || "",
3282
3312
  varNames: {},
3283
3313
  preserveWhitespace: false,
3314
+ awaitCount: 0,
3284
3315
  options
3285
3316
  };
3286
3317
  b.append(`} catch (${current.errorVar}) {`);
@@ -3303,6 +3334,7 @@ function buildServerTemplate(template, imports, b, options) {
3303
3334
  varNames: {},
3304
3335
  preserveWhitespace: false,
3305
3336
  inHead: true,
3337
+ awaitCount: 0,
3306
3338
  options
3307
3339
  };
3308
3340
  b.append("");
@@ -3319,9 +3351,23 @@ function buildServerTemplate(template, imports, b, options) {
3319
3351
  }
3320
3352
  } else if (chunk.script === "/* @end */") {
3321
3353
  b.append(`return { body: t_body, head: t_head };`);
3354
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) b.append(`});`);
3322
3355
  currentIndex += 1;
3323
3356
  current = template.components[currentIndex];
3324
- } else b.append(chunk.script);
3357
+ } else if (asyncChunks.has(chunkIndex)) b.append(chunk.script.replace(/(?<!async\s)function\s+([A-Za-z_$][\w$]*)((?:<(?:[^<>]|<[^<>]*>)*>)?)\s*\($/, "async function $1$2("));
3358
+ else b.append(chunk.script);
3359
+ }
3360
+ /**
3361
+ * Whether a markup tree contains an `@await` boundary, so the component's
3362
+ * render must be wrapped in a flush.
3363
+ */
3364
+ function containsAwaitGroup(node) {
3365
+ if (node === void 0 || node === null) return false;
3366
+ if (isControlNode(node) && node.operation === "@await group") return true;
3367
+ if (node.type !== "text" && node.type !== "comment") {
3368
+ for (let child of node.children) if (containsAwaitGroup(child)) return true;
3369
+ }
3370
+ return false;
3325
3371
  }
3326
3372
  //#endregion
3327
3373
  //#region src/compile/utils/trimWhitespace.ts
package/dist/compile.mjs CHANGED
@@ -2799,7 +2799,7 @@ function buildServerComponentNode(node, status, b) {
2799
2799
  const nameAttribute = slot.attributes.find((a) => a.name === "name");
2800
2800
  const slotName = nameAttribute?.value ? trimQuotes(nameAttribute.value) : "_";
2801
2801
  const slotParams = [slot.hasSlotProps ? "$slot: Record<PropertyKey, any>" : "_$slot?: Record<PropertyKey, any>", "// @ts-ignore\n// eslint-disable-next-line no-unused-vars\n$context?: Record<PropertyKey, any>"];
2802
- b.append(`${slot.hasSlotProps ? "// @ts-ignore\n" : ""}${slotsName}["${slotName}"] = (\n${slotParams.join(",\n")}\n) => {`);
2802
+ b.append(`${slot.hasSlotProps ? "// @ts-ignore\n" : ""}${slotsName}["${slotName}"] = async (\n${slotParams.join(",\n")}\n) => {`);
2803
2803
  b.append(`let t_body = "";`);
2804
2804
  for (let child of slot.children) buildServerNode(child, status, b);
2805
2805
  if (status.output) {
@@ -2818,7 +2818,7 @@ function buildServerComponentNode(node, status, b) {
2818
2818
  let renderParams = `${propsName}, $context`;
2819
2819
  if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
2820
2820
  const componentResult = nextVarName("comp", status);
2821
- b.append(`const ${componentResult} = ${componentName}(${renderParams});`);
2821
+ b.append(`const ${componentResult} = await ${componentName}(${renderParams});`);
2822
2822
  b.append(`${status.inHead ? "t_head" : "t_body"} += ${componentResult}.body;`);
2823
2823
  b.append(`t_head += ${componentResult}.head;`);
2824
2824
  status.output += HYDRATION_END_COMMENT;
@@ -2826,16 +2826,37 @@ function buildServerComponentNode(node, status, b) {
2826
2826
  }
2827
2827
  //#endregion
2828
2828
  //#region src/compile/build/server/buildServerAwaitNode.ts
2829
+ /**
2830
+ * Builds an `@await` boundary for the server (ASYNC.md → "Server rendering").
2831
+ *
2832
+ * Rather than inlining the `with` branch (the old shape), the boundary is
2833
+ * handed to the `t_await_server` runtime helper: content and `with` branches
2834
+ * become closures, and the helper decides at render time whether resolved
2835
+ * content can ship (all `source: "server"` reads settled) or the `with`
2836
+ * branch + client fetch is the honest output. The runtime emits the
2837
+ * hydration markers and anchor itself, including the `<!--t-await:...-->`
2838
+ * values payload for resolved boundaries.
2839
+ */
2829
2840
  function buildServerAwaitNode(node, status, b) {
2830
- status.output += HYDRATION_START_COMMENT;
2831
2841
  flushOutput(status, b);
2832
- for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") buildServerAwaitBranch(branch, status, b);
2833
- status.output += HYDRATION_END_COMMENT;
2834
- status.output += "<!>";
2835
- }
2836
- function buildServerAwaitBranch(node, status, b) {
2837
- for (let child of node.children) buildServerNode(child, status, b);
2842
+ status.imports.add("t_await_server");
2843
+ const id = status.awaitCount++;
2844
+ const resultName = nextVarName("await", status);
2845
+ b.append(`const ${resultName} = await t_await_server("${id}", async () => {`);
2846
+ b.append(`let t_body = "";`);
2847
+ b.append(`let t_head = "";`);
2848
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@await") for (let child of branch.children) buildServerNode(child, status, b);
2838
2849
  flushOutput(status, b);
2850
+ b.append(`return { body: t_body, head: t_head };`);
2851
+ b.append(`}, async () => {`);
2852
+ b.append(`let t_body = "";`);
2853
+ b.append(`let t_head = "";`);
2854
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") for (let child of branch.children) buildServerNode(child, status, b);
2855
+ flushOutput(status, b);
2856
+ b.append(`return { body: t_body, head: t_head };`);
2857
+ b.append(`});`);
2858
+ b.append(`${status.inHead ? "t_head" : "t_body"} += ${resultName}.body;`);
2859
+ b.append(`t_head += ${resultName}.head;`);
2839
2860
  }
2840
2861
  //#endregion
2841
2862
  //#region src/compile/build/server/buildServerForNode.ts
@@ -3090,7 +3111,7 @@ function buildServerSlotNode(node, status, b) {
3090
3111
  else b.append(`${propsName}["${name}"] = true;`);
3091
3112
  }
3092
3113
  b.append(`if ($slots && $slots["${slotName}"]) {`);
3093
- b.append(`${status.inHead ? "t_head" : "t_body"} += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3114
+ b.append(`${status.inHead ? "t_head" : "t_body"} += await $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3094
3115
  const fill = node.children.find((c) => isSpecialNode(c) && (c.tagName === "fill" || c.tagName === "filldef"));
3095
3116
  if (fill && isSpecialNode(fill) && fill.children.length) {
3096
3117
  b.append(`} else {`);
@@ -3193,6 +3214,8 @@ const importsMap = {
3193
3214
  $unwrap: "import { $unwrap } from \"${folder}\";",
3194
3215
  $peek: "import { $peek } from \"${folder}\";",
3195
3216
  $batch: "import { $batch } from \"${folder}\";",
3217
+ t_server_flush: "import { t_server_flush } from \"${folder}\";",
3218
+ t_await_server: "import { t_await_server } from \"${folder}\";",
3196
3219
  t_fmt: "import { t_fmt } from \"${folder}\";",
3197
3220
  t_attr: "import { t_attr } from \"${folder}\";",
3198
3221
  t_class: "import { t_class } from \"${folder}\";",
@@ -3235,18 +3258,24 @@ function buildServerTemplate(template, imports, b, options) {
3235
3258
  if (/\$unwrap\b/.test(scriptCode)) imports.add("$unwrap");
3236
3259
  if (/\$peek\b/.test(scriptCode)) imports.add("$peek");
3237
3260
  if (/\$batch\b/.test(scriptCode)) imports.add("$batch");
3261
+ const asyncChunks = /* @__PURE__ */ new Set();
3262
+ for (let [i, chunk] of template.script.entries()) if (chunk.script === "/* @params */" && i > 0) asyncChunks.add(i - 1);
3238
3263
  let currentIndex = 0;
3239
3264
  let current = template.components[0];
3240
- for (let chunk of template.script) if (chunk.script === "/* @params */") {
3265
+ for (let [chunkIndex, chunk] of template.script.entries()) if (chunk.script === "/* @params */") {
3241
3266
  let params = [
3242
3267
  current.params ?? `${current.props?.length ? "$props: Record<PropertyKey, any>" : "_$props?: Record<PropertyKey, any>"}`,
3243
3268
  `${current.contextProps?.length || current.markup && markupRendersComponent(current.markup) || current.error && markupRendersComponent(current.error) ? "$context" : "_$context"}?: Record<PropertyKey, any>`,
3244
3269
  `${current.slotProps?.length ? "$slots" : "_$slots"}?: Record<string, ServerSlotRender>`
3245
3270
  ];
3246
3271
  b.append(params.join(",\n") + ",");
3247
- } else if (chunk.script === ") /* @return_type */ {") b.append("): { body: string; head: string } {");
3272
+ } else if (chunk.script === ") /* @return_type */ {") b.append("): Promise<{ body: string; head: string }> {");
3248
3273
  else if (chunk.script === "/* @start */") {
3249
3274
  if (current.contextProps?.length) b.append(`$context = Object.assign({}, $context);`);
3275
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) {
3276
+ imports.add("t_server_flush");
3277
+ b.append(`return t_server_flush(async () => {`);
3278
+ }
3250
3279
  b.append(`let t_body = "";`);
3251
3280
  b.append(`let t_head = "";`);
3252
3281
  } else if (chunk.script === "/* @render */") {
@@ -3257,6 +3286,7 @@ function buildServerTemplate(template, imports, b, options) {
3257
3286
  styleHash: current.style?.hash || "",
3258
3287
  varNames: {},
3259
3288
  preserveWhitespace: false,
3289
+ awaitCount: 0,
3260
3290
  options
3261
3291
  };
3262
3292
  b.append("");
@@ -3280,6 +3310,7 @@ function buildServerTemplate(template, imports, b, options) {
3280
3310
  styleHash: current.style?.hash || "",
3281
3311
  varNames: {},
3282
3312
  preserveWhitespace: false,
3313
+ awaitCount: 0,
3283
3314
  options
3284
3315
  };
3285
3316
  b.append(`} catch (${current.errorVar}) {`);
@@ -3302,6 +3333,7 @@ function buildServerTemplate(template, imports, b, options) {
3302
3333
  varNames: {},
3303
3334
  preserveWhitespace: false,
3304
3335
  inHead: true,
3336
+ awaitCount: 0,
3305
3337
  options
3306
3338
  };
3307
3339
  b.append("");
@@ -3318,9 +3350,23 @@ function buildServerTemplate(template, imports, b, options) {
3318
3350
  }
3319
3351
  } else if (chunk.script === "/* @end */") {
3320
3352
  b.append(`return { body: t_body, head: t_head };`);
3353
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) b.append(`});`);
3321
3354
  currentIndex += 1;
3322
3355
  current = template.components[currentIndex];
3323
- } else b.append(chunk.script);
3356
+ } else if (asyncChunks.has(chunkIndex)) b.append(chunk.script.replace(/(?<!async\s)function\s+([A-Za-z_$][\w$]*)((?:<(?:[^<>]|<[^<>]*>)*>)?)\s*\($/, "async function $1$2("));
3357
+ else b.append(chunk.script);
3358
+ }
3359
+ /**
3360
+ * Whether a markup tree contains an `@await` boundary, so the component's
3361
+ * render must be wrapped in a flush.
3362
+ */
3363
+ function containsAwaitGroup(node) {
3364
+ if (node === void 0 || node === null) return false;
3365
+ if (isControlNode(node) && node.operation === "@await group") return true;
3366
+ if (node.type !== "text" && node.type !== "comment") {
3367
+ for (let child of node.children) if (containsAwaitGroup(child)) return true;
3368
+ }
3369
+ return false;
3324
3370
  }
3325
3371
  //#endregion
3326
3372
  //#region src/compile/utils/trimWhitespace.ts