@llui/dom 0.0.19 → 0.0.21

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 (109) hide show
  1. package/README.md +25 -21
  2. package/dist/addressed.d.ts +1 -1
  3. package/dist/addressed.d.ts.map +1 -1
  4. package/dist/addressed.js.map +1 -1
  5. package/dist/binding.d.ts +2 -2
  6. package/dist/binding.d.ts.map +1 -1
  7. package/dist/binding.js +2 -2
  8. package/dist/binding.js.map +1 -1
  9. package/dist/devtools.d.ts +3 -3
  10. package/dist/devtools.d.ts.map +1 -1
  11. package/dist/devtools.js +14 -12
  12. package/dist/devtools.js.map +1 -1
  13. package/dist/el-split.js +3 -3
  14. package/dist/el-split.js.map +1 -1
  15. package/dist/el-template.js +3 -3
  16. package/dist/el-template.js.map +1 -1
  17. package/dist/elements.js +1 -1
  18. package/dist/elements.js.map +1 -1
  19. package/dist/hmr.d.ts +2 -1
  20. package/dist/hmr.d.ts.map +1 -1
  21. package/dist/hmr.js +74 -34
  22. package/dist/hmr.js.map +1 -1
  23. package/dist/index.d.ts +7 -3
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +4 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/internal.d.ts +1 -1
  28. package/dist/internal.d.ts.map +1 -1
  29. package/dist/internal.js +1 -1
  30. package/dist/internal.js.map +1 -1
  31. package/dist/lifetime.d.ts +39 -0
  32. package/dist/lifetime.d.ts.map +1 -0
  33. package/dist/lifetime.js +250 -0
  34. package/dist/lifetime.js.map +1 -0
  35. package/dist/mathml-elements.js +1 -1
  36. package/dist/mathml-elements.js.map +1 -1
  37. package/dist/mount.d.ts +41 -7
  38. package/dist/mount.d.ts.map +1 -1
  39. package/dist/mount.js +216 -9
  40. package/dist/mount.js.map +1 -1
  41. package/dist/primitives/branch.d.ts.map +1 -1
  42. package/dist/primitives/branch.js +30 -20
  43. package/dist/primitives/branch.js.map +1 -1
  44. package/dist/primitives/child.js +12 -12
  45. package/dist/primitives/child.js.map +1 -1
  46. package/dist/primitives/context.js +6 -6
  47. package/dist/primitives/context.js.map +1 -1
  48. package/dist/primitives/each.d.ts.map +1 -1
  49. package/dist/primitives/each.js +37 -32
  50. package/dist/primitives/each.js.map +1 -1
  51. package/dist/primitives/error-boundary.js +7 -7
  52. package/dist/primitives/error-boundary.js.map +1 -1
  53. package/dist/primitives/foreign.js +3 -3
  54. package/dist/primitives/foreign.js.map +1 -1
  55. package/dist/primitives/lazy.js +11 -11
  56. package/dist/primitives/lazy.js.map +1 -1
  57. package/dist/primitives/on-mount.js +2 -2
  58. package/dist/primitives/on-mount.js.map +1 -1
  59. package/dist/primitives/portal.js +4 -4
  60. package/dist/primitives/portal.js.map +1 -1
  61. package/dist/primitives/sample.d.ts +18 -0
  62. package/dist/primitives/sample.d.ts.map +1 -0
  63. package/dist/primitives/sample.js +22 -0
  64. package/dist/primitives/sample.js.map +1 -0
  65. package/dist/primitives/scope.d.ts +25 -0
  66. package/dist/primitives/scope.d.ts.map +1 -0
  67. package/dist/primitives/scope.js +36 -0
  68. package/dist/primitives/scope.js.map +1 -0
  69. package/dist/primitives/selector.js +4 -4
  70. package/dist/primitives/selector.js.map +1 -1
  71. package/dist/primitives/show.d.ts.map +1 -1
  72. package/dist/primitives/show.js +4 -1
  73. package/dist/primitives/show.js.map +1 -1
  74. package/dist/primitives/slice.d.ts.map +1 -1
  75. package/dist/primitives/slice.js +16 -1
  76. package/dist/primitives/slice.js.map +1 -1
  77. package/dist/primitives/text.js +3 -3
  78. package/dist/primitives/text.js.map +1 -1
  79. package/dist/primitives/unsafe-html.d.ts +28 -0
  80. package/dist/primitives/unsafe-html.d.ts.map +1 -0
  81. package/dist/primitives/unsafe-html.js +82 -0
  82. package/dist/primitives/unsafe-html.js.map +1 -0
  83. package/dist/primitives/virtual-each.d.ts.map +1 -1
  84. package/dist/primitives/virtual-each.js +9 -9
  85. package/dist/primitives/virtual-each.js.map +1 -1
  86. package/dist/render-context.d.ts +2 -2
  87. package/dist/render-context.d.ts.map +1 -1
  88. package/dist/render-context.js.map +1 -1
  89. package/dist/ssr.d.ts +4 -4
  90. package/dist/ssr.d.ts.map +1 -1
  91. package/dist/ssr.js +3 -3
  92. package/dist/ssr.js.map +1 -1
  93. package/dist/svg-elements.js +1 -1
  94. package/dist/svg-elements.js.map +1 -1
  95. package/dist/tracking/disposer-log.d.ts +4 -4
  96. package/dist/tracking/disposer-log.d.ts.map +1 -1
  97. package/dist/tracking/disposer-log.js.map +1 -1
  98. package/dist/types.d.ts +50 -9
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js.map +1 -1
  101. package/dist/update-loop.d.ts +3 -3
  102. package/dist/update-loop.d.ts.map +1 -1
  103. package/dist/update-loop.js +12 -8
  104. package/dist/update-loop.js.map +1 -1
  105. package/dist/view-helpers.d.ts +15 -1
  106. package/dist/view-helpers.d.ts.map +1 -1
  107. package/dist/view-helpers.js +6 -0
  108. package/dist/view-helpers.js.map +1 -1
  109. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"child.js","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChildOptions, ComponentDef } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createScope, disposeScope, addDisposer } from '../scope.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from '../update-loop.js'\nimport { createBinding, setFlatBindings } from '../binding.js'\nimport { registerChild, unregisterChild } from '../addressed.js'\nimport { createView } from '../view-helpers.js'\n\nconst FULL_MASK = 0xffffffff\n\nexport function child<S, ChildM>(opts: ChildOptions<S, ChildM>): Node[] {\n // Dev-mode guard: props must be a function, not a static object\n if (typeof opts.props !== 'function') {\n throw new Error(\n `child(\"${String(opts.key)}\"): props must be a reactive accessor function ` +\n `(s => ({ ... })), not a static object. Static props are captured once at mount ` +\n `and never update.`,\n )\n }\n\n const parentCtx = getRenderContext('child')\n const parentScope = parentCtx.rootScope\n const childScope = createScope(parentScope)\n childScope._kind = 'child'\n // Tag eagerly: childScope lives as long as the child component is mounted,\n // so disposing it IS the child-unmount event. Setting the cause up front\n // (instead of inside the disposer closure below) ensures the parent's\n // _disposerLog sees it when `disposeScope` walks up the parent chain —\n // `childInst.rootScope` is an orphan (parent = null) and cannot emit.\n childScope.disposalCause = 'child-unmount'\n const parentSend = parentCtx.send\n\n const childDef = opts.def as ComponentDef<unknown, ChildM, unknown>\n const initialProps = opts.props(parentCtx.state as S)\n const childInst = createComponentInstance(childDef, initialProps)\n\n // Wrap child's send to intercept messages for onMsg → parent\n const originalSend = childInst.send\n childInst.send = (msg: ChildM) => {\n originalSend(msg)\n if (opts.onMsg && parentSend) {\n // Defer to after the child processes — use microtask\n queueMicrotask(() => {\n const parentMsg = opts.onMsg!(msg)\n if (parentMsg != null) {\n parentSend(parentMsg)\n }\n })\n }\n }\n\n // Track props for shallow-diff\n let prevProps: Record<string, unknown> = { ...initialProps }\n\n // Register a binding on the child scope that watches parent props changes.\n // This is a side-effect-only (`kind: 'effect'`) binding: Phase 2 runs the\n // accessor purely to fire the diff + propsMsg dispatch below. There is no\n // DOM output — the comment node is a detached anchor kept only so the\n // Binding shape stays uniform with other kinds.\n createBinding(childScope, {\n mask: FULL_MASK,\n accessor: ((parentState: S) => {\n const newProps = opts.props(parentState)\n\n let changed = false\n for (const key of Object.keys(newProps)) {\n if (!Object.is(newProps[key], prevProps[key])) {\n changed = true\n break\n }\n }\n\n if (changed && childDef.propsMsg) {\n const msg = childDef.propsMsg(newProps)\n // Dispatch via `originalSend` so framework-synthesized propsMsg\n // traffic bypasses the `onMsg` wrapper below. Otherwise a naive\n // `onMsg: m => forward(m)` echoes props/set back to the parent,\n // which mutates parent state, re-fires this accessor, and loops\n // forever.\n originalSend(msg)\n flushInstance(childInst)\n }\n prevProps = { ...newProps }\n }) as (state: never) => unknown,\n kind: 'effect',\n node: document.createComment('child:' + opts.key),\n perItem: false,\n })\n\n // Run the child's view within the child's render context\n setFlatBindings(childInst.allBindings)\n setRenderContext({\n ...childInst,\n send: childInst.send as (msg: unknown) => void,\n instance: childInst as ComponentInstance,\n })\n const nodes = childDef.view(createView(childInst.send))\n clearRenderContext()\n setFlatBindings(parentCtx.allBindings)\n setRenderContext(parentCtx)\n\n // Register in component registry for addressed effects\n registerChild(opts.key, { send: childInst.send as (msg: unknown) => void })\n\n // Cleanup: dispose child instance when parent scope disposes\n addDisposer(childScope, () => {\n unregisterChild(opts.key)\n childInst.rootScope.disposalCause = 'child-unmount'\n disposeScope(childInst.rootScope)\n })\n\n return nodes\n}\n"]}
1
+ {"version":3,"file":"child.js","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChildOptions, ComponentDef } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createLifetime, disposeLifetime, addDisposer } from '../lifetime.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from '../update-loop.js'\nimport { createBinding, setFlatBindings } from '../binding.js'\nimport { registerChild, unregisterChild } from '../addressed.js'\nimport { createView } from '../view-helpers.js'\n\nconst FULL_MASK = 0xffffffff\n\nexport function child<S, ChildM>(opts: ChildOptions<S, ChildM>): Node[] {\n // Dev-mode guard: props must be a function, not a static object\n if (typeof opts.props !== 'function') {\n throw new Error(\n `child(\"${String(opts.key)}\"): props must be a reactive accessor function ` +\n `(s => ({ ... })), not a static object. Static props are captured once at mount ` +\n `and never update.`,\n )\n }\n\n const parentCtx = getRenderContext('child')\n const parentLifetime = parentCtx.rootLifetime\n const childLifetime = createLifetime(parentLifetime)\n childLifetime._kind = 'child'\n // Tag eagerly: childLifetime lives as long as the child component is mounted,\n // so disposing it IS the child-unmount event. Setting the cause up front\n // (instead of inside the disposer closure below) ensures the parent's\n // _disposerLog sees it when `disposeLifetime` walks up the parent chain —\n // `childInst.rootLifetime` is an orphan (parent = null) and cannot emit.\n childLifetime.disposalCause = 'child-unmount'\n const parentSend = parentCtx.send\n\n const childDef = opts.def as ComponentDef<unknown, ChildM, unknown, Record<string, unknown>>\n const initialProps = opts.props(parentCtx.state as S)\n const childInst = createComponentInstance(childDef, initialProps)\n\n // Wrap child's send to intercept messages for onMsg → parent\n const originalSend = childInst.send\n childInst.send = (msg: ChildM) => {\n originalSend(msg)\n if (opts.onMsg && parentSend) {\n // Defer to after the child processes — use microtask\n queueMicrotask(() => {\n const parentMsg = opts.onMsg!(msg)\n if (parentMsg != null) {\n parentSend(parentMsg)\n }\n })\n }\n }\n\n // Track props for shallow-diff\n let prevProps: Record<string, unknown> = { ...initialProps }\n\n // Register a binding on the child scope that watches parent props changes.\n // This is a side-effect-only (`kind: 'effect'`) binding: Phase 2 runs the\n // accessor purely to fire the diff + propsMsg dispatch below. There is no\n // DOM output — the comment node is a detached anchor kept only so the\n // Binding shape stays uniform with other kinds.\n createBinding(childLifetime, {\n mask: FULL_MASK,\n accessor: ((parentState: S) => {\n const newProps = opts.props(parentState)\n\n let changed = false\n for (const key of Object.keys(newProps)) {\n if (!Object.is(newProps[key], prevProps[key])) {\n changed = true\n break\n }\n }\n\n if (changed && childDef.propsMsg) {\n const msg = childDef.propsMsg(newProps)\n // Dispatch via `originalSend` so framework-synthesized propsMsg\n // traffic bypasses the `onMsg` wrapper below. Otherwise a naive\n // `onMsg: m => forward(m)` echoes props/set back to the parent,\n // which mutates parent state, re-fires this accessor, and loops\n // forever.\n originalSend(msg)\n flushInstance(childInst)\n }\n prevProps = { ...newProps }\n }) as (state: never) => unknown,\n kind: 'effect',\n node: document.createComment('child:' + opts.key),\n perItem: false,\n })\n\n // Run the child's view within the child's render context\n setFlatBindings(childInst.allBindings)\n setRenderContext({\n ...childInst,\n send: childInst.send as (msg: unknown) => void,\n instance: childInst as ComponentInstance,\n })\n const nodes = childDef.view(createView(childInst.send))\n clearRenderContext()\n setFlatBindings(parentCtx.allBindings)\n setRenderContext(parentCtx)\n\n // Register in component registry for addressed effects\n registerChild(opts.key, { send: childInst.send as (msg: unknown) => void })\n\n // Cleanup: dispose child instance when parent scope disposes\n addDisposer(childLifetime, () => {\n unregisterChild(opts.key)\n childInst.rootLifetime.disposalCause = 'child-unmount'\n disposeLifetime(childInst.rootLifetime)\n })\n\n return nodes\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js';
2
- import { createScope } from '../scope.js';
2
+ import { createLifetime } from '../lifetime.js';
3
3
  /**
4
4
  * Per-scope storage: scope → (context-id → accessor).
5
5
  * WeakMap so disposed scopes are GC'd.
@@ -35,18 +35,18 @@ export function createContext(defaultValue, name) {
35
35
  */
36
36
  export function provide(ctx, accessor, children) {
37
37
  const renderCtx = getRenderContext('provide');
38
- const parentScope = renderCtx.rootScope;
38
+ const parentLifetime = renderCtx.rootLifetime;
39
39
  // Create a sub-scope so the context is attached to THIS provider alone.
40
40
  // Descendants (including those mounted later via show/branch/each) walk
41
41
  // up to this scope via their own parent chain and find the accessor.
42
42
  // Nested providers create their own sub-scope, shadowing outer values.
43
- const providerScope = createScope(parentScope);
43
+ const providerScope = createLifetime(parentLifetime);
44
44
  const map = new Map();
45
45
  map.set(ctx._id, accessor);
46
46
  contextMap.set(providerScope, map);
47
- // Render children with the provider scope as the new rootScope so any
47
+ // Render children with the provider scope as the new rootLifetime so any
48
48
  // primitives (bindings, structural blocks, nested providers) attach here.
49
- setRenderContext({ ...renderCtx, rootScope: providerScope });
49
+ setRenderContext({ ...renderCtx, rootLifetime: providerScope });
50
50
  try {
51
51
  return children();
52
52
  }
@@ -70,7 +70,7 @@ export function provide(ctx, accessor, children) {
70
70
  export function useContext(ctx) {
71
71
  let scope = null;
72
72
  try {
73
- scope = getRenderContext('useContext').rootScope;
73
+ scope = getRenderContext('useContext').rootLifetime;
74
74
  }
75
75
  catch {
76
76
  // No render context (e.g. called from connect() in a unit test).
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Scope } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createScope } from '../scope.js'\n\n/**\n * Per-scope storage: scope → (context-id → accessor).\n * WeakMap so disposed scopes are GC'd.\n */\nconst contextMap = new WeakMap<Scope, Map<symbol, (s: unknown) => unknown>>()\n\nexport interface Context<T> {\n readonly _id: symbol\n readonly _default: T | undefined\n readonly _name: string | undefined\n}\n\n/**\n * Create a typed context key. Pass a default value to make consumers without a\n * provider resolve to it; omit to make unprovided consumption throw.\n *\n * ```ts\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n * ```\n */\nexport function createContext<T>(defaultValue?: T, name?: string): Context<T> {\n return { _id: Symbol(name ?? 'llui-ctx'), _default: defaultValue, _name: name }\n}\n\n/**\n * Provide a reactive value for `ctx` to every descendant rendered inside `children`.\n * The accessor `(s: S) => T` is evaluated lazily at binding read time, so providers\n * can thread state slices down without prop drilling.\n *\n * ```ts\n * view: ({ send }) => [\n * provide(ThemeContext, (s: State) => s.theme, () => [\n * header(send),\n * main(send),\n * ]),\n * ]\n * ```\n *\n * Nested providers shadow outer ones within their subtree. The outer value is\n * restored after `children()` returns, so sibling subtrees aren't affected.\n */\nexport function provide<S, T>(\n ctx: Context<T>,\n accessor: (s: S) => T,\n children: () => Node[],\n): Node[] {\n const renderCtx = getRenderContext('provide')\n const parentScope = renderCtx.rootScope\n // Create a sub-scope so the context is attached to THIS provider alone.\n // Descendants (including those mounted later via show/branch/each) walk\n // up to this scope via their own parent chain and find the accessor.\n // Nested providers create their own sub-scope, shadowing outer values.\n const providerScope = createScope(parentScope)\n const map = new Map<symbol, (s: unknown) => unknown>()\n map.set(ctx._id, accessor as (s: unknown) => unknown)\n contextMap.set(providerScope, map)\n // Render children with the provider scope as the new rootScope so any\n // primitives (bindings, structural blocks, nested providers) attach here.\n setRenderContext({ ...renderCtx, rootScope: providerScope })\n try {\n return children()\n } finally {\n clearRenderContext()\n setRenderContext(renderCtx)\n }\n}\n\n/**\n * Read a context accessor within a view or view-function. Walks the scope chain\n * from the current render point to find the nearest provider. Returns an\n * `(s: S) => T` accessor that can be passed to bindings (text/class/etc.).\n *\n * ```ts\n * export function themedCard(): Node[] {\n * const theme = useContext(ThemeContext)\n * return div({ class: (s) => `card theme-${theme(s)}` }, [...])\n * }\n * ```\n */\nexport function useContext<S, T>(ctx: Context<T>): (s: S) => T {\n let scope: Scope | null = null\n try {\n scope = getRenderContext('useContext').rootScope\n } catch {\n // No render context (e.g. called from connect() in a unit test).\n // Fall through to default resolution below.\n }\n while (scope) {\n const map = contextMap.get(scope)\n if (map?.has(ctx._id)) {\n const accessor = map.get(ctx._id)!\n return accessor as (s: S) => T\n }\n scope = scope.parent\n }\n if (ctx._default !== undefined) {\n const d = ctx._default\n return () => d\n }\n const label = ctx._name ?? ctx._id.description ?? 'unknown'\n throw new Error(\n `[LLui] useContext(${label}): no provider found and no default value. ` +\n `Wrap a parent element with provide(${label}, accessor, () => [...]) ` +\n `or pass a default to createContext().`,\n )\n}\n\n/**\n * Provide a state-independent value to every descendant. Companion to\n * `provide()` for the common case of publishing a stable dispatcher\n * bag, callback record, or DI container — anything that doesn't depend\n * on the parent's state.\n *\n * ```ts\n * provideValue(ToastContext, { show: (m) => send({ type: 'toast', m }) }, () => [\n * main([pageSlot()]),\n * ])\n * ```\n *\n * Equivalent to `provide(ctx, () => value, children)`, but exists so\n * the call site reads as \"provide this value\" rather than \"provide an\n * accessor that ignores its state argument and returns a value.\" Pair\n * with `useContextValue` for symmetric ergonomics on the consumer side.\n *\n * Internally still uses the accessor mechanism: the value is wrapped\n * in a constant lambda. Consumers that read via the reactive\n * `useContext` form will get an `(s) => T` whose accessor ignores `s`.\n */\nexport function provideValue<T>(ctx: Context<T>, value: T, children: () => Node[]): Node[] {\n return provide(ctx, () => value, children)\n}\n\n/**\n * Read a state-independent value from the nearest provider. Companion\n * to `useContext()` for the common case of consuming a stable\n * dispatcher bag, callback record, or DI container.\n *\n * ```ts\n * const toast = useContextValue(ToastContext)\n * button({ onClick: () => toast.show('Saved') }, [text('Save')])\n * ```\n *\n * Equivalent to calling the accessor returned by `useContext` with\n * `undefined`, but reads as a single function call instead of a\n * three-step \"look up the accessor, ignore the state arg, get the\n * value\" dance.\n *\n * ## Value capture contract\n *\n * **The returned value is captured once, at view-construction time.**\n * Any reference you store from `useContextValue(ctx)` into a closure\n * — for example, by assigning it to a local `const` inside `view(...)`\n * and reading it from an event handler — sees the value as it was\n * when the view ran. The closure does NOT re-read the context on each\n * event dispatch.\n *\n * That's fine, and usually what you want, for stable dispatcher bags:\n * the bag's methods close over the layout's `send`, and `send` itself\n * is stable across the layout's lifetime, so the methods work\n * correctly regardless of when the handler fires. Pages can stash\n * `const toast = useContextValue(ToastContext)` at the top of their\n * `view()` and call `toast.show(...)` from any event handler below.\n *\n * **Do NOT use `useContextValue` when the consumer needs to see\n * updates to the context value.** If a parent re-`provideValue`s the\n * context with a different object later, existing consumers already\n * holding the captured value will still see the old one. For\n * reactive consumption, use `useContext(ctx)` — that returns an\n * accessor that re-reads the provider on each binding evaluation, so\n * reactive bindings (`class`, `text`, etc.) pick up updates\n * automatically.\n *\n * **Do NOT use `useContextValue` against a provider whose accessor\n * reads from state.** The accessor is invoked with `undefined` here,\n * so any `(s) => s.something` provider will throw or return garbage.\n * Match `provideValue` on the producer side with `useContextValue` on\n * the consumer side, and `provide` with `useContext`.\n */\nexport function useContextValue<T>(ctx: Context<T>): T {\n const accessor = useContext<unknown, T>(ctx)\n // The contract above: the producer side promised this accessor\n // doesn't read its state arg. Pass undefined.\n return accessor(undefined)\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Lifetime } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createLifetime } from '../lifetime.js'\n\n/**\n * Per-scope storage: scope → (context-id → accessor).\n * WeakMap so disposed scopes are GC'd.\n */\nconst contextMap = new WeakMap<Lifetime, Map<symbol, (s: unknown) => unknown>>()\n\nexport interface Context<T> {\n readonly _id: symbol\n readonly _default: T | undefined\n readonly _name: string | undefined\n}\n\n/**\n * Create a typed context key. Pass a default value to make consumers without a\n * provider resolve to it; omit to make unprovided consumption throw.\n *\n * ```ts\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n * ```\n */\nexport function createContext<T>(defaultValue?: T, name?: string): Context<T> {\n return { _id: Symbol(name ?? 'llui-ctx'), _default: defaultValue, _name: name }\n}\n\n/**\n * Provide a reactive value for `ctx` to every descendant rendered inside `children`.\n * The accessor `(s: S) => T` is evaluated lazily at binding read time, so providers\n * can thread state slices down without prop drilling.\n *\n * ```ts\n * view: ({ send }) => [\n * provide(ThemeContext, (s: State) => s.theme, () => [\n * header(send),\n * main(send),\n * ]),\n * ]\n * ```\n *\n * Nested providers shadow outer ones within their subtree. The outer value is\n * restored after `children()` returns, so sibling subtrees aren't affected.\n */\nexport function provide<S, T>(\n ctx: Context<T>,\n accessor: (s: S) => T,\n children: () => Node[],\n): Node[] {\n const renderCtx = getRenderContext('provide')\n const parentLifetime = renderCtx.rootLifetime\n // Create a sub-scope so the context is attached to THIS provider alone.\n // Descendants (including those mounted later via show/branch/each) walk\n // up to this scope via their own parent chain and find the accessor.\n // Nested providers create their own sub-scope, shadowing outer values.\n const providerScope = createLifetime(parentLifetime)\n const map = new Map<symbol, (s: unknown) => unknown>()\n map.set(ctx._id, accessor as (s: unknown) => unknown)\n contextMap.set(providerScope, map)\n // Render children with the provider scope as the new rootLifetime so any\n // primitives (bindings, structural blocks, nested providers) attach here.\n setRenderContext({ ...renderCtx, rootLifetime: providerScope })\n try {\n return children()\n } finally {\n clearRenderContext()\n setRenderContext(renderCtx)\n }\n}\n\n/**\n * Read a context accessor within a view or view-function. Walks the scope chain\n * from the current render point to find the nearest provider. Returns an\n * `(s: S) => T` accessor that can be passed to bindings (text/class/etc.).\n *\n * ```ts\n * export function themedCard(): Node[] {\n * const theme = useContext(ThemeContext)\n * return div({ class: (s) => `card theme-${theme(s)}` }, [...])\n * }\n * ```\n */\nexport function useContext<S, T>(ctx: Context<T>): (s: S) => T {\n let scope: Lifetime | null = null\n try {\n scope = getRenderContext('useContext').rootLifetime\n } catch {\n // No render context (e.g. called from connect() in a unit test).\n // Fall through to default resolution below.\n }\n while (scope) {\n const map = contextMap.get(scope)\n if (map?.has(ctx._id)) {\n const accessor = map.get(ctx._id)!\n return accessor as (s: S) => T\n }\n scope = scope.parent\n }\n if (ctx._default !== undefined) {\n const d = ctx._default\n return () => d\n }\n const label = ctx._name ?? ctx._id.description ?? 'unknown'\n throw new Error(\n `[LLui] useContext(${label}): no provider found and no default value. ` +\n `Wrap a parent element with provide(${label}, accessor, () => [...]) ` +\n `or pass a default to createContext().`,\n )\n}\n\n/**\n * Provide a state-independent value to every descendant. Companion to\n * `provide()` for the common case of publishing a stable dispatcher\n * bag, callback record, or DI container — anything that doesn't depend\n * on the parent's state.\n *\n * ```ts\n * provideValue(ToastContext, { show: (m) => send({ type: 'toast', m }) }, () => [\n * main([pageSlot()]),\n * ])\n * ```\n *\n * Equivalent to `provide(ctx, () => value, children)`, but exists so\n * the call site reads as \"provide this value\" rather than \"provide an\n * accessor that ignores its state argument and returns a value.\" Pair\n * with `useContextValue` for symmetric ergonomics on the consumer side.\n *\n * Internally still uses the accessor mechanism: the value is wrapped\n * in a constant lambda. Consumers that read via the reactive\n * `useContext` form will get an `(s) => T` whose accessor ignores `s`.\n */\nexport function provideValue<T>(ctx: Context<T>, value: T, children: () => Node[]): Node[] {\n return provide(ctx, () => value, children)\n}\n\n/**\n * Read a state-independent value from the nearest provider. Companion\n * to `useContext()` for the common case of consuming a stable\n * dispatcher bag, callback record, or DI container.\n *\n * ```ts\n * const toast = useContextValue(ToastContext)\n * button({ onClick: () => toast.show('Saved') }, [text('Save')])\n * ```\n *\n * Equivalent to calling the accessor returned by `useContext` with\n * `undefined`, but reads as a single function call instead of a\n * three-step \"look up the accessor, ignore the state arg, get the\n * value\" dance.\n *\n * ## Value capture contract\n *\n * **The returned value is captured once, at view-construction time.**\n * Any reference you store from `useContextValue(ctx)` into a closure\n * — for example, by assigning it to a local `const` inside `view(...)`\n * and reading it from an event handler — sees the value as it was\n * when the view ran. The closure does NOT re-read the context on each\n * event dispatch.\n *\n * That's fine, and usually what you want, for stable dispatcher bags:\n * the bag's methods close over the layout's `send`, and `send` itself\n * is stable across the layout's lifetime, so the methods work\n * correctly regardless of when the handler fires. Pages can stash\n * `const toast = useContextValue(ToastContext)` at the top of their\n * `view()` and call `toast.show(...)` from any event handler below.\n *\n * **Do NOT use `useContextValue` when the consumer needs to see\n * updates to the context value.** If a parent re-`provideValue`s the\n * context with a different object later, existing consumers already\n * holding the captured value will still see the old one. For\n * reactive consumption, use `useContext(ctx)` — that returns an\n * accessor that re-reads the provider on each binding evaluation, so\n * reactive bindings (`class`, `text`, etc.) pick up updates\n * automatically.\n *\n * **Do NOT use `useContextValue` against a provider whose accessor\n * reads from state.** The accessor is invoked with `undefined` here,\n * so any `(s) => s.something` provider will throw or return garbage.\n * Match `provideValue` on the producer side with `useContextValue` on\n * the consumer side, and `provide` with `useContext`.\n */\nexport function useContextValue<T>(ctx: Context<T>): T {\n const accessor = useContext<unknown, T>(ctx)\n // The contract above: the producer side promised this accessor\n // doesn't read its state arg. Pass undefined.\n return accessor(undefined)\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/primitives/each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAA;AAuBnE,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAEpD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,CAEzE;AAgCD,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAmR1E"}
1
+ {"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/primitives/each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,aAAa,CAAA;AAuBtE,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAEpD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,CAEzE;AAgCD,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAmR1E"}
@@ -1,5 +1,5 @@
1
1
  import { getRenderContext, setRenderContext, clearRenderContext, } from '../render-context.js';
2
- import { createScope, disposeScope, disposeScopesBulk, addDisposer, removeOrphanedChildren, } from '../scope.js';
2
+ import { createLifetime, disposeLifetime, disposeLifetimesBulk, addDisposer, removeOrphanedChildren, } from '../lifetime.js';
3
3
  import { getFlatBindings, setFlatBindings } from '../binding.js';
4
4
  import { FULL_MASK } from '../update-loop.js';
5
5
  // Clear callbacks — registered by selector.bind() during render, called by reconcileClear().
@@ -18,7 +18,7 @@ export function registerOnRemove(cb) {
18
18
  }
19
19
  // Reusable render context for buildEntry — avoids object allocation per entry
20
20
  const buildCtx = {
21
- rootScope: null,
21
+ rootLifetime: null,
22
22
  state: null,
23
23
  allBindings: [],
24
24
  structuralBlocks: [],
@@ -35,7 +35,7 @@ const buildBag = {
35
35
  };
36
36
  export function each(opts) {
37
37
  const ctx = getRenderContext('each');
38
- const parentScope = ctx.rootScope;
38
+ const parentLifetime = ctx.rootLifetime;
39
39
  const blocks = ctx.structuralBlocks;
40
40
  const anchor = document.createComment('each');
41
41
  const entries = [];
@@ -111,7 +111,7 @@ export function each(opts) {
111
111
  lastItemsRef = newItems;
112
112
  const oldKeys = snapshotKeys();
113
113
  const report = opts.onTransition ? { entering: [], leaving: [] } : null;
114
- reconcileEntries(entries, newItems, opts, parentScope, parent, anchor, ctx, state, leaving, report);
114
+ reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, ctx, state, leaving, report);
115
115
  if (opts.onTransition && report) {
116
116
  opts.onTransition({ entering: report.entering, leaving: report.leaving, parent });
117
117
  }
@@ -159,8 +159,8 @@ export function each(opts) {
159
159
  s.disposalCause = 'each-remove';
160
160
  scopes.push(s);
161
161
  }
162
- disposeScopesBulk(scopes);
163
- removeOrphanedChildren(parentScope);
162
+ disposeLifetimesBulk(scopes);
163
+ removeOrphanedChildren(parentLifetime);
164
164
  entries.length = 0;
165
165
  emitDiff(oldKeys);
166
166
  },
@@ -178,7 +178,7 @@ export function each(opts) {
178
178
  const newLen = newItems.length;
179
179
  if (newLen >= oldLen) {
180
180
  // Not a removal — fallback (shouldn't happen if compiler detected correctly)
181
- reconcileEntries(entries, newItems, opts, parentScope, parent, anchor, ctx, state, leaving, null);
181
+ reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, ctx, state, leaving, null);
182
182
  emitDiff(oldKeys);
183
183
  return;
184
184
  }
@@ -201,7 +201,7 @@ export function each(opts) {
201
201
  for (const node of entry.nodes)
202
202
  parent.removeChild(node);
203
203
  entry.scope.disposalCause = 'each-remove';
204
- disposeScope(entry.scope, true);
204
+ disposeLifetime(entry.scope, true);
205
205
  entries[oi] = null;
206
206
  didRemove = true;
207
207
  }
@@ -214,7 +214,7 @@ export function each(opts) {
214
214
  entries[w++] = entries[r];
215
215
  }
216
216
  entries.length = w;
217
- removeOrphanedChildren(parentScope);
217
+ removeOrphanedChildren(parentLifetime);
218
218
  }
219
219
  // Update indices for remaining entries
220
220
  for (let i = 0; i < entries.length; i++) {
@@ -245,7 +245,7 @@ export function each(opts) {
245
245
  activeRemoveCallbacks = removeCallbacks;
246
246
  for (let i = 0; i < initialItems.length; i++) {
247
247
  const item = initialItems[i];
248
- const entry = buildEntry(item, i, opts, parentScope, ctx);
248
+ const entry = buildEntry(item, i, opts, parentLifetime, ctx);
249
249
  entries.push(entry);
250
250
  }
251
251
  activeClearCallbacks = null;
@@ -257,15 +257,15 @@ export function each(opts) {
257
257
  opts.enter(entry.nodes);
258
258
  }
259
259
  }
260
- addDisposer(parentScope, () => {
260
+ addDisposer(parentLifetime, () => {
261
261
  const idx = blocks.indexOf(block);
262
262
  if (idx !== -1)
263
263
  blocks.splice(idx, 1);
264
- // parentScope is being disposed — its children array is about to be
264
+ // parentLifetime is being disposed — its children array is about to be
265
265
  // cleared by the recursive dispose pass, so skip per-entry parent
266
266
  // removal (avoids O(N²) indexOf+splice).
267
267
  for (const entry of entries) {
268
- disposeScope(entry.scope, true);
268
+ disposeLifetime(entry.scope, true);
269
269
  }
270
270
  entries.length = 0;
271
271
  // Force-remove any mid-leave entries immediately
@@ -274,7 +274,7 @@ export function each(opts) {
274
274
  if (node.parentNode)
275
275
  node.parentNode.removeChild(node);
276
276
  }
277
- disposeScope(entry.scope, true);
277
+ disposeLifetime(entry.scope, true);
278
278
  }
279
279
  leaving.length = 0;
280
280
  });
@@ -298,7 +298,7 @@ function removeEntry(entry, opts, leaving) {
298
298
  node.parentNode.removeChild(node);
299
299
  }
300
300
  entry.scope.disposalCause = 'each-remove';
301
- disposeScope(entry.scope);
301
+ disposeLifetime(entry.scope);
302
302
  const idx = leaving.indexOf(entry);
303
303
  if (idx !== -1)
304
304
  leaving.splice(idx, 1);
@@ -318,12 +318,12 @@ function fireEnter(entry, opts) {
318
318
  void opts.enter(entry.nodes);
319
319
  }
320
320
  }
321
- function buildEntry(item, index, opts, parentScope, ctx, state) {
321
+ function buildEntry(item, index, opts, parentLifetime, ctx, state) {
322
322
  const key = opts.key(item);
323
323
  // Use a lightweight scope — just needs itemUpdaters for per-item bindings.
324
324
  // Full scope features (disposers, bindings, children) are only needed when
325
325
  // the render callback uses structural primitives or selector.bind().
326
- const scope = createScope(parentScope);
326
+ const scope = createLifetime(parentLifetime);
327
327
  scope._kind = 'each';
328
328
  const currentState = (state ?? ctx.state);
329
329
  const send = ctx.send;
@@ -358,7 +358,12 @@ function buildEntry(item, index, opts, parentScope, ctx, state) {
358
358
  else {
359
359
  fieldCache = new Map();
360
360
  }
361
- const accessor = () => entry.current[key];
361
+ // `current` returns the whole item — essential for primitive T
362
+ // (where the field map is useless) and for whole-record sampling.
363
+ // Caller must call it like a method: `item.current()`.
364
+ const accessor = key === 'current'
365
+ ? () => entry.current
366
+ : () => entry.current[key];
362
367
  accessor.__perItem = true;
363
368
  fieldCache.set(key, accessor);
364
369
  return accessor;
@@ -368,7 +373,7 @@ function buildEntry(item, index, opts, parentScope, ctx, state) {
368
373
  };
369
374
  const indexAccessor = () => entry.index;
370
375
  // Reuse a single context object to avoid allocation per entry
371
- buildCtx.rootScope = scope;
376
+ buildCtx.rootLifetime = scope;
372
377
  buildCtx.state = currentState;
373
378
  buildCtx.allBindings = ctx.allBindings;
374
379
  buildCtx.structuralBlocks = ctx.structuralBlocks;
@@ -405,7 +410,7 @@ function collectNodes(target, nodes) {
405
410
  for (const n of nodes)
406
411
  target.push(n);
407
412
  }
408
- function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor, ctx, state, leaving, report) {
413
+ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, ctx, state, leaving, report) {
409
414
  const oldLen = entries.length;
410
415
  const newLen = newItems.length;
411
416
  const hasLeave = !!opts.leave;
@@ -440,8 +445,8 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
440
445
  s.disposalCause = 'each-remove';
441
446
  scopes.push(s);
442
447
  }
443
- disposeScopesBulk(scopes);
444
- removeOrphanedChildren(parentScope);
448
+ disposeLifetimesBulk(scopes);
449
+ removeOrphanedChildren(parentLifetime);
445
450
  entries.length = 0;
446
451
  return;
447
452
  }
@@ -458,7 +463,7 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
458
463
  const frag = document.createDocumentFragment();
459
464
  const newlyAdded = [];
460
465
  for (let i = oldLen; i < newLen; i++) {
461
- const entry = buildEntry(newItems[i], i, opts, parentScope, ctx, state);
466
+ const entry = buildEntry(newItems[i], i, opts, parentLifetime, ctx, state);
462
467
  entries.push(entry);
463
468
  newlyAdded.push(entry);
464
469
  for (const node of entry.nodes)
@@ -547,20 +552,20 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
547
552
  range.setEndAfter(lastEntry.nodes[lastEntry.nodes.length - 1]);
548
553
  range.deleteContents();
549
554
  // Bulk dispose all old scopes
550
- const oldScopes = [];
555
+ const oldLifetimes = [];
551
556
  for (let i = 0; i < entries.length; i++) {
552
557
  const s = entries[i].scope;
553
558
  s.disposalCause = 'each-remove';
554
- oldScopes.push(s);
559
+ oldLifetimes.push(s);
555
560
  }
556
- disposeScopesBulk(oldScopes);
557
- removeOrphanedChildren(parentScope);
561
+ disposeLifetimesBulk(oldLifetimes);
562
+ removeOrphanedChildren(parentLifetime);
558
563
  entries.length = 0;
559
564
  // Build all new entries into a fragment
560
565
  const frag = document.createDocumentFragment();
561
566
  const newlyAdded = [];
562
567
  for (let i = 0; i < newLen; i++) {
563
- const entry = buildEntry(newItems[i], i, opts, parentScope, ctx, state);
568
+ const entry = buildEntry(newItems[i], i, opts, parentLifetime, ctx, state);
564
569
  entries.push(entry);
565
570
  newlyAdded.push(entry);
566
571
  for (const node of entry.nodes)
@@ -594,14 +599,14 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
594
599
  newEntries.push(existing);
595
600
  }
596
601
  else {
597
- const entry = buildEntry(item, i, opts, parentScope, ctx, state);
602
+ const entry = buildEntry(item, i, opts, parentLifetime, ctx, state);
598
603
  newEntries.push(entry);
599
604
  newlyAdded.push(entry);
600
605
  }
601
606
  }
602
607
  // Remove entries not in the new list. Use bulk-detach pattern so
603
608
  // disposing K removals costs O(K+P) rather than O(K*P) where P is
604
- // parentScope.children.length (avoids K * indexOf+splice).
609
+ // parentLifetime.children.length (avoids K * indexOf+splice).
605
610
  let didBulkDetach = false;
606
611
  for (const entry of entries) {
607
612
  if (!usedKeys.has(entry.key)) {
@@ -614,13 +619,13 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
614
619
  for (const node of entry.nodes)
615
620
  parent.removeChild(node);
616
621
  entry.scope.disposalCause = 'each-remove';
617
- disposeScope(entry.scope, true);
622
+ disposeLifetime(entry.scope, true);
618
623
  didBulkDetach = true;
619
624
  }
620
625
  }
621
626
  }
622
627
  if (didBulkDetach)
623
- removeOrphanedChildren(parentScope);
628
+ removeOrphanedChildren(parentLifetime);
624
629
  // Reorder DOM
625
630
  const hasSurvivors = newEntries.some((e) => oldByKey.has(e.key));
626
631
  if (!hasSurvivors || !survivorsInOrder(entries, newEntries, usedKeys)) {