@torpor/view 1.0.3 → 1.1.1

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
@@ -2201,12 +2201,15 @@ function headElementSelector(node) {
2201
2201
  function buildElementAttributes$1(node, varName, status, b) {
2202
2202
  let fragment = status.fragmentStack.at(-1)?.fragment;
2203
2203
  if (!fragment) throw new Error("No fragment on stack");
2204
- for (let { name, value, reactive, fullyReactive, span } of node.attributes) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (value != null && fullyReactive) {
2205
- if (name === "&group") buildBindGroupAttribute(node, varName, value, status, b);
2206
- else if (name === "&value" || name === "&checked") buildBindAttribute(node, varName, name, value, status, b);
2204
+ const combinedHandlers = combinedEventHandlers(node, status);
2205
+ const consumedHandlers = new Set(Array.from(combinedHandlers.values(), (c) => c.index));
2206
+ for (let [index, { name, value, reactive, fullyReactive, span }] of node.attributes.entries()) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (value != null && fullyReactive) {
2207
+ if (name === "&group") buildBindGroupAttribute(node, varName, value, status, b, combinedHandlers.get(index));
2208
+ else if (name === "&value" || name === "&checked") buildBindAttribute(node, varName, name, value, status, b, combinedHandlers.get(index));
2207
2209
  else if (name === "onmount") buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
2208
- else if (name.startsWith("on")) buildEventAttribute(varName, name, value, span, status, b);
2209
- else if (name.startsWith("transition")) buildTransitionAttribute(node, varName, name, value, status, b);
2210
+ else if (name.startsWith("on")) {
2211
+ if (!consumedHandlers.has(index)) buildEventAttribute(varName, name, value, span, status, b);
2212
+ } else if (name.startsWith("transition")) buildTransitionAttribute(node, varName, name, value, status, b);
2210
2213
  else if (name === "class") {
2211
2214
  status.imports.add("t_class");
2212
2215
  const params = [value];
@@ -2244,21 +2247,20 @@ function buildElementAttributes$1(node, varName, status, b) {
2244
2247
  }
2245
2248
  }
2246
2249
  }
2247
- function buildBindGroupAttribute(node, varName, value, status, b) {
2250
+ function buildBindGroupAttribute(node, varName, value, status, b, combined) {
2248
2251
  value = replaceForVarNames(value, status);
2249
2252
  let eventName = "change";
2250
2253
  let inputValue = node.attributes.find((a) => a.name === "value")?.value;
2251
2254
  let set = `${value} == ${inputValue}`;
2252
2255
  let propName = "checked";
2253
2256
  buildRun("setBinding", `${`${varName}.${propName} = ${set}`};`, status, b);
2254
- status.imports.add("t_event");
2255
- b.append(`t_event(${varName}, "${eventName}", (e) => {
2256
- if (e.target.${propName}) ${value} = ${inputValue};
2257
- });`);
2257
+ emitBindEvent(varName, eventName, `(e) => {
2258
+ if (e.target.${propName}) ${value} = ${inputValue};
2259
+ }`, `if (e.target.${propName}) ${value} = ${inputValue};`, combined, status, b);
2258
2260
  }
2259
- function buildBindAttribute(node, varName, name, value, status, b) {
2261
+ function buildBindAttribute(node, varName, name, value, status, b, combined) {
2260
2262
  value = replaceForVarNames(value, status);
2261
- let eventName = "input";
2263
+ let eventName = bindEventName(node, name);
2262
2264
  let defaultValue = "\"\"";
2263
2265
  let inputValue = "e.target.value";
2264
2266
  if (node.tagName === "input") {
@@ -2271,25 +2273,83 @@ function buildBindAttribute(node, varName, name, value, status, b) {
2271
2273
  case "checkbox":
2272
2274
  defaultValue = "false";
2273
2275
  inputValue = "e.target.checked";
2274
- break;
2275
- case "radio":
2276
- eventName = "change";
2277
- inputValue = "e.target.value";
2278
2276
  }
2279
2277
  } else if (node.tagName === "select") {
2280
- eventName = "change";
2281
2278
  if (node.attributes.find((a) => a.name === "multiple")) {
2282
2279
  buildRun("setBinding", `Array.from(${varName}.options).forEach((opt) => opt.selected = Array.isArray(${value}) && ${value}.includes(opt.value));`, status, b);
2283
- status.imports.add("t_event");
2284
- b.append(`t_event(${varName}, "change", (e) => ${value} = Array.from(e.target.selectedOptions).map((opt) => opt.value));`);
2280
+ emitBindEvent(varName, eventName, `(e) => ${value} = Array.from(e.target.selectedOptions).map((opt) => opt.value)`, `${value} = Array.from(e.target.selectedOptions).map((opt) => opt.value);`, combined, status, b);
2285
2281
  return;
2286
2282
  }
2287
2283
  }
2288
2284
  let set = `${value} || ${defaultValue}`;
2289
2285
  const propName = name.substring(1);
2290
2286
  buildRun("setBinding", `${propName === "value" ? `${varName}.${propName} = String(${set})` : `${varName}.${propName} = ${set}`};`, status, b);
2287
+ emitBindEvent(varName, eventName, `(e) => ${value} = ${inputValue}`, `${value} = ${inputValue};`, combined, status, b);
2288
+ }
2289
+ /**
2290
+ * Gets the name of the event that a &-binding attribute uses to write the
2291
+ * element's state back to the bound value
2292
+ */
2293
+ function bindEventName(node, name) {
2294
+ if (name === "&group") return "change";
2295
+ if (node.tagName === "select") return "change";
2296
+ if (node.tagName === "input") {
2297
+ let typeAttribute = node.attributes.find((a) => a.name === "type");
2298
+ if (typeAttribute?.value && trimQuotes(typeAttribute.value) === "radio") return "change";
2299
+ }
2300
+ return "input";
2301
+ }
2302
+ /**
2303
+ * Pairs &-binding attributes (&value, &checked, &group) with user `on<event>`
2304
+ * handler attributes listening to the same event. Both would emit their own
2305
+ * t_event call for the element, but the second registration would clobber
2306
+ * the first -- typically the binding's state write -- so the pair is emitted
2307
+ * as one t_event that calls the binding write and the user handler in source
2308
+ * order. Returns a map from binding attribute index to the combined handler.
2309
+ */
2310
+ function combinedEventHandlers(node, status) {
2311
+ const bindings = /* @__PURE__ */ new Map();
2312
+ const handlers = /* @__PURE__ */ new Map();
2313
+ for (let [index, { name, value, fullyReactive }] of node.attributes.entries()) {
2314
+ if (value == null || !fullyReactive) continue;
2315
+ if (name === "&value" || name === "&checked" || name === "&group") bindings.set(bindEventName(node, name), index);
2316
+ else if (name.startsWith("on")) handlers.set(name.substring(2), index);
2317
+ }
2318
+ const combined = /* @__PURE__ */ new Map();
2319
+ for (let [eventName, bindingIndex] of bindings) {
2320
+ const handlerIndex = handlers.get(eventName);
2321
+ if (handlerIndex === void 0) continue;
2322
+ const handler = node.attributes[handlerIndex];
2323
+ combined.set(bindingIndex, {
2324
+ index: handlerIndex,
2325
+ value: replaceForVarNames(handler.value, status),
2326
+ span: handler.span,
2327
+ first: handlerIndex < bindingIndex
2328
+ });
2329
+ }
2330
+ return combined;
2331
+ }
2332
+ /**
2333
+ * Emits the t_event call for a &-binding, optionally combined with a user
2334
+ * `on<event>` handler for the same event (which is called after -- or, when
2335
+ * it appeared first in source, before -- the binding write)
2336
+ */
2337
+ function emitBindEvent(varName, eventName, listener, body, combined, status, b) {
2291
2338
  status.imports.add("t_event");
2292
- b.append(`t_event(${varName}, "${eventName}", (e) => ${value} = ${inputValue});`);
2339
+ if (!combined) {
2340
+ b.append(`t_event(${varName}, "${eventName}", ${listener});`);
2341
+ return;
2342
+ }
2343
+ b.append(`t_event(${varName}, "${eventName}", (e) => {`);
2344
+ const handlerCall = `((${combined.value}) as ((e: any) => any) | undefined)?.(e);`;
2345
+ if (combined.first) {
2346
+ addMappedText("", handlerCall, "", combined.span, status, b);
2347
+ b.append(body);
2348
+ } else {
2349
+ b.append(body);
2350
+ addMappedText("", handlerCall, "", combined.span, status, b);
2351
+ }
2352
+ b.append("});");
2293
2353
  }
2294
2354
  function buildEventAttribute(varName, name, value, span, status, b) {
2295
2355
  value = replaceForVarNames(value, status);
@@ -2800,7 +2860,7 @@ function buildServerComponentNode(node, status, b) {
2800
2860
  const nameAttribute = slot.attributes.find((a) => a.name === "name");
2801
2861
  const slotName = nameAttribute?.value ? trimQuotes(nameAttribute.value) : "_";
2802
2862
  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) => {`);
2863
+ b.append(`${slot.hasSlotProps ? "// @ts-ignore\n" : ""}${slotsName}["${slotName}"] = async (\n${slotParams.join(",\n")}\n) => {`);
2804
2864
  b.append(`let t_body = "";`);
2805
2865
  for (let child of slot.children) buildServerNode(child, status, b);
2806
2866
  if (status.output) {
@@ -2819,7 +2879,7 @@ function buildServerComponentNode(node, status, b) {
2819
2879
  let renderParams = `${propsName}, $context`;
2820
2880
  if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
2821
2881
  const componentResult = nextVarName("comp", status);
2822
- b.append(`const ${componentResult} = ${componentName}(${renderParams});`);
2882
+ b.append(`const ${componentResult} = await ${componentName}(${renderParams});`);
2823
2883
  b.append(`${status.inHead ? "t_head" : "t_body"} += ${componentResult}.body;`);
2824
2884
  b.append(`t_head += ${componentResult}.head;`);
2825
2885
  status.output += HYDRATION_END_COMMENT;
@@ -2827,16 +2887,37 @@ function buildServerComponentNode(node, status, b) {
2827
2887
  }
2828
2888
  //#endregion
2829
2889
  //#region src/compile/build/server/buildServerAwaitNode.ts
2890
+ /**
2891
+ * Builds an `@await` boundary for the server (ASYNC.md → "Server rendering").
2892
+ *
2893
+ * Rather than inlining the `with` branch (the old shape), the boundary is
2894
+ * handed to the `t_await_server` runtime helper: content and `with` branches
2895
+ * become closures, and the helper decides at render time whether resolved
2896
+ * content can ship (all `source: "server"` reads settled) or the `with`
2897
+ * branch + client fetch is the honest output. The runtime emits the
2898
+ * hydration markers and anchor itself, including the `<!--t-await:...-->`
2899
+ * values payload for resolved boundaries.
2900
+ */
2830
2901
  function buildServerAwaitNode(node, status, b) {
2831
- status.output += HYDRATION_START_COMMENT;
2832
2902
  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);
2903
+ status.imports.add("t_await_server");
2904
+ const id = status.awaitCount++;
2905
+ const resultName = nextVarName("await", status);
2906
+ b.append(`const ${resultName} = await t_await_server("${id}", async () => {`);
2907
+ b.append(`let t_body = "";`);
2908
+ b.append(`let t_head = "";`);
2909
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@await") for (let child of branch.children) buildServerNode(child, status, b);
2910
+ flushOutput(status, b);
2911
+ b.append(`return { body: t_body, head: t_head };`);
2912
+ b.append(`}, async () => {`);
2913
+ b.append(`let t_body = "";`);
2914
+ b.append(`let t_head = "";`);
2915
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") for (let child of branch.children) buildServerNode(child, status, b);
2839
2916
  flushOutput(status, b);
2917
+ b.append(`return { body: t_body, head: t_head };`);
2918
+ b.append(`});`);
2919
+ b.append(`${status.inHead ? "t_head" : "t_body"} += ${resultName}.body;`);
2920
+ b.append(`t_head += ${resultName}.head;`);
2840
2921
  }
2841
2922
  //#endregion
2842
2923
  //#region src/compile/build/server/buildServerForNode.ts
@@ -3091,7 +3172,7 @@ function buildServerSlotNode(node, status, b) {
3091
3172
  else b.append(`${propsName}["${name}"] = true;`);
3092
3173
  }
3093
3174
  b.append(`if ($slots && $slots["${slotName}"]) {`);
3094
- b.append(`${status.inHead ? "t_head" : "t_body"} += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3175
+ b.append(`${status.inHead ? "t_head" : "t_body"} += await $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3095
3176
  const fill = node.children.find((c) => isSpecialNode(c) && (c.tagName === "fill" || c.tagName === "filldef"));
3096
3177
  if (fill && isSpecialNode(fill) && fill.children.length) {
3097
3178
  b.append(`} else {`);
@@ -3194,6 +3275,8 @@ const importsMap = {
3194
3275
  $unwrap: "import { $unwrap } from \"${folder}\";",
3195
3276
  $peek: "import { $peek } from \"${folder}\";",
3196
3277
  $batch: "import { $batch } from \"${folder}\";",
3278
+ t_server_flush: "import { t_server_flush } from \"${folder}\";",
3279
+ t_await_server: "import { t_await_server } from \"${folder}\";",
3197
3280
  t_fmt: "import { t_fmt } from \"${folder}\";",
3198
3281
  t_attr: "import { t_attr } from \"${folder}\";",
3199
3282
  t_class: "import { t_class } from \"${folder}\";",
@@ -3236,18 +3319,24 @@ function buildServerTemplate(template, imports, b, options) {
3236
3319
  if (/\$unwrap\b/.test(scriptCode)) imports.add("$unwrap");
3237
3320
  if (/\$peek\b/.test(scriptCode)) imports.add("$peek");
3238
3321
  if (/\$batch\b/.test(scriptCode)) imports.add("$batch");
3322
+ const asyncChunks = /* @__PURE__ */ new Set();
3323
+ for (let [i, chunk] of template.script.entries()) if (chunk.script === "/* @params */" && i > 0) asyncChunks.add(i - 1);
3239
3324
  let currentIndex = 0;
3240
3325
  let current = template.components[0];
3241
- for (let chunk of template.script) if (chunk.script === "/* @params */") {
3326
+ for (let [chunkIndex, chunk] of template.script.entries()) if (chunk.script === "/* @params */") {
3242
3327
  let params = [
3243
3328
  current.params ?? `${current.props?.length ? "$props: Record<PropertyKey, any>" : "_$props?: Record<PropertyKey, any>"}`,
3244
3329
  `${current.contextProps?.length || current.markup && markupRendersComponent(current.markup) || current.error && markupRendersComponent(current.error) ? "$context" : "_$context"}?: Record<PropertyKey, any>`,
3245
3330
  `${current.slotProps?.length ? "$slots" : "_$slots"}?: Record<string, ServerSlotRender>`
3246
3331
  ];
3247
3332
  b.append(params.join(",\n") + ",");
3248
- } else if (chunk.script === ") /* @return_type */ {") b.append("): { body: string; head: string } {");
3333
+ } else if (chunk.script === ") /* @return_type */ {") b.append("): Promise<{ body: string; head: string }> {");
3249
3334
  else if (chunk.script === "/* @start */") {
3250
3335
  if (current.contextProps?.length) b.append(`$context = Object.assign({}, $context);`);
3336
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) {
3337
+ imports.add("t_server_flush");
3338
+ b.append(`return t_server_flush(async () => {`);
3339
+ }
3251
3340
  b.append(`let t_body = "";`);
3252
3341
  b.append(`let t_head = "";`);
3253
3342
  } else if (chunk.script === "/* @render */") {
@@ -3258,6 +3347,7 @@ function buildServerTemplate(template, imports, b, options) {
3258
3347
  styleHash: current.style?.hash || "",
3259
3348
  varNames: {},
3260
3349
  preserveWhitespace: false,
3350
+ awaitCount: 0,
3261
3351
  options
3262
3352
  };
3263
3353
  b.append("");
@@ -3281,6 +3371,7 @@ function buildServerTemplate(template, imports, b, options) {
3281
3371
  styleHash: current.style?.hash || "",
3282
3372
  varNames: {},
3283
3373
  preserveWhitespace: false,
3374
+ awaitCount: 0,
3284
3375
  options
3285
3376
  };
3286
3377
  b.append(`} catch (${current.errorVar}) {`);
@@ -3303,6 +3394,7 @@ function buildServerTemplate(template, imports, b, options) {
3303
3394
  varNames: {},
3304
3395
  preserveWhitespace: false,
3305
3396
  inHead: true,
3397
+ awaitCount: 0,
3306
3398
  options
3307
3399
  };
3308
3400
  b.append("");
@@ -3319,9 +3411,23 @@ function buildServerTemplate(template, imports, b, options) {
3319
3411
  }
3320
3412
  } else if (chunk.script === "/* @end */") {
3321
3413
  b.append(`return { body: t_body, head: t_head };`);
3414
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) b.append(`});`);
3322
3415
  currentIndex += 1;
3323
3416
  current = template.components[currentIndex];
3324
- } else b.append(chunk.script);
3417
+ } else if (asyncChunks.has(chunkIndex)) b.append(chunk.script.replace(/(?<!async\s)function\s+([A-Za-z_$][\w$]*)((?:<(?:[^<>]|<[^<>]*>)*>)?)\s*\($/, "async function $1$2("));
3418
+ else b.append(chunk.script);
3419
+ }
3420
+ /**
3421
+ * Whether a markup tree contains an `@await` boundary, so the component's
3422
+ * render must be wrapped in a flush.
3423
+ */
3424
+ function containsAwaitGroup(node) {
3425
+ if (node === void 0 || node === null) return false;
3426
+ if (isControlNode(node) && node.operation === "@await group") return true;
3427
+ if (node.type !== "text" && node.type !== "comment") {
3428
+ for (let child of node.children) if (containsAwaitGroup(child)) return true;
3429
+ }
3430
+ return false;
3325
3431
  }
3326
3432
  //#endregion
3327
3433
  //#region src/compile/utils/trimWhitespace.ts