@llui/dom 0.0.33 → 0.0.35

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 (39) hide show
  1. package/dist/hmr.js +5 -0
  2. package/dist/hmr.js.map +1 -1
  3. package/dist/mount.d.ts.map +1 -1
  4. package/dist/mount.js +5 -0
  5. package/dist/mount.js.map +1 -1
  6. package/dist/primitives/branch.d.ts.map +1 -1
  7. package/dist/primitives/branch.js +18 -3
  8. package/dist/primitives/branch.js.map +1 -1
  9. package/dist/primitives/child.d.ts.map +1 -1
  10. package/dist/primitives/child.js +15 -3
  11. package/dist/primitives/child.js.map +1 -1
  12. package/dist/primitives/each.d.ts.map +1 -1
  13. package/dist/primitives/each.js +36 -14
  14. package/dist/primitives/each.js.map +1 -1
  15. package/dist/primitives/foreign.d.ts.map +1 -1
  16. package/dist/primitives/foreign.js +15 -3
  17. package/dist/primitives/foreign.js.map +1 -1
  18. package/dist/primitives/sample.d.ts +32 -1
  19. package/dist/primitives/sample.d.ts.map +1 -1
  20. package/dist/primitives/sample.js +52 -2
  21. package/dist/primitives/sample.js.map +1 -1
  22. package/dist/primitives/show.d.ts.map +1 -1
  23. package/dist/primitives/show.js +13 -1
  24. package/dist/primitives/show.js.map +1 -1
  25. package/dist/primitives/virtual-each.d.ts.map +1 -1
  26. package/dist/primitives/virtual-each.js +25 -5
  27. package/dist/primitives/virtual-each.js.map +1 -1
  28. package/dist/render-context.d.ts +3 -0
  29. package/dist/render-context.d.ts.map +1 -1
  30. package/dist/render-context.js +27 -1
  31. package/dist/render-context.js.map +1 -1
  32. package/dist/types.d.ts +23 -0
  33. package/dist/types.d.ts.map +1 -1
  34. package/dist/types.js.map +1 -1
  35. package/dist/update-loop.d.ts +6 -1
  36. package/dist/update-loop.d.ts.map +1 -1
  37. package/dist/update-loop.js +214 -59
  38. package/dist/update-loop.js.map +1 -1
  39. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { getRenderContext } from '../render-context.js';
1
+ import { getRenderContext, currentAccessor } from '../render-context.js';
2
2
  /**
3
3
  * Read current state inside a render context and return the result of
4
4
  * `selector(state)`. No binding is created, no mask is assigned — this
@@ -13,6 +13,36 @@ import { getRenderContext } from '../render-context.js';
13
13
  * Use `each` + `ItemAccessor` instead — see the "List of editable
14
14
  * rows" recipe in the cookbook.
15
15
  *
16
+ * **Don't use inside an accessor** (`each().key`, `each().items`,
17
+ * `branch().on`, `show().when`, `scope().on`, `child().props`,
18
+ * `foreign().props`, or a binding accessor like `text(s => …)`).
19
+ * Accessors must be pure functions of their parameter — the compiler's
20
+ * mask analysis only sees reads of the parameter, so a `sample()` read
21
+ * is invisible. The result is a hidden dependency that breaks
22
+ * reconciliation: structural blocks gate out updates that should fire,
23
+ * binding accessors miss state changes, and `key` callbacks create
24
+ * keys that don't track outer state correctly.
25
+ *
26
+ * To depend on outer state inside an accessor, **lift it into the
27
+ * accessor's parameter**. For `each().key` reading a sibling field
28
+ * `rev`:
29
+ *
30
+ * ```ts
31
+ * // ❌ wrong — sample() in key is invisible to mask gating
32
+ * each({
33
+ * items: (s) => s.items,
34
+ * key: (it) => `${it.id}|${sample(s => s.rev)}`,
35
+ * render: …,
36
+ * })
37
+ *
38
+ * // ✅ right — bake outer state into items, key is pure of T
39
+ * each({
40
+ * items: (s) => s.items.map((it) => ({ it, rev: s.rev })),
41
+ * key: (r) => `${r.it.id}|${r.rev}`,
42
+ * render: …,
43
+ * })
44
+ * ```
45
+ *
16
46
  * **Use for** passing a state snapshot to an imperative renderer
17
47
  * (foreign libraries, third-party canvas/svg builders), reading a
18
48
  * value to compute a static piece of structure that doesn't need to
@@ -25,9 +55,29 @@ import { getRenderContext } from '../render-context.js';
25
55
  * context is live — including `each.render`, whose bag intentionally
26
56
  * does not carry View methods.
27
57
  *
28
- * Throws if called outside a render context.
58
+ * Throws if called outside a render context, or from inside an
59
+ * accessor.
29
60
  */
30
61
  export function sample(selector) {
62
+ const accessor = currentAccessor();
63
+ if (accessor !== null) {
64
+ throw new Error(`[LLui] sample() must not be called from inside ${accessor}. ` +
65
+ `Accessors must be pure functions of their parameter — sample() reads ` +
66
+ `state outside the parameter, which is invisible to the compiler's mask ` +
67
+ `analysis and breaks reconciliation.\n\n` +
68
+ `To depend on outer state, lift it into the accessor's parameter. For ` +
69
+ `each().key reading a sibling field, bake it into the items map:\n` +
70
+ ` // ❌ wrong\n` +
71
+ ` each({\n` +
72
+ ` items: (s) => s.rows,\n` +
73
+ ` key: (it) => \`\${it.id}|\${sample(s => s.rev)}\`,\n` +
74
+ ` })\n` +
75
+ ` // ✅ right\n` +
76
+ ` each({\n` +
77
+ ` items: (s) => s.rows.map((it) => ({ it, rev: s.rev })),\n` +
78
+ ` key: (r) => \`\${r.it.id}|\${r.rev}\`,\n` +
79
+ ` })`);
80
+ }
31
81
  const ctx = getRenderContext('sample');
32
82
  return selector(ctx.state);
33
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sample.js","sourceRoot":"","sources":["../../src/primitives/sample.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,MAAM,CAAO,QAAqB;IAChD,MAAM,GAAG,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACtC,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAU,CAAC,CAAA;AACjC,CAAC","sourcesContent":["import { getRenderContext } from '../render-context.js'\n\n/**\n * Read current state inside a render context and return the result of\n * `selector(state)`. No binding is created, no mask is assigned — this\n * is a one-shot imperative read at view-construction time.\n *\n * **Don't use for variable-length lists.** Wrapping a list-render in\n * `sample` looks idiomatic but silently breaks reactivity: the\n * `.map(...)` runs once at construction, captures the row objects in\n * closure, and never re-runs when state updates in place. The cells\n * inside the captured rows show stale data; only a full structural\n * rebuild (e.g. a parent `branch` swapping arms) will refresh them.\n * Use `each` + `ItemAccessor` instead — see the \"List of editable\n * rows\" recipe in the cookbook.\n *\n * **Use for** passing a state snapshot to an imperative renderer\n * (foreign libraries, third-party canvas/svg builders), reading a\n * value to compute a static piece of structure that doesn't need to\n * react, or any case where a reactive binding would be semantically\n * wrong (e.g. capturing a value at *this exact moment* for a\n * one-shot side effect).\n *\n * Also exposed as `h.sample` on the View bag for destructure-from-`h`\n * ergonomics. The top-level import form works everywhere a render\n * context is live — including `each.render`, whose bag intentionally\n * does not carry View methods.\n *\n * Throws if called outside a render context.\n */\nexport function sample<S, R>(selector: (s: S) => R): R {\n const ctx = getRenderContext('sample')\n return selector(ctx.state as S)\n}\n"]}
1
+ {"version":3,"file":"sample.js","sourceRoot":"","sources":["../../src/primitives/sample.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAM,UAAU,MAAM,CAAO,QAAqB;IAChD,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,kDAAkD,QAAQ,IAAI;YAC5D,uEAAuE;YACvE,yEAAyE;YACzE,yCAAyC;YACzC,uEAAuE;YACvE,mEAAmE;YACnE,gBAAgB;YAChB,YAAY;YACZ,6BAA6B;YAC7B,0DAA0D;YAC1D,QAAQ;YACR,gBAAgB;YAChB,YAAY;YACZ,+DAA+D;YAC/D,8CAA8C;YAC9C,MAAM,CACT,CAAA;IACH,CAAC;IACD,MAAM,GAAG,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACtC,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAU,CAAC,CAAA;AACjC,CAAC","sourcesContent":["import { getRenderContext, currentAccessor } from '../render-context.js'\n\n/**\n * Read current state inside a render context and return the result of\n * `selector(state)`. No binding is created, no mask is assigned — this\n * is a one-shot imperative read at view-construction time.\n *\n * **Don't use for variable-length lists.** Wrapping a list-render in\n * `sample` looks idiomatic but silently breaks reactivity: the\n * `.map(...)` runs once at construction, captures the row objects in\n * closure, and never re-runs when state updates in place. The cells\n * inside the captured rows show stale data; only a full structural\n * rebuild (e.g. a parent `branch` swapping arms) will refresh them.\n * Use `each` + `ItemAccessor` instead — see the \"List of editable\n * rows\" recipe in the cookbook.\n *\n * **Don't use inside an accessor** (`each().key`, `each().items`,\n * `branch().on`, `show().when`, `scope().on`, `child().props`,\n * `foreign().props`, or a binding accessor like `text(s => …)`).\n * Accessors must be pure functions of their parameter — the compiler's\n * mask analysis only sees reads of the parameter, so a `sample()` read\n * is invisible. The result is a hidden dependency that breaks\n * reconciliation: structural blocks gate out updates that should fire,\n * binding accessors miss state changes, and `key` callbacks create\n * keys that don't track outer state correctly.\n *\n * To depend on outer state inside an accessor, **lift it into the\n * accessor's parameter**. For `each().key` reading a sibling field\n * `rev`:\n *\n * ```ts\n * // ❌ wrong — sample() in key is invisible to mask gating\n * each({\n * items: (s) => s.items,\n * key: (it) => `${it.id}|${sample(s => s.rev)}`,\n * render: …,\n * })\n *\n * // ✅ right — bake outer state into items, key is pure of T\n * each({\n * items: (s) => s.items.map((it) => ({ it, rev: s.rev })),\n * key: (r) => `${r.it.id}|${r.rev}`,\n * render: …,\n * })\n * ```\n *\n * **Use for** passing a state snapshot to an imperative renderer\n * (foreign libraries, third-party canvas/svg builders), reading a\n * value to compute a static piece of structure that doesn't need to\n * react, or any case where a reactive binding would be semantically\n * wrong (e.g. capturing a value at *this exact moment* for a\n * one-shot side effect).\n *\n * Also exposed as `h.sample` on the View bag for destructure-from-`h`\n * ergonomics. The top-level import form works everywhere a render\n * context is live — including `each.render`, whose bag intentionally\n * does not carry View methods.\n *\n * Throws if called outside a render context, or from inside an\n * accessor.\n */\nexport function sample<S, R>(selector: (s: S) => R): R {\n const accessor = currentAccessor()\n if (accessor !== null) {\n throw new Error(\n `[LLui] sample() must not be called from inside ${accessor}. ` +\n `Accessors must be pure functions of their parameter — sample() reads ` +\n `state outside the parameter, which is invisible to the compiler's mask ` +\n `analysis and breaks reconciliation.\\n\\n` +\n `To depend on outer state, lift it into the accessor's parameter. For ` +\n `each().key reading a sibling field, bake it into the items map:\\n` +\n ` // ❌ wrong\\n` +\n ` each({\\n` +\n ` items: (s) => s.rows,\\n` +\n ` key: (it) => \\`\\${it.id}|\\${sample(s => s.rev)}\\`,\\n` +\n ` })\\n` +\n ` // ✅ right\\n` +\n ` each({\\n` +\n ` items: (s) => s.rows.map((it) => ({ it, rev: s.rev })),\\n` +\n ` key: (r) => \\`\\${r.it.id}|\\${r.rev}\\`,\\n` +\n ` })`,\n )\n }\n const ctx = getRenderContext('sample')\n return selector(ctx.state as S)\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAK9C,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAepE"}
1
+ {"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAM9C,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAyBpE"}
@@ -1,3 +1,4 @@
1
+ import { enterAccessor, exitAccessor } from '../render-context.js';
1
2
  import { branch } from './branch.js';
2
3
  const EMPTY = () => [];
3
4
  export function show(opts) {
@@ -8,7 +9,18 @@ export function show(opts) {
8
9
  // branch.on is string-only; stringify the boolean for the case lookup.
9
10
  // JS object literals stringify boolean keys, so `cases.{true, false}`
10
11
  // matches `String(true)` / `String(false)`.
11
- on: (s) => String(opts.when(s)),
12
+ //
13
+ // Label as `show().when` (not `branch().on`) so a sample() inside the
14
+ // user's `when` callback yields an error pointing at the right surface.
15
+ on: (s) => {
16
+ enterAccessor('show().when');
17
+ try {
18
+ return String(opts.when(s));
19
+ }
20
+ finally {
21
+ exitAccessor();
22
+ }
23
+ },
12
24
  cases: { true: opts.render, false: opts.fallback ?? EMPTY },
13
25
  enter: opts.enter,
14
26
  leave: opts.leave,
@@ -1 +1 @@
1
- {"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,EAAY,CAAA;AAEhC,MAAM,UAAU,IAAI,CAAiB,IAAuB;IAC1D,sEAAsE;IACtE,wEAAwE;IACxE,2BAA2B;IAC3B,OAAO,MAAM,CAAO;QAClB,uEAAuE;QACvE,sEAAsE;QACtE,4CAA4C;QAC5C,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;QAC3D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,eAAe,EAAE,WAAW;KAC7B,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type { ShowOptions } from '../types.js'\nimport { branch } from './branch.js'\n\nconst EMPTY = () => [] as Node[]\n\nexport function show<S, M = unknown>(opts: ShowOptions<S, M>): Node[] {\n // `__disposalCause` is read by branch.ts when it disposes the leaving\n // arm — it lets the disposer log distinguish show/hide transitions from\n // multi-case branch swaps.\n return branch<S, M>({\n // branch.on is string-only; stringify the boolean for the case lookup.\n // JS object literals stringify boolean keys, so `cases.{true, false}`\n // matches `String(true)` / `String(false)`.\n on: (s) => String(opts.when(s)),\n cases: { true: opts.render, false: opts.fallback ?? EMPTY },\n enter: opts.enter,\n leave: opts.leave,\n onTransition: opts.onTransition,\n __disposalCause: 'show-hide',\n })\n}\n"]}
1
+ {"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/primitives/show.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,EAAY,CAAA;AAEhC,MAAM,UAAU,IAAI,CAAiB,IAAuB;IAC1D,sEAAsE;IACtE,wEAAwE;IACxE,2BAA2B;IAC3B,OAAO,MAAM,CAAO;QAClB,uEAAuE;QACvE,sEAAsE;QACtE,4CAA4C;QAC5C,EAAE;QACF,sEAAsE;QACtE,wEAAwE;QACxE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE;YACR,aAAa,CAAC,aAAa,CAAC,CAAA;YAC5B,IAAI,CAAC;gBACH,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;YAC7B,CAAC;oBAAS,CAAC;gBACT,YAAY,EAAE,CAAA;YAChB,CAAC;QACH,CAAC;QACD,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;QAC3D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,eAAe,EAAE,WAAW;KAC7B,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import type { ShowOptions } from '../types.js'\nimport { enterAccessor, exitAccessor } from '../render-context.js'\nimport { branch } from './branch.js'\n\nconst EMPTY = () => [] as Node[]\n\nexport function show<S, M = unknown>(opts: ShowOptions<S, M>): Node[] {\n // `__disposalCause` is read by branch.ts when it disposes the leaving\n // arm — it lets the disposer log distinguish show/hide transitions from\n // multi-case branch swaps.\n return branch<S, M>({\n // branch.on is string-only; stringify the boolean for the case lookup.\n // JS object literals stringify boolean keys, so `cases.{true, false}`\n // matches `String(true)` / `String(false)`.\n //\n // Label as `show().when` (not `branch().on`) so a sample() inside the\n // user's `when` callback yields an error pointing at the right surface.\n on: (s) => {\n enterAccessor('show().when')\n try {\n return String(opts.when(s))\n } finally {\n exitAccessor()\n }\n },\n cases: { true: opts.render, false: opts.fallback ?? EMPTY },\n enter: opts.enter,\n leave: opts.leave,\n onTransition: opts.onTransition,\n __disposalCause: 'show-hide',\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-each.d.ts","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAY,IAAI,EAAE,MAAM,aAAa,CAAA;AAY/D,MAAM,WAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO;IACnD,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;IACpB,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAA;IACjC,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAA;IAClB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAA;IACvB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,CAAC,IAAI,EAAE;QACb,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAA;QACrB,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAA;QAC1C,KAAK,EAAE,MAAM,MAAM,CAAA;KACpB,KAAK,IAAI,EAAE,CAAA;CACb;AAkBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAmMxF"}
1
+ {"version":3,"file":"virtual-each.d.ts","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAY,IAAI,EAAE,MAAM,aAAa,CAAA;AAc/D,MAAM,WAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO;IACnD,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;IACpB,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAA;IACjC,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAA;IAClB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAA;IACvB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,CAAC,IAAI,EAAE;QACb,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAA;QACrB,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAA;QAC1C,KAAK,EAAE,MAAM,MAAM,CAAA;KACpB,KAAK,IAAI,EAAE,CAAA;CACb;AAkBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAsNxF"}
@@ -1,4 +1,4 @@
1
- import { getRenderContext, setRenderContext, clearRenderContext, } from '../render-context.js';
1
+ import { getRenderContext, setRenderContext, clearRenderContext, enterAccessor, exitAccessor, } from '../render-context.js';
2
2
  import { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js';
3
3
  import { getFlatBindings, setFlatBindings } from '../binding.js';
4
4
  import { FULL_MASK } from '../update-loop.js';
@@ -63,8 +63,28 @@ export function virtualEach(opts) {
63
63
  const end = Math.min(length, Math.ceil((scrollTop + opts.containerHeight) / opts.itemHeight) + overscan);
64
64
  return [start, end];
65
65
  };
66
+ // Wrap accessor invocations to make sample()/h.sample() inside throw a
67
+ // targeted error. Mirrors the wrappers in each.ts.
68
+ const callItems = (state) => {
69
+ enterAccessor('virtualEach().items');
70
+ try {
71
+ return opts.items(state);
72
+ }
73
+ finally {
74
+ exitAccessor();
75
+ }
76
+ };
77
+ const callKey = (item) => {
78
+ enterAccessor('virtualEach().key');
79
+ try {
80
+ return opts.key(item);
81
+ }
82
+ finally {
83
+ exitAccessor();
84
+ }
85
+ };
66
86
  const buildEntry = (item, index, state) => {
67
- const key = opts.key(item);
87
+ const key = callKey(item);
68
88
  const scope = createLifetime(parentLifetime);
69
89
  const wrapper = ctx.dom.createElement('div');
70
90
  wrapper.style.position = 'absolute';
@@ -127,7 +147,7 @@ export function virtualEach(opts) {
127
147
  return entry;
128
148
  };
129
149
  const reconcile = (state) => {
130
- const items = opts.items(state);
150
+ const items = callItems(state);
131
151
  lastItems = items;
132
152
  // Update spacer total height
133
153
  spacer.style.height = `${items.length * opts.itemHeight}px`;
@@ -147,7 +167,7 @@ export function virtualEach(opts) {
147
167
  const visibleKeys = new Map();
148
168
  for (let i = start; i < end; i++) {
149
169
  const item = items[i];
150
- visibleKeys.set(opts.key(item), { item, index: i });
170
+ visibleKeys.set(callKey(item), { item, index: i });
151
171
  }
152
172
  // Dispose entries no longer visible
153
173
  for (const [key, entry] of entries) {
@@ -188,7 +208,7 @@ export function virtualEach(opts) {
188
208
  const block = {
189
209
  mask: FULL_MASK,
190
210
  reconcile(state) {
191
- const newItems = opts.items(state);
211
+ const newItems = callItems(state);
192
212
  if (newItems === lastItems)
193
213
  return;
194
214
  reconcile(state);
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-each.js","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ItemAccessor, Lifetime, Send } from '../types.js'\nimport {\n getRenderContext,\n setRenderContext,\n clearRenderContext,\n type RenderContext,\n} from '../render-context.js'\nimport { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js'\nimport { getFlatBindings, setFlatBindings } from '../binding.js'\nimport { FULL_MASK } from '../update-loop.js'\nimport type { StructuralBlock } from '../structural.js'\n\nexport interface VirtualEachOptions<S, T, M = unknown> {\n items: (s: S) => T[]\n key: (item: T) => string | number\n /** Fixed pixel height per item. Required — dynamic heights are not supported yet. */\n itemHeight: number\n /** Scrollable container height in pixels. */\n containerHeight: number\n /** Extra rows to render above/below the viewport for smooth scrolling. Default: 3. */\n overscan?: number\n /** Optional class for the scroll container. */\n class?: string\n render: (opts: {\n send: Send<M>\n item: ItemAccessor<T>\n acc: <R>(selector: (t: T) => R) => () => R\n index: () => number\n }) => Node[]\n}\n\ninterface VirtualEntry<T> {\n key: string | number\n current: T\n index: number\n scope: Lifetime\n wrapper: HTMLElement\n}\n\nconst buildCtx: RenderContext = {\n rootLifetime: null as unknown as Lifetime,\n state: null,\n allBindings: [],\n structuralBlocks: [],\n dom: null as unknown as import('../dom-env.js').DomEnv,\n}\n\n/**\n * Virtualized list — renders only the rows visible in the scroll viewport.\n * Use for lists with 1k+ items where a regular `each()` would be too slow.\n *\n * Current limitations:\n * - Fixed row height (`itemHeight`) — dynamic heights not supported\n * - No transitions / animations\n * - No cross-container reuse (items outside view are fully disposed)\n *\n * ```ts\n * view: ({ text }) => [\n * ...virtualEach({\n * items: (s) => s.rows,\n * key: (r) => r.id,\n * itemHeight: 40,\n * containerHeight: 600,\n * render: ({ item }) => [\n * div({ class: 'row' }, [text(item.label)]),\n * ],\n * }),\n * ]\n * ```\n */\nexport function virtualEach<S, T, M = unknown>(opts: VirtualEachOptions<S, T, M>): Node[] {\n const ctx = getRenderContext('virtualEach')\n const parentLifetime = ctx.rootLifetime\n const blocks = ctx.structuralBlocks\n const send = ctx.send as (msg: M) => void\n\n const overscan = opts.overscan ?? 3\n\n // Scroll container\n const scroll = ctx.dom.createElement('div') as HTMLDivElement\n scroll.style.overflow = 'auto'\n scroll.style.position = 'relative'\n scroll.style.height = `${opts.containerHeight}px`\n scroll.dataset.virtualContainer = ''\n if (opts.class) scroll.className = opts.class\n\n // Inner spacer sized to full content height\n const spacer = ctx.dom.createElement('div') as HTMLDivElement\n spacer.style.position = 'relative'\n spacer.style.width = '100%'\n spacer.dataset.virtualSpacer = ''\n scroll.appendChild(spacer)\n\n // Map of key → entry\n const entries = new Map<string | number, VirtualEntry<T>>()\n let lastItems: T[] = []\n let scrollTop = 0\n\n const computeRange = (length: number): [number, number] => {\n if (length === 0) return [0, 0]\n const start = Math.max(0, Math.floor(scrollTop / opts.itemHeight) - overscan)\n const end = Math.min(\n length,\n Math.ceil((scrollTop + opts.containerHeight) / opts.itemHeight) + overscan,\n )\n return [start, end]\n }\n\n const buildEntry = (item: T, index: number, state: S): VirtualEntry<T> => {\n const key = opts.key(item)\n const scope = createLifetime(parentLifetime)\n\n const wrapper = ctx.dom.createElement('div') as HTMLDivElement\n wrapper.style.position = 'absolute'\n wrapper.style.top = `${index * opts.itemHeight}px`\n wrapper.style.left = '0'\n wrapper.style.right = '0'\n wrapper.style.height = `${opts.itemHeight}px`\n wrapper.dataset.virtualItem = ''\n wrapper.dataset.virtualKey = String(key)\n\n const entry: VirtualEntry<T> = { key, current: item, index, scope, wrapper }\n\n // Item accessor: item(selector) and item.field\n const itemFn = <R>(selector: (t: T) => R): (() => R) => {\n const accessor = (): R => selector(entry.current)\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n return accessor\n }\n\n let itemProxy: ItemAccessor<T> | null = null\n const getItemProxy = (): ItemAccessor<T> => {\n if (itemProxy) return itemProxy\n const fieldCache = new Map<string, () => unknown>()\n itemProxy = new Proxy(itemFn as object, {\n get(target, prop) {\n if (typeof prop === 'symbol' || prop === 'then' || prop === 'prototype') {\n return Reflect.get(target, prop)\n }\n const k = prop as string\n const cached = fieldCache.get(k)\n if (cached) return cached\n const accessor = (): unknown => (entry.current as Record<string, unknown>)[k]\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n fieldCache.set(k, accessor)\n return accessor\n },\n }) as ItemAccessor<T>\n return itemProxy\n }\n\n const indexAccessor = (): number => entry.index\n\n buildCtx.rootLifetime = scope\n buildCtx.state = state\n buildCtx.allBindings = ctx.allBindings\n buildCtx.structuralBlocks = ctx.structuralBlocks\n buildCtx.dom = ctx.dom\n buildCtx.instance = ctx.instance\n const prevFlat = getFlatBindings()\n setFlatBindings(ctx.allBindings)\n setRenderContext(buildCtx)\n\n const nodes = opts.render({\n send,\n item: getItemProxy(),\n acc: itemFn,\n index: indexAccessor,\n })\n\n clearRenderContext()\n setFlatBindings(prevFlat)\n setRenderContext(ctx)\n\n for (const node of nodes) wrapper.appendChild(node)\n return entry\n }\n\n const reconcile = (state: S): void => {\n const items = opts.items(state)\n lastItems = items\n\n // Update spacer total height\n spacer.style.height = `${items.length * opts.itemHeight}px`\n\n if (items.length === 0) {\n // Dispose all entries\n for (const entry of entries.values()) {\n entry.scope.disposalCause = 'each-remove'\n disposeLifetime(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n }\n entries.clear()\n return\n }\n\n const [start, end] = computeRange(items.length)\n\n // Build a map of key → {item, index} for the visible range\n const visibleKeys = new Map<string | number, { item: T; index: number }>()\n for (let i = start; i < end; i++) {\n const item = items[i]!\n visibleKeys.set(opts.key(item), { item, index: i })\n }\n\n // Dispose entries no longer visible\n for (const [key, entry] of entries) {\n if (!visibleKeys.has(key)) {\n entry.scope.disposalCause = 'each-remove'\n disposeLifetime(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n entries.delete(key)\n }\n }\n\n // Create new entries + update existing\n for (const [key, { item, index }] of visibleKeys) {\n const existing = entries.get(key)\n if (existing) {\n existing.current = item\n if (existing.index !== index) {\n existing.index = index\n existing.wrapper.style.top = `${index * opts.itemHeight}px`\n }\n } else {\n const entry = buildEntry(item, index, state)\n entries.set(key, entry)\n spacer.appendChild(entry.wrapper)\n }\n }\n }\n\n // Scroll handler — reconcile visible range without touching component state\n const onScroll = (): void => {\n scrollTop = scroll.scrollTop\n reconcile(ctx.state as S)\n }\n scroll.addEventListener('scroll', onScroll, { passive: true })\n\n // Register as a structural block BEFORE initial render so this block\n // precedes any nested blocks its rows register. See branch.ts for the\n // full rationale (Phase 1 iteration safety).\n const block: StructuralBlock = {\n mask: FULL_MASK,\n reconcile(state: unknown) {\n const newItems = opts.items(state as S)\n if (newItems === lastItems) return\n reconcile(state as S)\n },\n }\n blocks.push(block)\n\n // Initial render\n reconcile(ctx.state as S)\n\n // Cleanup on parent disposal\n addDisposer(parentLifetime, () => {\n scroll.removeEventListener('scroll', onScroll)\n for (const entry of entries.values()) {\n disposeLifetime(entry.scope)\n }\n entries.clear()\n })\n\n return [scroll]\n}\n"]}
1
+ {"version":3,"file":"virtual-each.js","sourceRoot":"","sources":["../../src/primitives/virtual-each.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ItemAccessor, Lifetime, Send } from '../types.js'\nimport {\n getRenderContext,\n setRenderContext,\n clearRenderContext,\n enterAccessor,\n exitAccessor,\n type RenderContext,\n} from '../render-context.js'\nimport { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js'\nimport { getFlatBindings, setFlatBindings } from '../binding.js'\nimport { FULL_MASK } from '../update-loop.js'\nimport type { StructuralBlock } from '../structural.js'\n\nexport interface VirtualEachOptions<S, T, M = unknown> {\n items: (s: S) => T[]\n key: (item: T) => string | number\n /** Fixed pixel height per item. Required — dynamic heights are not supported yet. */\n itemHeight: number\n /** Scrollable container height in pixels. */\n containerHeight: number\n /** Extra rows to render above/below the viewport for smooth scrolling. Default: 3. */\n overscan?: number\n /** Optional class for the scroll container. */\n class?: string\n render: (opts: {\n send: Send<M>\n item: ItemAccessor<T>\n acc: <R>(selector: (t: T) => R) => () => R\n index: () => number\n }) => Node[]\n}\n\ninterface VirtualEntry<T> {\n key: string | number\n current: T\n index: number\n scope: Lifetime\n wrapper: HTMLElement\n}\n\nconst buildCtx: RenderContext = {\n rootLifetime: null as unknown as Lifetime,\n state: null,\n allBindings: [],\n structuralBlocks: [],\n dom: null as unknown as import('../dom-env.js').DomEnv,\n}\n\n/**\n * Virtualized list — renders only the rows visible in the scroll viewport.\n * Use for lists with 1k+ items where a regular `each()` would be too slow.\n *\n * Current limitations:\n * - Fixed row height (`itemHeight`) — dynamic heights not supported\n * - No transitions / animations\n * - No cross-container reuse (items outside view are fully disposed)\n *\n * ```ts\n * view: ({ text }) => [\n * ...virtualEach({\n * items: (s) => s.rows,\n * key: (r) => r.id,\n * itemHeight: 40,\n * containerHeight: 600,\n * render: ({ item }) => [\n * div({ class: 'row' }, [text(item.label)]),\n * ],\n * }),\n * ]\n * ```\n */\nexport function virtualEach<S, T, M = unknown>(opts: VirtualEachOptions<S, T, M>): Node[] {\n const ctx = getRenderContext('virtualEach')\n const parentLifetime = ctx.rootLifetime\n const blocks = ctx.structuralBlocks\n const send = ctx.send as (msg: M) => void\n\n const overscan = opts.overscan ?? 3\n\n // Scroll container\n const scroll = ctx.dom.createElement('div') as HTMLDivElement\n scroll.style.overflow = 'auto'\n scroll.style.position = 'relative'\n scroll.style.height = `${opts.containerHeight}px`\n scroll.dataset.virtualContainer = ''\n if (opts.class) scroll.className = opts.class\n\n // Inner spacer sized to full content height\n const spacer = ctx.dom.createElement('div') as HTMLDivElement\n spacer.style.position = 'relative'\n spacer.style.width = '100%'\n spacer.dataset.virtualSpacer = ''\n scroll.appendChild(spacer)\n\n // Map of key → entry\n const entries = new Map<string | number, VirtualEntry<T>>()\n let lastItems: T[] = []\n let scrollTop = 0\n\n const computeRange = (length: number): [number, number] => {\n if (length === 0) return [0, 0]\n const start = Math.max(0, Math.floor(scrollTop / opts.itemHeight) - overscan)\n const end = Math.min(\n length,\n Math.ceil((scrollTop + opts.containerHeight) / opts.itemHeight) + overscan,\n )\n return [start, end]\n }\n\n // Wrap accessor invocations to make sample()/h.sample() inside throw a\n // targeted error. Mirrors the wrappers in each.ts.\n const callItems = (state: S): T[] => {\n enterAccessor('virtualEach().items')\n try {\n return opts.items(state)\n } finally {\n exitAccessor()\n }\n }\n const callKey = (item: T): string | number => {\n enterAccessor('virtualEach().key')\n try {\n return opts.key(item)\n } finally {\n exitAccessor()\n }\n }\n\n const buildEntry = (item: T, index: number, state: S): VirtualEntry<T> => {\n const key = callKey(item)\n const scope = createLifetime(parentLifetime)\n\n const wrapper = ctx.dom.createElement('div') as HTMLDivElement\n wrapper.style.position = 'absolute'\n wrapper.style.top = `${index * opts.itemHeight}px`\n wrapper.style.left = '0'\n wrapper.style.right = '0'\n wrapper.style.height = `${opts.itemHeight}px`\n wrapper.dataset.virtualItem = ''\n wrapper.dataset.virtualKey = String(key)\n\n const entry: VirtualEntry<T> = { key, current: item, index, scope, wrapper }\n\n // Item accessor: item(selector) and item.field\n const itemFn = <R>(selector: (t: T) => R): (() => R) => {\n const accessor = (): R => selector(entry.current)\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n return accessor\n }\n\n let itemProxy: ItemAccessor<T> | null = null\n const getItemProxy = (): ItemAccessor<T> => {\n if (itemProxy) return itemProxy\n const fieldCache = new Map<string, () => unknown>()\n itemProxy = new Proxy(itemFn as object, {\n get(target, prop) {\n if (typeof prop === 'symbol' || prop === 'then' || prop === 'prototype') {\n return Reflect.get(target, prop)\n }\n const k = prop as string\n const cached = fieldCache.get(k)\n if (cached) return cached\n const accessor = (): unknown => (entry.current as Record<string, unknown>)[k]\n ;(accessor as unknown as { __perItem: true }).__perItem = true\n fieldCache.set(k, accessor)\n return accessor\n },\n }) as ItemAccessor<T>\n return itemProxy\n }\n\n const indexAccessor = (): number => entry.index\n\n buildCtx.rootLifetime = scope\n buildCtx.state = state\n buildCtx.allBindings = ctx.allBindings\n buildCtx.structuralBlocks = ctx.structuralBlocks\n buildCtx.dom = ctx.dom\n buildCtx.instance = ctx.instance\n const prevFlat = getFlatBindings()\n setFlatBindings(ctx.allBindings)\n setRenderContext(buildCtx)\n\n const nodes = opts.render({\n send,\n item: getItemProxy(),\n acc: itemFn,\n index: indexAccessor,\n })\n\n clearRenderContext()\n setFlatBindings(prevFlat)\n setRenderContext(ctx)\n\n for (const node of nodes) wrapper.appendChild(node)\n return entry\n }\n\n const reconcile = (state: S): void => {\n const items = callItems(state)\n lastItems = items\n\n // Update spacer total height\n spacer.style.height = `${items.length * opts.itemHeight}px`\n\n if (items.length === 0) {\n // Dispose all entries\n for (const entry of entries.values()) {\n entry.scope.disposalCause = 'each-remove'\n disposeLifetime(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n }\n entries.clear()\n return\n }\n\n const [start, end] = computeRange(items.length)\n\n // Build a map of key → {item, index} for the visible range\n const visibleKeys = new Map<string | number, { item: T; index: number }>()\n for (let i = start; i < end; i++) {\n const item = items[i]!\n visibleKeys.set(callKey(item), { item, index: i })\n }\n\n // Dispose entries no longer visible\n for (const [key, entry] of entries) {\n if (!visibleKeys.has(key)) {\n entry.scope.disposalCause = 'each-remove'\n disposeLifetime(entry.scope)\n if (entry.wrapper.parentNode) entry.wrapper.parentNode.removeChild(entry.wrapper)\n entries.delete(key)\n }\n }\n\n // Create new entries + update existing\n for (const [key, { item, index }] of visibleKeys) {\n const existing = entries.get(key)\n if (existing) {\n existing.current = item\n if (existing.index !== index) {\n existing.index = index\n existing.wrapper.style.top = `${index * opts.itemHeight}px`\n }\n } else {\n const entry = buildEntry(item, index, state)\n entries.set(key, entry)\n spacer.appendChild(entry.wrapper)\n }\n }\n }\n\n // Scroll handler — reconcile visible range without touching component state\n const onScroll = (): void => {\n scrollTop = scroll.scrollTop\n reconcile(ctx.state as S)\n }\n scroll.addEventListener('scroll', onScroll, { passive: true })\n\n // Register as a structural block BEFORE initial render so this block\n // precedes any nested blocks its rows register. See branch.ts for the\n // full rationale (Phase 1 iteration safety).\n const block: StructuralBlock = {\n mask: FULL_MASK,\n reconcile(state: unknown) {\n const newItems = callItems(state as S)\n if (newItems === lastItems) return\n reconcile(state as S)\n },\n }\n blocks.push(block)\n\n // Initial render\n reconcile(ctx.state as S)\n\n // Cleanup on parent disposal\n addDisposer(parentLifetime, () => {\n scroll.removeEventListener('scroll', onScroll)\n for (const entry of entries.values()) {\n disposeLifetime(entry.scope)\n }\n entries.clear()\n })\n\n return [scroll]\n}\n"]}
@@ -19,5 +19,8 @@ export interface RenderContext {
19
19
  }
20
20
  export declare function setRenderContext(ctx: RenderContext): void;
21
21
  export declare function clearRenderContext(): void;
22
+ export declare function enterAccessor(label: string): void;
23
+ export declare function exitAccessor(): void;
24
+ export declare function currentAccessor(): string | null;
22
25
  export declare function getRenderContext(primitiveName?: string): RenderContext;
23
26
  //# sourceMappingURL=render-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-context.d.ts","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,QAAQ,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,OAAO,EAAE,CAAA;IACtB,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7B;;;;;;OAMG;IACH,GAAG,EAAE,MAAM,CAAA;CAMZ;AAID,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAEzD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,wBAAgB,gBAAgB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,aAAa,CAmCtE"}
1
+ {"version":3,"file":"render-context.d.ts","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,QAAQ,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,OAAO,EAAE,CAAA;IACtB,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7B;;;;;;OAMG;IACH,GAAG,EAAE,MAAM,CAAA;CAMZ;AAID,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAEzD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAaD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAG/C;AAED,wBAAgB,gBAAgB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,aAAa,CAyCtE"}
@@ -5,6 +5,26 @@ export function setRenderContext(ctx) {
5
5
  export function clearRenderContext() {
6
6
  currentContext = null;
7
7
  }
8
+ // Accessor stack — tracks which structural-primitive or binding accessor is
9
+ // currently executing. `sample()` reads the top of this stack to detect calls
10
+ // from inside an accessor (forbidden — accessors must be pure functions of
11
+ // their parameter, since their reads drive mask gating).
12
+ //
13
+ // Implemented as an array (rather than a counter) so the targeted error can
14
+ // name the innermost accessor: "inside each().key" rather than just "inside
15
+ // an accessor". Nested primitives push/pop in LIFO order; the top is the
16
+ // site that called sample().
17
+ const accessorStack = [];
18
+ export function enterAccessor(label) {
19
+ accessorStack.push(label);
20
+ }
21
+ export function exitAccessor() {
22
+ accessorStack.pop();
23
+ }
24
+ export function currentAccessor() {
25
+ const len = accessorStack.length;
26
+ return len > 0 ? accessorStack[len - 1] : null;
27
+ }
8
28
  export function getRenderContext(primitiveName) {
9
29
  if (!currentContext) {
10
30
  const name = primitiveName ? `${primitiveName}()` : 'primitives';
@@ -33,7 +53,13 @@ export function getRenderContext(primitiveName) {
33
53
  `from inside the component's view callback so their result can be spread ` +
34
54
  `into the returned node tree.\n` +
35
55
  ` 3. Calling a primitive from a setTimeout / Promise / event handler — ` +
36
- `the render context only persists during the synchronous view() call.` +
56
+ `the render context only persists during the synchronous view() call.\n` +
57
+ ` 4. Calling a primitive from a structural accessor (each().key, ` +
58
+ `each().items, branch().on, show().when, child().props, …) or a ` +
59
+ `binding accessor (text(s => …), el({attr: s => …})) during reconcile — ` +
60
+ `accessors run during the update phase with no render context. They must ` +
61
+ `be pure functions of their parameter; reads outside the parameter break ` +
62
+ `mask gating.` +
37
63
  sampleGuidance);
38
64
  }
39
65
  return currentContext;
@@ -1 +1 @@
1
- {"version":3,"file":"render-context.js","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AA2BA,IAAI,cAAc,GAAyB,IAAI,CAAA;AAE/C,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,cAAc,GAAG,GAAG,CAAA;AACtB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,cAAc,GAAG,IAAI,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,aAAsB;IACrD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;QAChE,kEAAkE;QAClE,mEAAmE;QACnE,kEAAkE;QAClE,mEAAmE;QACnE,kEAAkE;QAClE,2DAA2D;QAC3D,MAAM,cAAc,GAClB,aAAa,KAAK,QAAQ;YACxB,CAAC,CAAC,iEAAiE;gBACjE,iEAAiE;gBACjE,qEAAqE;gBACrE,YAAY;gBACZ,wCAAwC;gBACxC,0CAA0C;gBAC1C,uDAAuD;gBACvD,oDAAoD;gBACpD,MAAM;YACR,CAAC,CAAC,EAAE,CAAA;QACR,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,4DAA4D;YACxE,0DAA0D;YAC1D,sEAAsE;YACtE,yEAAyE;YACzE,0EAA0E;YAC1E,0EAA0E;YAC1E,gCAAgC;YAChC,yEAAyE;YACzE,sEAAsE;YACtE,cAAc,CACjB,CAAA;IACH,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import type { Lifetime, Binding } from './types.js'\nimport type { StructuralBlock } from './structural.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport type { DomEnv } from './dom-env.js'\n\nexport interface RenderContext {\n rootLifetime: Lifetime\n state: unknown\n allBindings: Binding[]\n structuralBlocks: StructuralBlock[]\n container?: Element\n send?: (msg: unknown) => void\n /**\n * The DOM implementation backing this render pass. Seeded by\n * `mountApp` / `hydrateApp` / `renderToString` and threaded through\n * every nested primitive via context spread. Primitives construct\n * DOM through `ctx.dom.createElement(...)` etc. instead of reaching\n * for `globalThis.document`.\n */\n dom: DomEnv\n /** @internal dev-only — the owning ComponentInstance. Set by mount /\n * hydrate / child to let primitives (currently `each`) emit tracker\n * data to `inst._eachDiffLog`. Nested contexts pass through via\n * spread (e.g. `{ ...ctx, rootLifetime }`). Undefined outside dev. */\n instance?: ComponentInstance\n}\n\nlet currentContext: RenderContext | null = null\n\nexport function setRenderContext(ctx: RenderContext): void {\n currentContext = ctx\n}\n\nexport function clearRenderContext(): void {\n currentContext = null\n}\n\nexport function getRenderContext(primitiveName?: string): RenderContext {\n if (!currentContext) {\n const name = primitiveName ? `${primitiveName}()` : 'primitives'\n // `sample()` is specifically the one users reach for from adapter\n // send wrappers / event handlers / async callbacks expecting it to\n // be \"imperative and safe.\" It isn't — it's a view-primitive that\n // reads the render-time state snapshot, and the context is cleared\n // as soon as view() returns. Point at the sanctioned escape hatch\n // in the thrown message so the caller doesn't have to dig.\n const sampleGuidance =\n primitiveName === 'sample'\n ? '\\n\\nFor the \"read state inside a callback / handler\" case: use ' +\n 'AppHandle.getState() instead. It is safe to call from anywhere ' +\n '(event handlers, adapter send wrappers, async callbacks, timers).\\n' +\n 'Example:\\n' +\n ' const handle = mountApp(root, App)\\n' +\n \" el.addEventListener('click', () => {\\n\" +\n ' const { count } = handle.getState() as AppState\\n' +\n \" if (count > 0) handle.send({ type: 'tick' })\\n\" +\n ' })'\n : ''\n throw new Error(\n `[LLui] ${name} can only be called inside a component's view() function. ` +\n `It was called outside a render context. Common causes:\\n` +\n ` 1. Calling a primitive at module scope instead of inside view().\\n` +\n ` 2. Calling an overlay helper (dialog.overlay, popover.overlay, …) at ` +\n `module scope — these internally use show()/branch() and must be invoked ` +\n `from inside the component's view callback so their result can be spread ` +\n `into the returned node tree.\\n` +\n ` 3. Calling a primitive from a setTimeout / Promise / event handler — ` +\n `the render context only persists during the synchronous view() call.` +\n sampleGuidance,\n )\n }\n return currentContext\n}\n"]}
1
+ {"version":3,"file":"render-context.js","sourceRoot":"","sources":["../src/render-context.ts"],"names":[],"mappings":"AA2BA,IAAI,cAAc,GAAyB,IAAI,CAAA;AAE/C,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,cAAc,GAAG,GAAG,CAAA;AACtB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,cAAc,GAAG,IAAI,CAAA;AACvB,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,2EAA2E;AAC3E,yDAAyD;AACzD,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,yEAAyE;AACzE,6BAA6B;AAC7B,MAAM,aAAa,GAAa,EAAE,CAAA;AAElC,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,aAAa,CAAC,GAAG,EAAE,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAA;IAChC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,aAAsB;IACrD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;QAChE,kEAAkE;QAClE,mEAAmE;QACnE,kEAAkE;QAClE,mEAAmE;QACnE,kEAAkE;QAClE,2DAA2D;QAC3D,MAAM,cAAc,GAClB,aAAa,KAAK,QAAQ;YACxB,CAAC,CAAC,iEAAiE;gBACjE,iEAAiE;gBACjE,qEAAqE;gBACrE,YAAY;gBACZ,wCAAwC;gBACxC,0CAA0C;gBAC1C,uDAAuD;gBACvD,oDAAoD;gBACpD,MAAM;YACR,CAAC,CAAC,EAAE,CAAA;QACR,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,4DAA4D;YACxE,0DAA0D;YAC1D,sEAAsE;YACtE,yEAAyE;YACzE,0EAA0E;YAC1E,0EAA0E;YAC1E,gCAAgC;YAChC,yEAAyE;YACzE,wEAAwE;YACxE,mEAAmE;YACnE,iEAAiE;YACjE,yEAAyE;YACzE,0EAA0E;YAC1E,0EAA0E;YAC1E,cAAc;YACd,cAAc,CACjB,CAAA;IACH,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import type { Lifetime, Binding } from './types.js'\nimport type { StructuralBlock } from './structural.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport type { DomEnv } from './dom-env.js'\n\nexport interface RenderContext {\n rootLifetime: Lifetime\n state: unknown\n allBindings: Binding[]\n structuralBlocks: StructuralBlock[]\n container?: Element\n send?: (msg: unknown) => void\n /**\n * The DOM implementation backing this render pass. Seeded by\n * `mountApp` / `hydrateApp` / `renderToString` and threaded through\n * every nested primitive via context spread. Primitives construct\n * DOM through `ctx.dom.createElement(...)` etc. instead of reaching\n * for `globalThis.document`.\n */\n dom: DomEnv\n /** @internal dev-only — the owning ComponentInstance. Set by mount /\n * hydrate / child to let primitives (currently `each`) emit tracker\n * data to `inst._eachDiffLog`. Nested contexts pass through via\n * spread (e.g. `{ ...ctx, rootLifetime }`). Undefined outside dev. */\n instance?: ComponentInstance\n}\n\nlet currentContext: RenderContext | null = null\n\nexport function setRenderContext(ctx: RenderContext): void {\n currentContext = ctx\n}\n\nexport function clearRenderContext(): void {\n currentContext = null\n}\n\n// Accessor stack — tracks which structural-primitive or binding accessor is\n// currently executing. `sample()` reads the top of this stack to detect calls\n// from inside an accessor (forbidden — accessors must be pure functions of\n// their parameter, since their reads drive mask gating).\n//\n// Implemented as an array (rather than a counter) so the targeted error can\n// name the innermost accessor: \"inside each().key\" rather than just \"inside\n// an accessor\". Nested primitives push/pop in LIFO order; the top is the\n// site that called sample().\nconst accessorStack: string[] = []\n\nexport function enterAccessor(label: string): void {\n accessorStack.push(label)\n}\n\nexport function exitAccessor(): void {\n accessorStack.pop()\n}\n\nexport function currentAccessor(): string | null {\n const len = accessorStack.length\n return len > 0 ? accessorStack[len - 1]! : null\n}\n\nexport function getRenderContext(primitiveName?: string): RenderContext {\n if (!currentContext) {\n const name = primitiveName ? `${primitiveName}()` : 'primitives'\n // `sample()` is specifically the one users reach for from adapter\n // send wrappers / event handlers / async callbacks expecting it to\n // be \"imperative and safe.\" It isn't — it's a view-primitive that\n // reads the render-time state snapshot, and the context is cleared\n // as soon as view() returns. Point at the sanctioned escape hatch\n // in the thrown message so the caller doesn't have to dig.\n const sampleGuidance =\n primitiveName === 'sample'\n ? '\\n\\nFor the \"read state inside a callback / handler\" case: use ' +\n 'AppHandle.getState() instead. It is safe to call from anywhere ' +\n '(event handlers, adapter send wrappers, async callbacks, timers).\\n' +\n 'Example:\\n' +\n ' const handle = mountApp(root, App)\\n' +\n \" el.addEventListener('click', () => {\\n\" +\n ' const { count } = handle.getState() as AppState\\n' +\n \" if (count > 0) handle.send({ type: 'tick' })\\n\" +\n ' })'\n : ''\n throw new Error(\n `[LLui] ${name} can only be called inside a component's view() function. ` +\n `It was called outside a render context. Common causes:\\n` +\n ` 1. Calling a primitive at module scope instead of inside view().\\n` +\n ` 2. Calling an overlay helper (dialog.overlay, popover.overlay, …) at ` +\n `module scope — these internally use show()/branch() and must be invoked ` +\n `from inside the component's view callback so their result can be spread ` +\n `into the returned node tree.\\n` +\n ` 3. Calling a primitive from a setTimeout / Promise / event handler — ` +\n `the render context only persists during the synchronous view() call.\\n` +\n ` 4. Calling a primitive from a structural accessor (each().key, ` +\n `each().items, branch().on, show().when, child().props, …) or a ` +\n `binding accessor (text(s => …), el({attr: s => …})) during reconcile — ` +\n `accessors run during the update phase with no render context. They must ` +\n `be pure functions of their parameter; reads outside the parameter break ` +\n `mask gating.` +\n sampleGuidance,\n )\n }\n return currentContext\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -238,6 +238,29 @@ export interface AppHandle {
238
238
  state: unknown;
239
239
  effects: unknown[];
240
240
  } | null;
241
+ /**
242
+ * Install a hook called whenever a binding's accessor throws during
243
+ * the update cycle. The runtime catches the throw, leaves the
244
+ * binding's `lastValue` unchanged (DOM stays at its previous value
245
+ * rather than blanking), continues with sibling bindings, and
246
+ * notifies this hook with `{kind, key?, message, stack?}`.
247
+ *
248
+ * Used by `@llui/agent` to fold binding-evaluation throws into the
249
+ * dispatch envelope's `drain.errors` — so an LLM dispatching a Msg
250
+ * that breaks scoring doesn't get HTTP 500 + a frozen UI; instead
251
+ * the dispatch reports as `{status: 'dispatched', drain: {errors:
252
+ * [...]}}` with the rest of the page rendering normally.
253
+ *
254
+ * Pass `null` to remove the hook. Without a hook, throws fall back
255
+ * to `console.error` (dev mode) or `console.warn` (prod) so they
256
+ * aren't silently swallowed.
257
+ */
258
+ setOnBindingError(hook: ((info: {
259
+ kind: string;
260
+ key?: string;
261
+ message: string;
262
+ stack?: string;
263
+ }) => void) | null): void;
241
264
  }
242
265
  export interface Lifetime {
243
266
  id: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qEAAqE","sourcesContent":["// ── Component Definition ──────────────────────────────────────────\n\nimport type { View } from './view-helpers.js'\nimport type { StructuralBlock } from './structural.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport type { DisposerEvent } from './tracking/disposer-log.js'\n\nexport interface ComponentDef<S, M, E = never, D = void> {\n name: string\n init: (data: D) => [S, E[]]\n update: (state: S, msg: M) => [S, E[]]\n view: (h: View<S, M>) => Node[]\n onEffect?: (ctx: { effect: E; send: Send<M>; signal: AbortSignal }) => void\n\n // Level 2 composition\n propsMsg?: (props: Record<string, unknown>) => M\n receives?: Record<string, (params: unknown) => M>\n\n /** @internal Compiler-injected */\n __dirty?: (oldState: S, newState: S) => number | [number, number]\n /** @internal Compiler-injected */\n __renderToString?: (state: S) => string\n /** @internal Compiler-injected */\n __msgSchema?: object\n /** @internal Compiler-injected; keyed by Msg discriminant → annotations. See agent spec §5.1. */\n __msgAnnotations?: Record<\n string,\n {\n intent: string | null\n alwaysAffordable: boolean\n requiresConfirm: boolean\n humanOnly: boolean\n }\n >\n /** @internal Compiler-emitted; one entry per send() call site in view(). See agent spec §5.2. */\n __bindingDescriptors?: Array<{ variant: string }>\n /** @internal Compiler-injected; 32-char hex SHA-256 of schemas + annotations. See agent spec §12.3. */\n __schemaHash?: string\n /** @internal Compiler-injected — maps top-level state field → dirty-mask bit(s) */\n __maskLegend?: Record<string, number>\n /** @internal Compiler-injected — source-file location of the component() call */\n __componentMeta?: { file: string; line: number }\n /** @internal Compiler-injected — shape of the State type (for introspection) */\n __stateSchema?: object\n /** @internal Compiler-injected — Effect union schema (for introspection) */\n __effectSchema?: object\n /** @internal Compiler-injected — replaces generic Phase 1 + Phase 2 loop */\n __update?: (\n state: S,\n dirty: number,\n bindings: Binding[],\n blocks: StructuralBlock[],\n bindingsBeforePhase1: number,\n ) => void\n /** @internal Compiler-injected — per-message-type specialized handlers.\n * Bypass the entire processMessages pipeline for single-message updates. */\n __handlers?: Record<string, (inst: object, msg: unknown) => [S, E[]]>\n}\n\nexport type Send<M> = (msg: M) => void\n\n/**\n * Type-erased component definition for use at module boundaries where\n * the consumer's `S`, `M`, `E` are internal and invisible to the caller.\n *\n * Why this exists: `ComponentDef<S, M, E, D>` uses property syntax for\n * its callable fields (`init: (data: D) => ...`), and TypeScript\n * checks property syntax with strict (contravariant) variance. That's\n * the right call for user-facing type safety when authoring a\n * component (a narrower `Msg` type can't accidentally satisfy the\n * `update`'s param), but it means `ComponentDef<MyState, MyMsg, MyEffect, MyData>`\n * is NOT structurally assignable to `ComponentDef<unknown, unknown, unknown, unknown>`\n * — the `init: (data: MyData) => ...` field is contravariant in `MyData`,\n * so widening to `unknown` is rejected.\n *\n * `AnyComponentDef` (and `LazyDef<D>` below) declare the same fields\n * using **method syntax** (`init(data: D): ...`), which TypeScript\n * checks bivariantly. Concrete `ComponentDef<S, M, E, D>` assigns into\n * these structurally without any `widenDef` helper at the callsite.\n *\n * Used by every API that accepts an opaque component definition at a\n * module boundary:\n *\n * - `child({ def })`\n * - `lazy({ loader })` — returns `LazyDef<D>` so the loader's `D` survives\n * - `createOnRenderClient({ Layout })` (from `@llui/vike`)\n * - `createOnRenderHtml({ Layout })` (from `@llui/vike`)\n *\n * The `D` parameter is `unknown` here — `child()` doesn't pass init\n * data through, and `Layout` callers pass route-supplied data whose\n * shape is unknown at the type-erased boundary. Use `LazyDef<D>` when\n * you need to preserve the init data shape (the lazy loader case).\n */\nexport interface AnyComponentDef {\n name: string\n init(data: unknown): [unknown, unknown[]]\n update(state: unknown, msg: unknown): [unknown, unknown[]]\n view(h: unknown): Node[]\n onEffect?: unknown\n propsMsg?: unknown\n receives?: unknown\n __dirty?: unknown\n __renderToString?: unknown\n __msgSchema?: unknown\n __msgAnnotations?: unknown\n __bindingDescriptors?: unknown\n __schemaHash?: unknown\n __maskLegend?: unknown\n __componentMeta?: unknown\n __stateSchema?: unknown\n __effectSchema?: unknown\n __update?: unknown\n __handlers?: unknown\n}\n\n/**\n * Type-erased component definition for use at module boundaries where the\n * loaded component's S, M, E are internal and invisible to the caller.\n * Only `D` (init data) survives because the caller provides it.\n *\n * `ComponentDef<S, M, E, D>` is structurally assignable to `LazyDef<D>`\n * for any S, M, E — `view: (h: unknown) => Node[]` accepts any View via\n * contravariance, and all other fields widen to `unknown` return types.\n *\n * Used by `lazy()` as the loader's return type. Use `AnyComponentDef`\n * (above) when D is also opaque — most other adapter-layer APIs.\n */\nexport interface LazyDef<D = void> {\n name: string\n // Method syntax — TypeScript checks methods bivariantly, so\n // ComponentDef<S, M, E, D>'s concrete (state: S, msg: M) => ...\n // assigns here even though S/M ≠ unknown. Property syntax would\n // be contravariant and reject the assignment.\n init(data: D): [unknown, unknown[]]\n update(state: unknown, msg: unknown): [unknown, unknown[]]\n view(h: unknown): Node[]\n onEffect?: unknown\n propsMsg?: unknown\n receives?: unknown\n __dirty?: unknown\n __renderToString?: unknown\n __msgSchema?: unknown\n __msgAnnotations?: unknown\n __bindingDescriptors?: unknown\n __schemaHash?: unknown\n __maskLegend?: unknown\n __componentMeta?: unknown\n __stateSchema?: unknown\n __effectSchema?: unknown\n __update?: unknown\n __handlers?: unknown\n}\n\n/**\n * Maps a value shape to a reactive-props shape: every field becomes an accessor\n * `(s: S) => V`. Use for Level-1 view function signatures.\n *\n * ```ts\n * type ToolbarData = { tools: Tool[]; theme: 'light' | 'dark' }\n *\n * export function toolbar<S>(props: Props<ToolbarData, S>, send: Send<Msg>) {\n * return [div({ class: props.theme }, [each({ items: props.tools, ... })])]\n * }\n *\n * // Caller — TypeScript enforces per-field accessors; passing a raw value errors:\n * toolbar({ tools: (s: State) => s.tools, theme: (s) => s.theme }, send)\n * ```\n */\nexport type Props<T, S> = {\n [K in keyof T]: (s: S) => T[K]\n}\n\n// ── App Handle ────────────────────────────────────────────────────\n\nexport interface AppHandle {\n dispose(): void\n flush(): void\n /**\n * Dispatch a message into the mounted instance from outside its\n * normal view-bound `send` channel. Useful for adapter layers that\n * need to push updates into a long-lived instance — e.g.\n * `@llui/vike`'s persistent-layout chain pushes layout-data updates\n * into surviving layer instances on client navigation when their\n * `propsMsg` translates the new data into a state-update message.\n *\n * Messages are queued through the same path as `view`-side `send`\n * calls — they batch into the next microtask and process via the\n * normal update loop. Calling `send` after `dispose` is a no-op.\n */\n send(msg: unknown): void\n /**\n * Read the current state snapshot. Safe to call from anywhere —\n * event handlers, async callbacks, adapter `send` wrappers, or any\n * imperative context where the render context is not live.\n *\n * Unlike `sample()` (a view primitive that requires an active\n * render context and throws outside of `view()`), `getState` is\n * the sanctioned escape hatch for \"I need to know the current\n * state to decide what to dispatch.\" Typical shape:\n *\n * ```ts\n * const handle = mountApp(root, MyApp)\n * container.addEventListener('drop', () => {\n * const { mode } = handle.getState() as AppState\n * if (mode === 'drag') handle.send({ type: 'commit' })\n * })\n * ```\n *\n * Throws after `dispose()` — stale reads are silent bugs; a thrown\n * error pinpoints the callsite that forgot to detach.\n *\n * The return type is `unknown` because `AppHandle` is state-type\n * erased at this boundary; cast to your app's state type at the\n * call site.\n */\n getState(): unknown\n /**\n * Register a listener called synchronously after every update cycle\n * completes. The listener receives the new state. Returns an\n * unsubscribe function. Safe after dispose (no-op; returns a no-op\n * unsubscribe). See agent spec §10.5 (state-update frames).\n */\n subscribe(listener: (state: unknown) => void): () => void\n /**\n * Snapshot the Msg variants currently dispatchable from rendered UI.\n * Each entry corresponds to one or more event-handler bindings the\n * compiler tagged with literal `send({type: '<variant>'})` calls;\n * the registry tracks live mounts via lifetime refcounts so the\n * snapshot reflects what the user can click *right now*, not the\n * static catalogue of every variant the app could ever accept.\n *\n * Used by `@llui/agent` to populate `list_actions`. Apps that don't\n * use the LLui compiler tagger pass (or whose views contain no\n * literal sends) get an empty array — agents fall back to the Msg\n * schema for affordance discovery.\n */\n getBindingDescriptors(): Array<{ variant: string }>\n /**\n * Hot-swap the component's `update` function (and optionally\n * `onEffect`) without rebuilding the DOM. Pending messages are\n * drained against the OLD `update` first so a half-applied\n * transition can't leave the state ill-formed; subsequent\n * dispatches go through the new function.\n *\n * This is the lightweight HMR escape hatch for cases where the\n * full `replaceComponent` flow (in `@llui/dom/hmr`) is overkill —\n * pure `update.ts` edits don't change the view, so disposing the\n * root lifetime and re-running the view just to install a new\n * function is wasteful and loses focus / scroll / transient DOM\n * state. Wire it via `import.meta.hot`:\n *\n * ```ts\n * // main.ts\n * const handle = mountApp(root, App)\n * if (import.meta.hot) {\n * import.meta.hot.accept('./app/update', (mod) => {\n * if (mod) handle.swapUpdate(mod.update)\n * })\n * }\n * ```\n *\n * Caveats:\n * - Doesn't compensate for State shape changes — if the new\n * `update` reads a field the old state doesn't have, you'll\n * see runtime errors. Hard-reload after structural state\n * changes.\n * - Doesn't swap `view` or compiler-emitted `__update`/`__dirty`\n * — for those, prefer the auto-injected component-level HMR\n * accept callback (which routes through `replaceComponent`).\n */\n swapUpdate(\n newUpdate: (state: unknown, msg: unknown) => [unknown, unknown[]],\n newOnEffect?: unknown,\n ): void\n /**\n * Run the reducer in isolation against the current state. Returns\n * `[newState, effects]` — the reducer's literal output without any\n * commit, flush, or effect-handler invocation. Used by the agent's\n * `would_dispatch` tool to predict what a candidate dispatch would\n * do before committing to it.\n *\n * Pure-reducer assumption: TEA mandates `update` be pure. LLui's\n * runtime treats it that way too (no speculative re-runs for any\n * other reason). Apps whose reducers branch on `Date.now()` or\n * read `localStorage` will see prediction drift from real dispatch\n * by exactly that amount of impurity — surface as documented at\n * the agent's call site, not silently corrected here.\n *\n * **No effects fire.** Effect descriptors are returned for the\n * agent to inspect; `onEffect` is not called. This is the entire\n * point of the API.\n */\n runReducer(msg: unknown): { state: unknown; effects: unknown[] } | null\n}\n\n// ── Lifetime ─────────────────────────────────────────────────────────\n\nexport interface Lifetime {\n id: number\n parent: Lifetime | null\n children: Lifetime[]\n disposers: Array<() => void>\n bindings: Binding[]\n /** Per-item updaters — called directly by each() when item changes, bypassing Phase 2 */\n itemUpdaters: Array<() => void>\n /**\n * @internal dev-only back-reference to the owning ComponentInstance.\n * Populated on the root scope by `installDevTools` so `disposeLifetime`\n * can walk up the scope chain and emit DisposerEvents into the\n * instance's `_disposerLog`. Undefined in production.\n */\n instance?: ComponentInstance\n /**\n * @internal dev-only cause hint. Structural primitives (branch, each,\n * child, mountApp teardown) set this field immediately before calling\n * `disposeLifetime`; the dispose path reads it once to stamp the emitted\n * DisposerEvent. Left undefined, `disposeLifetime` falls back to\n * `'component-unmount'`. Undefined in production.\n */\n disposalCause?: DisposerEvent['cause']\n /** @internal dev-only — populated by structural primitives for scope-tree classification */\n _kind?: 'root' | 'show' | 'each' | 'branch' | 'scope' | 'child' | 'portal' | 'foreign'\n}\n\nexport interface LifetimeNode {\n scopeId: string\n kind: 'root' | 'show' | 'each' | 'branch' | 'scope' | 'child' | 'portal' | 'foreign'\n active: boolean\n children: LifetimeNode[]\n}\n\n// ── Binding ───────────────────────────────────────────────────────\n\n/**\n * Binding output kinds.\n *\n * `'text' | 'prop' | 'attr' | 'class' | 'style'` write their accessor's\n * return value to the DOM. `'effect'` is a side-effect-only watcher:\n * the accessor is invoked every Phase 2 tick its mask is hit, but its\n * return value is discarded and `applyBinding` is a no-op. Used by\n * `child()` to fire the prop-diff/propsMsg cascade on parent updates\n * without the cost of stringifying the returned props bag onto a\n * detached anchor node every render.\n */\nexport type BindingKind = 'text' | 'prop' | 'attr' | 'class' | 'style' | 'effect'\n\nexport interface Binding {\n mask: number\n accessor: (state: unknown) => unknown\n lastValue: unknown\n kind: BindingKind\n node: Node\n key?: string\n ownerLifetime: Lifetime\n perItem: boolean\n dead: boolean\n}\n\n// ── Structural Primitives ─────────────────────────────────────────\n\nexport interface TransitionOptions {\n enter?: (nodes: Node[]) => void | Promise<void>\n leave?: (nodes: Node[]) => void | Promise<void>\n onTransition?: (ctx: { entering: Node[]; leaving: Node[]; parent: Node }) => void | Promise<void>\n}\n\ninterface BranchOptionsBase extends TransitionOptions {\n /**\n * @internal Set by `show()` / `scope()` sugar when delegating to\n * `branch()`, so the dev-only disposer log can report `'show-hide'` /\n * `'scope-rebuild'` instead of the default `'branch-swap'` for the\n * leaving arm. User code should not set this directly.\n */\n __disposalCause?: DisposerEvent['cause']\n /** @internal Compiler-injected mask of paths read by `on`. */\n __mask?: number\n}\n\n/**\n * Discriminant accessor — reads a string-keyed value either from the\n * current state (`(s) => …`) or from a closed-over item accessor /\n * memo (`() => item.kind()`). Both forms re-evaluate on every commit.\n * The zero-arg form is the canonical shape for branching on per-row\n * fields inside an `each.render` callback.\n */\ntype Discriminant<S, K extends string> = ((s: S) => K) | (() => K)\n\n/**\n * All cases covered by `cases` — no default allowed (would be dead code).\n */\ntype BranchOptionsExhaustive<S, M, K extends string> = BranchOptionsBase & {\n on: Discriminant<S, K>\n cases: { [P in K]: (h: View<S, M>) => Node[] }\n default?: never\n}\n\n/**\n * `cases` may cover some but not all keys; `default` handles the rest.\n */\ntype BranchOptionsNonExhaustive<S, M, K extends string> = BranchOptionsBase & {\n on: Discriminant<S, K>\n cases?: { [P in K]?: (h: View<S, M>) => Node[] }\n default: (h: View<S, M>) => Node[]\n}\n\n/**\n * `on` returns a wide `string` — exhaustiveness cannot be verified at\n * compile time (the key domain is infinite). Lenient: `default` is\n * optional so existing call sites that predate exhaustiveness typing\n * continue to compile. Authors who want the gate opt in by narrowing\n * `on`'s return type to a literal union.\n */\ntype BranchOptionsWide<S, M> = BranchOptionsBase & {\n on: Discriminant<S, string>\n cases?: Record<string, (h: View<S, M>) => Node[]>\n default?: (h: View<S, M>) => Node[]\n}\n\n/**\n * Options for `branch()`.\n *\n * When `on` returns a literal string union (e.g. `'idle' | 'loading'\n * | 'done'`), TypeScript enforces exhaustiveness: either every union\n * member has a case (and `default` is disallowed as unreachable), or\n * `default` is required to cover the remainder. When `on` returns a\n * wide `string`, `default` stays optional — exhaustiveness isn't\n * meaningful for an unbounded domain.\n *\n * `cases` is optional when `default` is present; `branch({ on, default })`\n * is the canonical dynamic-rebuild shape — `scope()` sugar wraps\n * exactly this form.\n */\nexport type BranchOptions<S, M = unknown, K extends string = string> = string extends K\n ? BranchOptionsWide<S, M>\n : BranchOptionsExhaustive<S, M, K> | BranchOptionsNonExhaustive<S, M, K>\n\nexport interface ShowOptions<S, M = unknown> extends TransitionOptions {\n /**\n * Predicate evaluated on every commit. `(s) => …` reads from\n * component state; `() => …` (zero-arg) reads from a closed-over\n * source — typically `item.field()` inside an `each.render`\n * callback. Both forms are reactive: the predicate runs against\n * each new state and the rendered subtree mounts/unmounts on the\n * boolean transition.\n */\n when: ((s: S) => boolean) | (() => boolean)\n render: (h: View<S, M>) => Node[]\n fallback?: (h: View<S, M>) => Node[]\n}\n\n/**\n * Options for `scope()` — rebuilds a subtree when `on(state)` changes.\n *\n * Sugar over `branch({ on, cases: {}, default: render, __disposalCause: 'scope-rebuild' })`.\n * Use when the key is dynamic (e.g. an epoch counter bumped from the\n * outside) and you want a fresh arm — fresh lifetime, fresh bindings —\n * each time it changes. Combine with `sample()` inside `render` for a\n * one-shot current-state read.\n */\nexport interface ScopeOptions<S, M = unknown> extends TransitionOptions {\n /**\n * Key accessor evaluated on every commit. `(s) => …` reads from\n * component state; `() => …` (zero-arg) reads from a closed-over\n * source. The render callback rebuilds whenever the returned key\n * changes — fresh lifetime, fresh bindings.\n */\n on: ((s: S) => string) | (() => string)\n render: (h: View<S, M>) => Node[]\n /** @internal Compiler-injected mask of paths read by `on`. */\n __mask?: number\n}\n\n/**\n * Options for `each()`. The inherited `enter` / `leave` callbacks fire **per item**:\n * `enter(nodes)` runs after an item's DOM is inserted (including initial mount);\n * `leave(nodes)` runs before an item's DOM is removed and may return a Promise\n * to hold the DOM until the animation resolves. Setting `leave` disables the\n * bulk-clear / full-replace fast paths.\n */\n/**\n * Per-item accessor. Two access forms:\n * - `item.field` — shorthand, returns accessor for `item.current[field]`\n * - `item(t => t.expr)` — computed expressions\n *\n * In both cases the returned value is a `() => V` accessor.\n * Invoke it (`item.field()`) to read the current value imperatively.\n */\nexport type ItemAccessor<T> = {\n <R>(selector: (t: T) => R): () => R\n} & {\n [K in keyof T]-?: () => T[K]\n} & {\n /**\n * Read the whole current item. Needed when T is a primitive (where the\n * field-map branch collapses to method names like `toString`) or when\n * you want to sample the entire record rather than a single field.\n *\n * Shadows any literal `current` field on T — if T has such a field,\n * use `item(r => r.current)` to disambiguate.\n */\n current(): T\n}\n\nexport interface EachOptions<S, T, M = unknown> extends TransitionOptions {\n items: (s: S) => T[]\n key: (item: T) => string | number\n render: (opts: {\n send: Send<M>\n item: ItemAccessor<T>\n /**\n * Plain (non-Proxy) accessor factory. Compiler-output path; avoid in user code\n * (use `item.field` / `item(fn)` — more ergonomic and bypasses Proxy when compiled).\n */\n acc: <R>(selector: (t: T) => R) => () => R\n index: () => number\n /**\n * The component's View bag (`h.text`, `h.show`, `h.branch`,\n * `h.scope`, `h.sample`, …). Each-render callers used to reach\n * for the top-level imports; the bag form is symmetric with how\n * `branch.cases[k]`, `show.render`, and `scope.render` receive it.\n * Both still work — destructure whichever is more convenient.\n */\n h: View<S, M>\n /** @internal Compiler-injected — entry reference for row factory */\n entry?: Record<string, unknown>\n }) => Node[]\n}\n\nexport interface PortalOptions {\n target: HTMLElement | string\n render: () => Node[]\n}\n\nexport interface ForeignOptions<S, M, T extends Record<string, unknown>, Instance> {\n /**\n * Construct the imperative instance. Can be async — return a\n * `Promise<Instance>` to defer construction until e.g. a dynamic\n * `import()` resolves. While the promise is pending:\n *\n * - The container element is in the DOM immediately (so layout\n * doesn't shift when the instance arrives).\n * - `sync` is NOT called. State changes are tracked by the\n * primitive and the latest props are applied as the initial\n * sync right after the promise resolves.\n * - If the owning scope disposes before the promise resolves,\n * `destroy(instance)` runs on resolution — no matter how long\n * the promise takes.\n */\n mount: (ctx: { container: HTMLElement; send: Send<M> }) => Instance | Promise<Instance>\n props: (s: S) => T\n sync:\n | ((ctx: { instance: Instance; props: T; prev: T | undefined }) => void)\n | {\n [K in keyof T]?: (ctx: { instance: Instance; value: T[K]; prev: T[K] | undefined }) => void\n }\n destroy: (instance: Instance) => void\n container?: { tag?: string; attrs?: Record<string, string> }\n}\n\nexport interface ChildOptions<S, ChildM> {\n // Type-erased via AnyComponentDef so callers can pass any concrete\n // ComponentDef<S, M, E, D> without a widening helper. The runtime\n // narrows the message shape via the user-supplied `onMsg` callback,\n // which keeps `ChildM` typed at this boundary even though the def\n // itself is opaque.\n def: AnyComponentDef\n key: string | number\n props: (s: S) => Record<string, unknown>\n onMsg?: (msg: ChildM) => unknown | null\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qEAAqE","sourcesContent":["// ── Component Definition ──────────────────────────────────────────\n\nimport type { View } from './view-helpers.js'\nimport type { StructuralBlock } from './structural.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport type { DisposerEvent } from './tracking/disposer-log.js'\n\nexport interface ComponentDef<S, M, E = never, D = void> {\n name: string\n init: (data: D) => [S, E[]]\n update: (state: S, msg: M) => [S, E[]]\n view: (h: View<S, M>) => Node[]\n onEffect?: (ctx: { effect: E; send: Send<M>; signal: AbortSignal }) => void\n\n // Level 2 composition\n propsMsg?: (props: Record<string, unknown>) => M\n receives?: Record<string, (params: unknown) => M>\n\n /** @internal Compiler-injected */\n __dirty?: (oldState: S, newState: S) => number | [number, number]\n /** @internal Compiler-injected */\n __renderToString?: (state: S) => string\n /** @internal Compiler-injected */\n __msgSchema?: object\n /** @internal Compiler-injected; keyed by Msg discriminant → annotations. See agent spec §5.1. */\n __msgAnnotations?: Record<\n string,\n {\n intent: string | null\n alwaysAffordable: boolean\n requiresConfirm: boolean\n humanOnly: boolean\n }\n >\n /** @internal Compiler-emitted; one entry per send() call site in view(). See agent spec §5.2. */\n __bindingDescriptors?: Array<{ variant: string }>\n /** @internal Compiler-injected; 32-char hex SHA-256 of schemas + annotations. See agent spec §12.3. */\n __schemaHash?: string\n /** @internal Compiler-injected — maps top-level state field → dirty-mask bit(s) */\n __maskLegend?: Record<string, number>\n /** @internal Compiler-injected — source-file location of the component() call */\n __componentMeta?: { file: string; line: number }\n /** @internal Compiler-injected — shape of the State type (for introspection) */\n __stateSchema?: object\n /** @internal Compiler-injected — Effect union schema (for introspection) */\n __effectSchema?: object\n /** @internal Compiler-injected — replaces generic Phase 1 + Phase 2 loop */\n __update?: (\n state: S,\n dirty: number,\n bindings: Binding[],\n blocks: StructuralBlock[],\n bindingsBeforePhase1: number,\n ) => void\n /** @internal Compiler-injected — per-message-type specialized handlers.\n * Bypass the entire processMessages pipeline for single-message updates. */\n __handlers?: Record<string, (inst: object, msg: unknown) => [S, E[]]>\n}\n\nexport type Send<M> = (msg: M) => void\n\n/**\n * Type-erased component definition for use at module boundaries where\n * the consumer's `S`, `M`, `E` are internal and invisible to the caller.\n *\n * Why this exists: `ComponentDef<S, M, E, D>` uses property syntax for\n * its callable fields (`init: (data: D) => ...`), and TypeScript\n * checks property syntax with strict (contravariant) variance. That's\n * the right call for user-facing type safety when authoring a\n * component (a narrower `Msg` type can't accidentally satisfy the\n * `update`'s param), but it means `ComponentDef<MyState, MyMsg, MyEffect, MyData>`\n * is NOT structurally assignable to `ComponentDef<unknown, unknown, unknown, unknown>`\n * — the `init: (data: MyData) => ...` field is contravariant in `MyData`,\n * so widening to `unknown` is rejected.\n *\n * `AnyComponentDef` (and `LazyDef<D>` below) declare the same fields\n * using **method syntax** (`init(data: D): ...`), which TypeScript\n * checks bivariantly. Concrete `ComponentDef<S, M, E, D>` assigns into\n * these structurally without any `widenDef` helper at the callsite.\n *\n * Used by every API that accepts an opaque component definition at a\n * module boundary:\n *\n * - `child({ def })`\n * - `lazy({ loader })` — returns `LazyDef<D>` so the loader's `D` survives\n * - `createOnRenderClient({ Layout })` (from `@llui/vike`)\n * - `createOnRenderHtml({ Layout })` (from `@llui/vike`)\n *\n * The `D` parameter is `unknown` here — `child()` doesn't pass init\n * data through, and `Layout` callers pass route-supplied data whose\n * shape is unknown at the type-erased boundary. Use `LazyDef<D>` when\n * you need to preserve the init data shape (the lazy loader case).\n */\nexport interface AnyComponentDef {\n name: string\n init(data: unknown): [unknown, unknown[]]\n update(state: unknown, msg: unknown): [unknown, unknown[]]\n view(h: unknown): Node[]\n onEffect?: unknown\n propsMsg?: unknown\n receives?: unknown\n __dirty?: unknown\n __renderToString?: unknown\n __msgSchema?: unknown\n __msgAnnotations?: unknown\n __bindingDescriptors?: unknown\n __schemaHash?: unknown\n __maskLegend?: unknown\n __componentMeta?: unknown\n __stateSchema?: unknown\n __effectSchema?: unknown\n __update?: unknown\n __handlers?: unknown\n}\n\n/**\n * Type-erased component definition for use at module boundaries where the\n * loaded component's S, M, E are internal and invisible to the caller.\n * Only `D` (init data) survives because the caller provides it.\n *\n * `ComponentDef<S, M, E, D>` is structurally assignable to `LazyDef<D>`\n * for any S, M, E — `view: (h: unknown) => Node[]` accepts any View via\n * contravariance, and all other fields widen to `unknown` return types.\n *\n * Used by `lazy()` as the loader's return type. Use `AnyComponentDef`\n * (above) when D is also opaque — most other adapter-layer APIs.\n */\nexport interface LazyDef<D = void> {\n name: string\n // Method syntax — TypeScript checks methods bivariantly, so\n // ComponentDef<S, M, E, D>'s concrete (state: S, msg: M) => ...\n // assigns here even though S/M ≠ unknown. Property syntax would\n // be contravariant and reject the assignment.\n init(data: D): [unknown, unknown[]]\n update(state: unknown, msg: unknown): [unknown, unknown[]]\n view(h: unknown): Node[]\n onEffect?: unknown\n propsMsg?: unknown\n receives?: unknown\n __dirty?: unknown\n __renderToString?: unknown\n __msgSchema?: unknown\n __msgAnnotations?: unknown\n __bindingDescriptors?: unknown\n __schemaHash?: unknown\n __maskLegend?: unknown\n __componentMeta?: unknown\n __stateSchema?: unknown\n __effectSchema?: unknown\n __update?: unknown\n __handlers?: unknown\n}\n\n/**\n * Maps a value shape to a reactive-props shape: every field becomes an accessor\n * `(s: S) => V`. Use for Level-1 view function signatures.\n *\n * ```ts\n * type ToolbarData = { tools: Tool[]; theme: 'light' | 'dark' }\n *\n * export function toolbar<S>(props: Props<ToolbarData, S>, send: Send<Msg>) {\n * return [div({ class: props.theme }, [each({ items: props.tools, ... })])]\n * }\n *\n * // Caller — TypeScript enforces per-field accessors; passing a raw value errors:\n * toolbar({ tools: (s: State) => s.tools, theme: (s) => s.theme }, send)\n * ```\n */\nexport type Props<T, S> = {\n [K in keyof T]: (s: S) => T[K]\n}\n\n// ── App Handle ────────────────────────────────────────────────────\n\nexport interface AppHandle {\n dispose(): void\n flush(): void\n /**\n * Dispatch a message into the mounted instance from outside its\n * normal view-bound `send` channel. Useful for adapter layers that\n * need to push updates into a long-lived instance — e.g.\n * `@llui/vike`'s persistent-layout chain pushes layout-data updates\n * into surviving layer instances on client navigation when their\n * `propsMsg` translates the new data into a state-update message.\n *\n * Messages are queued through the same path as `view`-side `send`\n * calls — they batch into the next microtask and process via the\n * normal update loop. Calling `send` after `dispose` is a no-op.\n */\n send(msg: unknown): void\n /**\n * Read the current state snapshot. Safe to call from anywhere —\n * event handlers, async callbacks, adapter `send` wrappers, or any\n * imperative context where the render context is not live.\n *\n * Unlike `sample()` (a view primitive that requires an active\n * render context and throws outside of `view()`), `getState` is\n * the sanctioned escape hatch for \"I need to know the current\n * state to decide what to dispatch.\" Typical shape:\n *\n * ```ts\n * const handle = mountApp(root, MyApp)\n * container.addEventListener('drop', () => {\n * const { mode } = handle.getState() as AppState\n * if (mode === 'drag') handle.send({ type: 'commit' })\n * })\n * ```\n *\n * Throws after `dispose()` — stale reads are silent bugs; a thrown\n * error pinpoints the callsite that forgot to detach.\n *\n * The return type is `unknown` because `AppHandle` is state-type\n * erased at this boundary; cast to your app's state type at the\n * call site.\n */\n getState(): unknown\n /**\n * Register a listener called synchronously after every update cycle\n * completes. The listener receives the new state. Returns an\n * unsubscribe function. Safe after dispose (no-op; returns a no-op\n * unsubscribe). See agent spec §10.5 (state-update frames).\n */\n subscribe(listener: (state: unknown) => void): () => void\n /**\n * Snapshot the Msg variants currently dispatchable from rendered UI.\n * Each entry corresponds to one or more event-handler bindings the\n * compiler tagged with literal `send({type: '<variant>'})` calls;\n * the registry tracks live mounts via lifetime refcounts so the\n * snapshot reflects what the user can click *right now*, not the\n * static catalogue of every variant the app could ever accept.\n *\n * Used by `@llui/agent` to populate `list_actions`. Apps that don't\n * use the LLui compiler tagger pass (or whose views contain no\n * literal sends) get an empty array — agents fall back to the Msg\n * schema for affordance discovery.\n */\n getBindingDescriptors(): Array<{ variant: string }>\n /**\n * Hot-swap the component's `update` function (and optionally\n * `onEffect`) without rebuilding the DOM. Pending messages are\n * drained against the OLD `update` first so a half-applied\n * transition can't leave the state ill-formed; subsequent\n * dispatches go through the new function.\n *\n * This is the lightweight HMR escape hatch for cases where the\n * full `replaceComponent` flow (in `@llui/dom/hmr`) is overkill —\n * pure `update.ts` edits don't change the view, so disposing the\n * root lifetime and re-running the view just to install a new\n * function is wasteful and loses focus / scroll / transient DOM\n * state. Wire it via `import.meta.hot`:\n *\n * ```ts\n * // main.ts\n * const handle = mountApp(root, App)\n * if (import.meta.hot) {\n * import.meta.hot.accept('./app/update', (mod) => {\n * if (mod) handle.swapUpdate(mod.update)\n * })\n * }\n * ```\n *\n * Caveats:\n * - Doesn't compensate for State shape changes — if the new\n * `update` reads a field the old state doesn't have, you'll\n * see runtime errors. Hard-reload after structural state\n * changes.\n * - Doesn't swap `view` or compiler-emitted `__update`/`__dirty`\n * — for those, prefer the auto-injected component-level HMR\n * accept callback (which routes through `replaceComponent`).\n */\n swapUpdate(\n newUpdate: (state: unknown, msg: unknown) => [unknown, unknown[]],\n newOnEffect?: unknown,\n ): void\n /**\n * Run the reducer in isolation against the current state. Returns\n * `[newState, effects]` — the reducer's literal output without any\n * commit, flush, or effect-handler invocation. Used by the agent's\n * `would_dispatch` tool to predict what a candidate dispatch would\n * do before committing to it.\n *\n * Pure-reducer assumption: TEA mandates `update` be pure. LLui's\n * runtime treats it that way too (no speculative re-runs for any\n * other reason). Apps whose reducers branch on `Date.now()` or\n * read `localStorage` will see prediction drift from real dispatch\n * by exactly that amount of impurity — surface as documented at\n * the agent's call site, not silently corrected here.\n *\n * **No effects fire.** Effect descriptors are returned for the\n * agent to inspect; `onEffect` is not called. This is the entire\n * point of the API.\n */\n runReducer(msg: unknown): { state: unknown; effects: unknown[] } | null\n /**\n * Install a hook called whenever a binding's accessor throws during\n * the update cycle. The runtime catches the throw, leaves the\n * binding's `lastValue` unchanged (DOM stays at its previous value\n * rather than blanking), continues with sibling bindings, and\n * notifies this hook with `{kind, key?, message, stack?}`.\n *\n * Used by `@llui/agent` to fold binding-evaluation throws into the\n * dispatch envelope's `drain.errors` — so an LLM dispatching a Msg\n * that breaks scoring doesn't get HTTP 500 + a frozen UI; instead\n * the dispatch reports as `{status: 'dispatched', drain: {errors:\n * [...]}}` with the rest of the page rendering normally.\n *\n * Pass `null` to remove the hook. Without a hook, throws fall back\n * to `console.error` (dev mode) or `console.warn` (prod) so they\n * aren't silently swallowed.\n */\n setOnBindingError(\n hook: ((info: { kind: string; key?: string; message: string; stack?: string }) => void) | null,\n ): void\n}\n\n// ── Lifetime ─────────────────────────────────────────────────────────\n\nexport interface Lifetime {\n id: number\n parent: Lifetime | null\n children: Lifetime[]\n disposers: Array<() => void>\n bindings: Binding[]\n /** Per-item updaters — called directly by each() when item changes, bypassing Phase 2 */\n itemUpdaters: Array<() => void>\n /**\n * @internal dev-only back-reference to the owning ComponentInstance.\n * Populated on the root scope by `installDevTools` so `disposeLifetime`\n * can walk up the scope chain and emit DisposerEvents into the\n * instance's `_disposerLog`. Undefined in production.\n */\n instance?: ComponentInstance\n /**\n * @internal dev-only cause hint. Structural primitives (branch, each,\n * child, mountApp teardown) set this field immediately before calling\n * `disposeLifetime`; the dispose path reads it once to stamp the emitted\n * DisposerEvent. Left undefined, `disposeLifetime` falls back to\n * `'component-unmount'`. Undefined in production.\n */\n disposalCause?: DisposerEvent['cause']\n /** @internal dev-only — populated by structural primitives for scope-tree classification */\n _kind?: 'root' | 'show' | 'each' | 'branch' | 'scope' | 'child' | 'portal' | 'foreign'\n}\n\nexport interface LifetimeNode {\n scopeId: string\n kind: 'root' | 'show' | 'each' | 'branch' | 'scope' | 'child' | 'portal' | 'foreign'\n active: boolean\n children: LifetimeNode[]\n}\n\n// ── Binding ───────────────────────────────────────────────────────\n\n/**\n * Binding output kinds.\n *\n * `'text' | 'prop' | 'attr' | 'class' | 'style'` write their accessor's\n * return value to the DOM. `'effect'` is a side-effect-only watcher:\n * the accessor is invoked every Phase 2 tick its mask is hit, but its\n * return value is discarded and `applyBinding` is a no-op. Used by\n * `child()` to fire the prop-diff/propsMsg cascade on parent updates\n * without the cost of stringifying the returned props bag onto a\n * detached anchor node every render.\n */\nexport type BindingKind = 'text' | 'prop' | 'attr' | 'class' | 'style' | 'effect'\n\nexport interface Binding {\n mask: number\n accessor: (state: unknown) => unknown\n lastValue: unknown\n kind: BindingKind\n node: Node\n key?: string\n ownerLifetime: Lifetime\n perItem: boolean\n dead: boolean\n}\n\n// ── Structural Primitives ─────────────────────────────────────────\n\nexport interface TransitionOptions {\n enter?: (nodes: Node[]) => void | Promise<void>\n leave?: (nodes: Node[]) => void | Promise<void>\n onTransition?: (ctx: { entering: Node[]; leaving: Node[]; parent: Node }) => void | Promise<void>\n}\n\ninterface BranchOptionsBase extends TransitionOptions {\n /**\n * @internal Set by `show()` / `scope()` sugar when delegating to\n * `branch()`, so the dev-only disposer log can report `'show-hide'` /\n * `'scope-rebuild'` instead of the default `'branch-swap'` for the\n * leaving arm. User code should not set this directly.\n */\n __disposalCause?: DisposerEvent['cause']\n /** @internal Compiler-injected mask of paths read by `on`. */\n __mask?: number\n}\n\n/**\n * Discriminant accessor — reads a string-keyed value either from the\n * current state (`(s) => …`) or from a closed-over item accessor /\n * memo (`() => item.kind()`). Both forms re-evaluate on every commit.\n * The zero-arg form is the canonical shape for branching on per-row\n * fields inside an `each.render` callback.\n */\ntype Discriminant<S, K extends string> = ((s: S) => K) | (() => K)\n\n/**\n * All cases covered by `cases` — no default allowed (would be dead code).\n */\ntype BranchOptionsExhaustive<S, M, K extends string> = BranchOptionsBase & {\n on: Discriminant<S, K>\n cases: { [P in K]: (h: View<S, M>) => Node[] }\n default?: never\n}\n\n/**\n * `cases` may cover some but not all keys; `default` handles the rest.\n */\ntype BranchOptionsNonExhaustive<S, M, K extends string> = BranchOptionsBase & {\n on: Discriminant<S, K>\n cases?: { [P in K]?: (h: View<S, M>) => Node[] }\n default: (h: View<S, M>) => Node[]\n}\n\n/**\n * `on` returns a wide `string` — exhaustiveness cannot be verified at\n * compile time (the key domain is infinite). Lenient: `default` is\n * optional so existing call sites that predate exhaustiveness typing\n * continue to compile. Authors who want the gate opt in by narrowing\n * `on`'s return type to a literal union.\n */\ntype BranchOptionsWide<S, M> = BranchOptionsBase & {\n on: Discriminant<S, string>\n cases?: Record<string, (h: View<S, M>) => Node[]>\n default?: (h: View<S, M>) => Node[]\n}\n\n/**\n * Options for `branch()`.\n *\n * When `on` returns a literal string union (e.g. `'idle' | 'loading'\n * | 'done'`), TypeScript enforces exhaustiveness: either every union\n * member has a case (and `default` is disallowed as unreachable), or\n * `default` is required to cover the remainder. When `on` returns a\n * wide `string`, `default` stays optional — exhaustiveness isn't\n * meaningful for an unbounded domain.\n *\n * `cases` is optional when `default` is present; `branch({ on, default })`\n * is the canonical dynamic-rebuild shape — `scope()` sugar wraps\n * exactly this form.\n */\nexport type BranchOptions<S, M = unknown, K extends string = string> = string extends K\n ? BranchOptionsWide<S, M>\n : BranchOptionsExhaustive<S, M, K> | BranchOptionsNonExhaustive<S, M, K>\n\nexport interface ShowOptions<S, M = unknown> extends TransitionOptions {\n /**\n * Predicate evaluated on every commit. `(s) => …` reads from\n * component state; `() => …` (zero-arg) reads from a closed-over\n * source — typically `item.field()` inside an `each.render`\n * callback. Both forms are reactive: the predicate runs against\n * each new state and the rendered subtree mounts/unmounts on the\n * boolean transition.\n */\n when: ((s: S) => boolean) | (() => boolean)\n render: (h: View<S, M>) => Node[]\n fallback?: (h: View<S, M>) => Node[]\n}\n\n/**\n * Options for `scope()` — rebuilds a subtree when `on(state)` changes.\n *\n * Sugar over `branch({ on, cases: {}, default: render, __disposalCause: 'scope-rebuild' })`.\n * Use when the key is dynamic (e.g. an epoch counter bumped from the\n * outside) and you want a fresh arm — fresh lifetime, fresh bindings —\n * each time it changes. Combine with `sample()` inside `render` for a\n * one-shot current-state read.\n */\nexport interface ScopeOptions<S, M = unknown> extends TransitionOptions {\n /**\n * Key accessor evaluated on every commit. `(s) => …` reads from\n * component state; `() => …` (zero-arg) reads from a closed-over\n * source. The render callback rebuilds whenever the returned key\n * changes — fresh lifetime, fresh bindings.\n */\n on: ((s: S) => string) | (() => string)\n render: (h: View<S, M>) => Node[]\n /** @internal Compiler-injected mask of paths read by `on`. */\n __mask?: number\n}\n\n/**\n * Options for `each()`. The inherited `enter` / `leave` callbacks fire **per item**:\n * `enter(nodes)` runs after an item's DOM is inserted (including initial mount);\n * `leave(nodes)` runs before an item's DOM is removed and may return a Promise\n * to hold the DOM until the animation resolves. Setting `leave` disables the\n * bulk-clear / full-replace fast paths.\n */\n/**\n * Per-item accessor. Two access forms:\n * - `item.field` — shorthand, returns accessor for `item.current[field]`\n * - `item(t => t.expr)` — computed expressions\n *\n * In both cases the returned value is a `() => V` accessor.\n * Invoke it (`item.field()`) to read the current value imperatively.\n */\nexport type ItemAccessor<T> = {\n <R>(selector: (t: T) => R): () => R\n} & {\n [K in keyof T]-?: () => T[K]\n} & {\n /**\n * Read the whole current item. Needed when T is a primitive (where the\n * field-map branch collapses to method names like `toString`) or when\n * you want to sample the entire record rather than a single field.\n *\n * Shadows any literal `current` field on T — if T has such a field,\n * use `item(r => r.current)` to disambiguate.\n */\n current(): T\n}\n\nexport interface EachOptions<S, T, M = unknown> extends TransitionOptions {\n items: (s: S) => T[]\n key: (item: T) => string | number\n render: (opts: {\n send: Send<M>\n item: ItemAccessor<T>\n /**\n * Plain (non-Proxy) accessor factory. Compiler-output path; avoid in user code\n * (use `item.field` / `item(fn)` — more ergonomic and bypasses Proxy when compiled).\n */\n acc: <R>(selector: (t: T) => R) => () => R\n index: () => number\n /**\n * The component's View bag (`h.text`, `h.show`, `h.branch`,\n * `h.scope`, `h.sample`, …). Each-render callers used to reach\n * for the top-level imports; the bag form is symmetric with how\n * `branch.cases[k]`, `show.render`, and `scope.render` receive it.\n * Both still work — destructure whichever is more convenient.\n */\n h: View<S, M>\n /** @internal Compiler-injected — entry reference for row factory */\n entry?: Record<string, unknown>\n }) => Node[]\n}\n\nexport interface PortalOptions {\n target: HTMLElement | string\n render: () => Node[]\n}\n\nexport interface ForeignOptions<S, M, T extends Record<string, unknown>, Instance> {\n /**\n * Construct the imperative instance. Can be async — return a\n * `Promise<Instance>` to defer construction until e.g. a dynamic\n * `import()` resolves. While the promise is pending:\n *\n * - The container element is in the DOM immediately (so layout\n * doesn't shift when the instance arrives).\n * - `sync` is NOT called. State changes are tracked by the\n * primitive and the latest props are applied as the initial\n * sync right after the promise resolves.\n * - If the owning scope disposes before the promise resolves,\n * `destroy(instance)` runs on resolution — no matter how long\n * the promise takes.\n */\n mount: (ctx: { container: HTMLElement; send: Send<M> }) => Instance | Promise<Instance>\n props: (s: S) => T\n sync:\n | ((ctx: { instance: Instance; props: T; prev: T | undefined }) => void)\n | {\n [K in keyof T]?: (ctx: { instance: Instance; value: T[K]; prev: T[K] | undefined }) => void\n }\n destroy: (instance: Instance) => void\n container?: { tag?: string; attrs?: Record<string, string> }\n}\n\nexport interface ChildOptions<S, ChildM> {\n // Type-erased via AnyComponentDef so callers can pass any concrete\n // ComponentDef<S, M, E, D> without a widening helper. The runtime\n // narrows the message shape via the user-supplied `onMsg` callback,\n // which keeps `ChildM` typed at this boundary even though the def\n // itself is opaque.\n def: AnyComponentDef\n key: string | number\n props: (s: S) => Record<string, unknown>\n onMsg?: (msg: ChildM) => unknown | null\n}\n"]}
@@ -44,5 +44,10 @@ export declare function _handleMsg(inst: ComponentInstance, msg: unknown, dirty:
44
44
  * Shared between genericUpdate and compiler-generated __update.
45
45
  * @public — used by compiler-generated `__update` functions
46
46
  */
47
- export declare function _runPhase2(state: unknown, dirty: number, bindings: Binding[], bindingsBeforePhase1: number, componentName?: string): void;
47
+ export declare function _runPhase2(state: unknown, dirty: number, bindings: Binding[], bindingsBeforePhase1: number, componentName?: string, onBindingError?: (info: {
48
+ kind: string;
49
+ key?: string;
50
+ message: string;
51
+ stack?: string;
52
+ }) => void): void;
48
53
  //# sourceMappingURL=update-loop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-loop.d.ts","sourceRoot":"","sources":["../src/update-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAItD,OAAO,EAAE,KAAK,MAAM,EAAc,MAAM,cAAc,CAAA;AAmBtD,eAAO,MAAM,SAAS,QAAiB,CAAA;AAMvC,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,CAAC,GAAG,EAAE;IAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,KAAK,IAAI,GAClE,IAAI,CAEN;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACtE,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1B,KAAK,EAAE,CAAC,CAAA;IACR,cAAc,EAAE,CAAC,EAAE,CAAA;IACnB,YAAY,EAAE,QAAQ,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,OAAO,EAAE,CAAA;IACtB,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,kBAAkB,EAAE,OAAO,CAAA;IAC3B,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,CAAC,EAAE,CAAA;IAChB,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAA;IACtB,MAAM,EAAE,WAAW,CAAA;IACnB,eAAe,EAAE,eAAe,CAAA;CAgDjC;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,EACvD,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC7B,IAAI,CAAC,EAAE,CAAC,EACR,cAAc,GAAE,QAAQ,GAAG,IAAW,EACtC,GAAG,CAAC,EAAE,MAAM,GACX,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAgD5B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,CAqCxF;AA4GD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,iBAAiB,EACvB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAqCtB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,EAAE,EACnB,oBAAoB,EAAE,MAAM,EAC5B,aAAa,CAAC,EAAE,MAAM,GACrB,IAAI,CAuDN"}
1
+ {"version":3,"file":"update-loop.d.ts","sourceRoot":"","sources":["../src/update-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAItD,OAAO,EAAE,KAAK,MAAM,EAAc,MAAM,cAAc,CAAA;AAoBtD,eAAO,MAAM,SAAS,QAAiB,CAAA;AAMvC,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,CAAC,GAAG,EAAE;IAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,KAAK,IAAI,GAClE,IAAI,CAEN;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACtE,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1B,KAAK,EAAE,CAAC,CAAA;IACR,cAAc,EAAE,CAAC,EAAE,CAAA;IACnB,YAAY,EAAE,QAAQ,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,OAAO,EAAE,CAAA;IACtB,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,kBAAkB,EAAE,OAAO,CAAA;IAC3B,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,CAAC,EAAE,CAAA;IAChB,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAA;IACtB,MAAM,EAAE,WAAW,CAAA;IACnB,eAAe,EAAE,eAAe,CAAA;CAkEjC;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,EACvD,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC7B,IAAI,CAAC,EAAE,CAAC,EACR,cAAc,GAAE,QAAQ,GAAG,IAAW,EACtC,GAAG,CAAC,EAAE,MAAM,GACX,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAgD5B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,CAqExF;AA0LD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,iBAAiB,EACvB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CA0CtB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,EAAE,EACnB,oBAAoB,EAAE,MAAM,EAC5B,aAAa,CAAC,EAAE,MAAM,EAMtB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,GAC/F,IAAI,CA6DN"}