@vielzeug/ore 26.11.9 → 26.11.11

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 (118) hide show
  1. package/dist/base-element.cjs +1 -1
  2. package/dist/base-element.cjs.map +1 -1
  3. package/dist/base-element.d.ts +12 -39
  4. package/dist/base-element.d.ts.map +1 -1
  5. package/dist/base-element.js +1 -1
  6. package/dist/base-element.js.map +1 -1
  7. package/dist/context.cjs.map +1 -1
  8. package/dist/context.js.map +1 -1
  9. package/dist/define.cjs +1 -1
  10. package/dist/define.cjs.map +1 -1
  11. package/dist/define.d.ts.map +1 -1
  12. package/dist/define.js +1 -1
  13. package/dist/define.js.map +1 -1
  14. package/dist/directives/each.cjs +1 -1
  15. package/dist/directives/each.cjs.map +1 -1
  16. package/dist/directives/each.d.ts.map +1 -1
  17. package/dist/directives/each.js +1 -1
  18. package/dist/directives/each.js.map +1 -1
  19. package/dist/directives/model.cjs.map +1 -1
  20. package/dist/directives/model.d.ts +2 -3
  21. package/dist/directives/model.d.ts.map +1 -1
  22. package/dist/directives/model.js.map +1 -1
  23. package/dist/directives/unsafe-html.cjs +1 -1
  24. package/dist/directives/unsafe-html.cjs.map +1 -1
  25. package/dist/directives/unsafe-html.d.ts.map +1 -1
  26. package/dist/directives/unsafe-html.js +1 -1
  27. package/dist/directives/unsafe-html.js.map +1 -1
  28. package/dist/directives/when.cjs +1 -1
  29. package/dist/directives/when.cjs.map +1 -1
  30. package/dist/directives/when.d.ts.map +1 -1
  31. package/dist/directives/when.js +1 -1
  32. package/dist/directives/when.js.map +1 -1
  33. package/dist/errors.cjs +1 -1
  34. package/dist/errors.cjs.map +1 -1
  35. package/dist/errors.d.ts +2 -0
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +1 -1
  38. package/dist/errors.js.map +1 -1
  39. package/dist/host-bind.cjs +1 -1
  40. package/dist/host-bind.cjs.map +1 -1
  41. package/dist/host-bind.d.ts +2 -2
  42. package/dist/host-bind.js +1 -1
  43. package/dist/host-bind.js.map +1 -1
  44. package/dist/index.cjs +1 -1
  45. package/dist/index.d.ts +0 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -1
  48. package/dist/ore.cjs +2 -2
  49. package/dist/ore.cjs.map +1 -1
  50. package/dist/ore.iife.js +2 -2
  51. package/dist/ore.iife.js.map +1 -1
  52. package/dist/ore.js +411 -424
  53. package/dist/ore.js.map +1 -1
  54. package/dist/props.cjs +1 -1
  55. package/dist/props.cjs.map +1 -1
  56. package/dist/props.d.ts +6 -10
  57. package/dist/props.d.ts.map +1 -1
  58. package/dist/props.js +1 -1
  59. package/dist/props.js.map +1 -1
  60. package/dist/runtime.cjs +1 -1
  61. package/dist/runtime.cjs.map +1 -1
  62. package/dist/runtime.d.ts +22 -9
  63. package/dist/runtime.d.ts.map +1 -1
  64. package/dist/runtime.js +1 -1
  65. package/dist/runtime.js.map +1 -1
  66. package/dist/slots.cjs.map +1 -1
  67. package/dist/slots.d.ts.map +1 -1
  68. package/dist/slots.js.map +1 -1
  69. package/dist/template/binding-types.d.ts +3 -9
  70. package/dist/template/binding-types.d.ts.map +1 -1
  71. package/dist/template/bindings.cjs +1 -1
  72. package/dist/template/bindings.cjs.map +1 -1
  73. package/dist/template/bindings.d.ts +8 -9
  74. package/dist/template/bindings.d.ts.map +1 -1
  75. package/dist/template/bindings.js +1 -1
  76. package/dist/template/bindings.js.map +1 -1
  77. package/dist/template/instantiator.cjs +1 -1
  78. package/dist/template/instantiator.cjs.map +1 -1
  79. package/dist/template/instantiator.d.ts.map +1 -1
  80. package/dist/template/instantiator.js +1 -1
  81. package/dist/template/instantiator.js.map +1 -1
  82. package/dist/template/result.cjs +1 -1
  83. package/dist/template/result.cjs.map +1 -1
  84. package/dist/template/result.d.ts +10 -16
  85. package/dist/template/result.d.ts.map +1 -1
  86. package/dist/template/result.js +1 -1
  87. package/dist/template/result.js.map +1 -1
  88. package/dist/testing/index.d.ts +1 -1
  89. package/dist/testing/index.d.ts.map +1 -1
  90. package/dist/testing/mount.cjs +1 -1
  91. package/dist/testing/mount.cjs.map +1 -1
  92. package/dist/testing/mount.d.ts.map +1 -1
  93. package/dist/testing/mount.js +1 -1
  94. package/dist/testing/mount.js.map +1 -1
  95. package/dist/testing/render-hook.cjs +1 -1
  96. package/dist/testing/render-hook.cjs.map +1 -1
  97. package/dist/testing/render-hook.d.ts +16 -13
  98. package/dist/testing/render-hook.d.ts.map +1 -1
  99. package/dist/testing/render-hook.js +1 -1
  100. package/dist/testing/render-hook.js.map +1 -1
  101. package/dist/testing.cjs.map +1 -1
  102. package/dist/testing.js.map +1 -1
  103. package/dist/utils/brand.cjs.map +1 -1
  104. package/dist/utils/brand.d.ts +1 -1
  105. package/dist/utils/brand.js.map +1 -1
  106. package/dist/utils/dom.cjs +1 -1
  107. package/dist/utils/dom.cjs.map +1 -1
  108. package/dist/utils/dom.d.ts +12 -10
  109. package/dist/utils/dom.d.ts.map +1 -1
  110. package/dist/utils/dom.js +1 -1
  111. package/dist/utils/dom.js.map +1 -1
  112. package/package.json +5 -5
  113. package/dist/directives/live.cjs +0 -2
  114. package/dist/directives/live.cjs.map +0 -1
  115. package/dist/directives/live.d.ts +0 -23
  116. package/dist/directives/live.d.ts.map +0 -1
  117. package/dist/directives/live.js +0 -2
  118. package/dist/directives/live.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"each.js","names":["rawEffect","createItem","dataSignal","signal","item","indexSignal","index","scope","createScope","cleanups","nodes","rangeStart","staging","render","fn","rangeEnd","removeItem","entry","runAll","removeNodes","reusedEntriesInOrder","previousPosition","i","itemsOrdered","lastPosition","nextOrdered","position","reconcileItems","next","key","nextKeys","itemsMap","untrack","createdThisPass","batch","invariant","reusedInOrder","created","cursor","endMarker","j","firstNode","node","parent","nextKeySet","rollbackCreated","candidate","collectKeys","keys","seen","keyFn","OreApiError","ORE_ERRORS","reportEachError","cause","error","reportRuntimeError","OreLifecycleError","anchor","each","listSignal","list","computed","createDirectiveResult","initialParent","container","fallbackNodes","fallbackCleanups","mountFallback","fallback","clearFallback","sub","effect","nextList","registerCleanup"],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { type MaybeReactive, removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = MaybeReactive<T[]>;\ntype ItemKey = number | string;\n\n/**\n * A rendered list item. `nodes` is the item's contiguous sibling range,\n * bracketed by `each/item-start` and `each/item` sentinel comments: the\n * sentinels wrap the template's own nodes, and every reactive directive inside\n * the item (`when()`, nested `each()`, html bindings) inserts and re-inserts\n * its content as siblings relative to nodes between the sentinels — before or\n * after its own anchor, but never past a sentinel. Relocating the item\n * therefore means moving the whole bracketed range, node by node: never a\n * bare subset.\n */\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\n/**\n * Mounts the item into `staging` (a detached fragment), bracketing its node\n * range with the start and end sentinels. Synchronous directive content mounts\n * between the sentinels (the end sentinel only exists once the whole template\n * is mounted), so the range is complete the moment this returns.\n */\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n staging: DocumentFragment,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n const cleanups: (() => void)[] = [];\n let nodes: Node[] = [];\n\n const rangeStart = document.createComment('each/item-start');\n\n staging.appendChild(rangeStart);\n\n scope.run(() => {\n const result = render(dataSignal, indexSignal);\n\n nodes = result.mount(staging, null, (fn) => cleanups.push(fn));\n });\n\n const rangeEnd = document.createComment('each/item');\n\n staging.appendChild(rangeEnd);\n nodes = [rangeStart, ...nodes, rangeEnd];\n\n return { cleanups, data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * True when reused entries appear in the next list in the same relative order\n * they hold in the DOM (`itemsOrdered`). Prepends, appends, interleaved\n * inserts, and content replacement all satisfy this; only genuine reorders\n * (or removes that flip survivor order) do not.\n */\nconst reusedEntriesInOrder = <T>(nextOrdered: ItemEntry<T>[], itemsOrdered: ItemEntry<T>[]): boolean => {\n const previousPosition = new Map<ItemEntry<T>, number>();\n\n for (let i = 0; i < itemsOrdered.length; i++) previousPosition.set(itemsOrdered[i], i);\n\n let lastPosition = -1;\n\n for (const entry of nextOrdered) {\n const position = previousPosition.get(entry);\n\n if (position === undefined) continue;\n\n if (position < lastPosition) return false;\n\n lastPosition = position;\n }\n\n return true;\n};\n\n/**\n * Reconciles the live item map against the next array. The pass is transactional:\n * new entries are created first, existing entries are updated in-place via signal\n * writes, and stale entries are destroyed only after the whole pass has succeeded.\n * If creation throws (user render code), the caller rolls back the entries created\n * during this pass and the previous list state survives: the last good render stays\n * on screen. Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n createdThisPass: ItemEntry<T>[],\n itemsOrdered: ItemEntry<T>[],\n): ItemEntry<T>[] => {\n // Phase 1: create every missing entry. This is the only step that can throw\n // (user render code), and it records what it created so a failure is undoable.\n // New entries mount into a detached staging fragment, not the live parent: an\n // entry not already at its final position would otherwise connect at the end,\n // then disconnect again when the ordering pass relocates it (double\n // setup/teardown of any custom elements it contains). Staged nodes connect\n // exactly once, when the ordering pass moves them into place.\n const staging = document.createDocumentFragment();\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n\n if (itemsMap.has(key)) continue;\n\n const entry = untrack(() => createItem(next[i], i, render, staging));\n\n createdThisPass.push(entry);\n itemsMap.set(key, entry);\n }\n\n // Phase 2: ordered list + in-place updates for reused entries (signal writes).\n const nextOrdered: ItemEntry<T>[] = [];\n\n batch(() => {\n for (let i = 0; i < next.length; i++) {\n const entry = itemsMap.get(nextKeys[i]);\n\n invariant(entry, 'each() reconcile phase 2 lost an entry created in phase 1');\n\n entry.data.value = next[i];\n entry.index.value = i;\n nextOrdered.push(entry);\n }\n });\n\n // DOM ordering: one right-to-left pass with a cursor. An entry is in place\n // when its range sentinel directly precedes the cursor. Moving an entry\n // disconnects and reconnects its custom elements, so moves are kept minimal:\n // when reused entries kept their relative order (the common case: prepend,\n // append, interleaved inserts, content replacement), reused ranges are never\n // moved at all and only entries created this pass are relocated next to their\n // nearest reused neighbour. Genuine reorders fall back to moving any range the\n // cursor cannot reach. A move relocates the whole contiguous range up to and\n // including the sentinel so content inserted by directives inside the item\n // travels with it.\n const reusedInOrder = reusedEntriesInOrder(nextOrdered, itemsOrdered);\n const created = new Set<ItemEntry<T>>(createdThisPass);\n\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const rangeEnd = entry.nodes[entry.nodes.length - 1];\n\n // Staged nodes never satisfy the adjacency test (they live in the fragment),\n // so created entries are always moved to their final position.\n if ((!reusedInOrder || created.has(entry)) && rangeEnd !== cursor.previousSibling) {\n let node: Node | null = firstNode;\n\n while (node) {\n const next: Node | null = node === rangeEnd ? null : node.nextSibling;\n\n parent.insertBefore(node, cursor);\n\n if (node === rangeEnd) break;\n node = next;\n }\n }\n\n cursor = firstNode;\n }\n\n // Phase 3: commit: destroy stale entries only now that the pass has succeeded.\n const nextKeySet = new Set(nextKeys);\n\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n return nextOrdered;\n};\n\n/** Undo a failed reconcile pass: destroy entries created during it, restoring the last good list. */\nconst rollbackCreated = <T>(itemsMap: Map<ItemKey, ItemEntry<T>>, created: ItemEntry<T>[]): void => {\n for (const entry of created) {\n removeItem(entry);\n\n for (const [key, candidate] of itemsMap) {\n if (candidate === entry) {\n itemsMap.delete(key);\n break;\n }\n }\n }\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n * Reused items keep their DOM nodes: when the surviving items stay in the same\n * relative order (prepend, append, interleaved inserts, content replacement),\n * no reused node is ever moved, so their CSS transitions, animations, and any\n * nested component state survive the update untouched. Only genuine reorders\n * move nodes.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index: an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function: an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log: listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n // The parent is resolved live, never captured: a nested each()'s anchor and\n // endMarker sit inside an outer each() item's range and move with it, so\n // every insertion must target wherever the endMarker currently lives.\n const container = (): ParentNode | null => endMarker.parentNode;\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const parent = container();\n\n if (!parent) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n const parent = container();\n\n if (!parent) return;\n\n const createdThisPass: ItemEntry<T>[] = [];\n\n try {\n itemsOrdered = untrack(() =>\n reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker, createdThisPass, itemsOrdered),\n );\n } catch (error) {\n // Undo this pass instead of tearing the list down: entries created before the\n // throw are removed, everything that was on screen stays exactly as it was.\n reportEachError(error, anchor);\n untrack(() => rollbackCreated(itemsMap, createdThisPass));\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"each.js","names":["rawEffect","createItem","dataSignal","signal","item","indexSignal","index","scope","createScope","nodes","rangeStart","staging","asCompiled","render","rangeEnd","removeItem","entry","removeNodes","reusedEntriesInOrder","previousPosition","i","itemsOrdered","lastPosition","nextOrdered","position","reconcileItems","next","key","nextKeys","itemsMap","untrack","createdThisPass","batch","invariant","reusedInOrder","created","cursor","endMarker","j","firstNode","node","parent","nextKeySet","rollbackCreated","candidate","collectKeys","keys","seen","keyFn","OreApiError","ORE_ERRORS","reportEachError","cause","error","reportRuntimeError","OreLifecycleError","anchor","each","listSignal","list","computed","createDirectiveResult","initialParent","container","fallbackNodes","fallbackScope","mountFallback","fallback","clearFallback","sub","effect","nextList","onCleanup"],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n onCleanup,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { asCompiled, createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { type MaybeReactive, removeNodes } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = MaybeReactive<T[]>;\ntype ItemKey = number | string;\n\n/**\n * A rendered list item. `nodes` is the item's contiguous sibling range,\n * bracketed by `each/item-start` and `each/item` sentinel comments: the\n * sentinels wrap the template's own nodes, and every reactive directive inside\n * the item (`when()`, nested `each()`, html bindings) inserts and re-inserts\n * its content as siblings relative to nodes between the sentinels — before or\n * after its own anchor, but never past a sentinel. Relocating the item\n * therefore means moving the whole bracketed range, node by node: never a\n * bare subset.\n */\ntype ItemEntry<T> = {\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\n/**\n * Mounts the item into `staging` (a detached fragment), bracketing its node\n * range with the start and end sentinels. Synchronous directive content mounts\n * between the sentinels (the end sentinel only exists once the whole template\n * is mounted), so the range is complete the moment this returns.\n */\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n staging: DocumentFragment,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n let nodes: Node[] = [];\n\n const rangeStart = document.createComment('each/item-start');\n\n staging.appendChild(rangeStart);\n\n scope.run(() => {\n const result = asCompiled(render(dataSignal, indexSignal));\n\n nodes = result.mount(staging, null);\n });\n\n const rangeEnd = document.createComment('each/item');\n\n staging.appendChild(rangeEnd);\n nodes = [rangeStart, ...nodes, rangeEnd];\n\n return { data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * True when reused entries appear in the next list in the same relative order\n * they hold in the DOM (`itemsOrdered`). Prepends, appends, interleaved\n * inserts, and content replacement all satisfy this; only genuine reorders\n * (or removes that flip survivor order) do not.\n */\nconst reusedEntriesInOrder = <T>(nextOrdered: ItemEntry<T>[], itemsOrdered: ItemEntry<T>[]): boolean => {\n const previousPosition = new Map<ItemEntry<T>, number>();\n\n for (let i = 0; i < itemsOrdered.length; i++) previousPosition.set(itemsOrdered[i], i);\n\n let lastPosition = -1;\n\n for (const entry of nextOrdered) {\n const position = previousPosition.get(entry);\n\n if (position === undefined) continue;\n\n if (position < lastPosition) return false;\n\n lastPosition = position;\n }\n\n return true;\n};\n\n/**\n * Reconciles the live item map against the next array. The pass is transactional:\n * new entries are created first, existing entries are updated in-place via signal\n * writes, and stale entries are destroyed only after the whole pass has succeeded.\n * If creation throws (user render code), the caller rolls back the entries created\n * during this pass and the previous list state survives: the last good render stays\n * on screen. Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n createdThisPass: ItemEntry<T>[],\n itemsOrdered: ItemEntry<T>[],\n): ItemEntry<T>[] => {\n // Phase 1: create every missing entry. This is the only step that can throw\n // (user render code), and it records what it created so a failure is undoable.\n // New entries mount into a detached staging fragment, not the live parent: an\n // entry not already at its final position would otherwise connect at the end,\n // then disconnect again when the ordering pass relocates it (double\n // setup/teardown of any custom elements it contains). Staged nodes connect\n // exactly once, when the ordering pass moves them into place.\n const staging = document.createDocumentFragment();\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n\n if (itemsMap.has(key)) continue;\n\n const entry = untrack(() => createItem(next[i], i, render, staging));\n\n createdThisPass.push(entry);\n itemsMap.set(key, entry);\n }\n\n // Phase 2: ordered list + in-place updates for reused entries (signal writes).\n const nextOrdered: ItemEntry<T>[] = [];\n\n batch(() => {\n for (let i = 0; i < next.length; i++) {\n const entry = itemsMap.get(nextKeys[i]);\n\n invariant(entry, 'each() reconcile phase 2 lost an entry created in phase 1');\n\n entry.data.value = next[i];\n entry.index.value = i;\n nextOrdered.push(entry);\n }\n });\n\n // DOM ordering: one right-to-left pass with a cursor. An entry is in place\n // when its range sentinel directly precedes the cursor. Moving an entry\n // disconnects and reconnects its custom elements, so moves are kept minimal:\n // when reused entries kept their relative order (the common case: prepend,\n // append, interleaved inserts, content replacement), reused ranges are never\n // moved at all and only entries created this pass are relocated next to their\n // nearest reused neighbour. Genuine reorders fall back to moving any range the\n // cursor cannot reach. A move relocates the whole contiguous range up to and\n // including the sentinel so content inserted by directives inside the item\n // travels with it.\n const reusedInOrder = reusedEntriesInOrder(nextOrdered, itemsOrdered);\n const created = new Set<ItemEntry<T>>(createdThisPass);\n\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const rangeEnd = entry.nodes[entry.nodes.length - 1];\n\n // Staged nodes never satisfy the adjacency test (they live in the fragment),\n // so created entries are always moved to their final position.\n if ((!reusedInOrder || created.has(entry)) && rangeEnd !== cursor.previousSibling) {\n let node: Node | null = firstNode;\n\n while (node) {\n const next: Node | null = node === rangeEnd ? null : node.nextSibling;\n\n parent.insertBefore(node, cursor);\n\n if (node === rangeEnd) break;\n node = next;\n }\n }\n\n cursor = firstNode;\n }\n\n // Phase 3: commit: destroy stale entries only now that the pass has succeeded.\n const nextKeySet = new Set(nextKeys);\n\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n return nextOrdered;\n};\n\n/** Undo a failed reconcile pass: destroy entries created during it, restoring the last good list. */\nconst rollbackCreated = <T>(itemsMap: Map<ItemKey, ItemEntry<T>>, created: ItemEntry<T>[]): void => {\n for (const entry of created) {\n removeItem(entry);\n\n for (const [key, candidate] of itemsMap) {\n if (candidate === entry) {\n itemsMap.delete(key);\n break;\n }\n }\n }\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n * Reused items keep their DOM nodes: when the surviving items stay in the same\n * relative order (prepend, append, interleaved inserts, content replacement),\n * no reused node is ever moved, so their CSS transitions, animations, and any\n * nested component state survive the update untouched. Only genuine reorders\n * move nodes.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index: an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function: an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log: listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor) => {\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n // The parent is resolved live, never captured: a nested each()'s anchor and\n // endMarker sit inside an outer each() item's range and move with it, so\n // every insertion must target wherever the endMarker currently lives.\n const container = (): ParentNode | null => endMarker.parentNode;\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackScope: Scope | null = null;\n\n // The fallback outlives the effect run that mounts it (an empty -> empty update\n // keeps it on screen), so it gets its own scope instead of the per-run owner.\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const parent = container();\n\n if (!parent) return;\n\n const scope = createScope();\n\n fallbackScope = scope;\n try {\n scope.run(() => {\n const result = asCompiled(fallback());\n\n fallbackNodes = result.mount(parent, endMarker);\n });\n } catch (error) {\n // Never leave a failed fallback scope unowned: the next empty-list run\n // would create another and overwrite the reference, leaking the first.\n clearFallback();\n throw error;\n }\n };\n\n const clearFallback = (): void => {\n fallbackScope?.dispose();\n fallbackScope = null;\n\n if (fallbackNodes) {\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n const parent = container();\n\n if (!parent) return;\n\n const createdThisPass: ItemEntry<T>[] = [];\n\n try {\n itemsOrdered = untrack(() =>\n reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker, createdThisPass, itemsOrdered),\n );\n } catch (error) {\n // Undo this pass instead of tearing the list down: entries created before the\n // throw are removed, everything that was on screen stays exactly as it was.\n reportEachError(error, anchor);\n untrack(() => rollbackCreated(itemsMap, createdThisPass));\n }\n });\n\n onCleanup(() => {\n sub.dispose();\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"model.cjs","names":["makeBrand","modelBrand","isModelSource","modelDefaultGet","el","modelDefaultEvent","model","options"],"sources":["../../src/directives/model.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\nimport { makeBrand } from '../utils/brand';\n\n/**\n * Options for `model()`. Every form control reads and writes differently; the\n * defaults cover native inputs, and `get`/`event` override them for anything else.\n */\nexport type ModelOptions<T = unknown> = {\n /**\n * Event that triggers the write-back from the control to the signal.\n * Defaults to `'change'` for checkboxes, radios, and `<select>`, and\n * `'input'` for everything else.\n */\n event?: string;\n /**\n * Reads the current value out of the element on each write-back event.\n * Defaults to `el.checked` for checkboxes/radios and `el.value` otherwise\n * (always a string for native text/number inputs: pass a `get` like\n * `(el) => Number(el.value)` to keep a numeric signal numeric).\n */\n get?: (el: HTMLElement) => T;\n};\n\n/**\n * Per-binding marker produced by `model()`. Like `live()`, the two-way behavior\n * belongs to this one binding site, never to the signal itself.\n *\n * @internal Consumed by the template binding layer (see `template/instantiator.ts`).\n */\nexport type ModelSource<T = unknown> = {\n readonly event?: string;\n readonly get?: (el: HTMLElement) => T;\n readonly source: Signal<T>;\n};\n\nconst modelBrand = makeBrand<ModelSource>('ore:model');\n\nexport const isModelSource = modelBrand.is;\n\n/** @internal Default value reader for `model()` (see template/instantiator.ts). */\nexport const modelDefaultGet = (el: HTMLElement): unknown => {\n if (el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')) return el.checked;\n\n return (el as HTMLInputElement).value;\n};\n\n/** @internal Default write-back event for `model()` (see template/instantiator.ts). */\nexport const modelDefaultEvent = (el: HTMLElement): string =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')\n ? 'change'\n : el instanceof HTMLSelectElement\n ? 'change'\n : 'input';\n\n/**\n * Two-way binding for a form control: `value=\"${model(signal)}\"`.\n *\n * Writes the signal to the control's `value`/`checked` property on every change,\n * and writes user input back into the signal on the control's change event\n * (`'change'` for checkboxes/radios/selects, `'input'` otherwise). This is the\n * complete controlled-input loop: the `live()` + manual `@input` handler pair it\n * replaces was easy to get half-wired.\n *\n * The signal is written with whatever `get` reads back (a string by default for\n * text inputs), so declare the signal with the matching type or pass `get`.\n *\n * @example\n * ```ts\n * const text = signal('');\n * const agree = signal(false);\n *\n * html`\n * <input value=\"${model(text)}\" />\n * <input type=\"checkbox\" checked=\"${model(agree)}\" />\n * <input type=\"number\" value=\"${model(count, { get: (el) => Number(el.value) })}\" />\n * `\n * ```\n */\nexport const model = <T>(source: Signal<T>, options?: ModelOptions<T>): ModelSource<T> =>\n modelBrand.stamp({ event: options?.event, get: options?.get, source }) as ModelSource<T>;\n"],"mappings":"AAmCA,IAAMC,E,4BAAaD,CAAAA,CAAAA,UAAuB,WAAW,EAExCE,EAAgBD,EAAW,GAG3BE,EAAmB,GAC1BC,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,SAAiBA,EAAG,QAEzFA,EAAwB,MAIrBC,EAAqB,GAChCD,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,UAErEA,aAAc,kBADd,SAGE,QA0BKE,GAAY,EAAmB,IAC1CL,EAAW,MAAM,CAAE,MAAOM,GAAS,MAAO,IAAKA,GAAS,IAAK,QAAO,CAAC,E"}
1
+ {"version":3,"file":"model.cjs","names":["makeBrand","modelBrand","isModelSource","modelDefaultGet","el","modelDefaultEvent","model","options"],"sources":["../../src/directives/model.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\nimport { makeBrand } from '../utils/brand';\n\n/**\n * Options for `model()`. Every form control reads and writes differently; the\n * defaults cover native inputs, and `get`/`event` override them for anything else.\n */\nexport type ModelOptions<T = unknown> = {\n /**\n * Event that triggers the write-back from the control to the signal.\n * Defaults to `'change'` for checkboxes, radios, and `<select>`, and\n * `'input'` for everything else.\n */\n event?: string;\n /**\n * Reads the current value out of the element on each write-back event.\n * Defaults to `el.checked` for checkboxes/radios and `el.value` otherwise\n * (always a string for native text/number inputs: pass a `get` like\n * `(el) => Number(el.value)` to keep a numeric signal numeric).\n */\n get?: (el: HTMLElement) => T;\n};\n\n/**\n * Per-binding marker produced by `model()`. The two-way behavior belongs to this\n * one binding site, never to the signal itself.\n *\n * @internal Consumed by the template binding layer (see `template/instantiator.ts`).\n */\nexport type ModelSource<T = unknown> = {\n readonly event?: string;\n readonly get?: (el: HTMLElement) => T;\n readonly source: Signal<T>;\n};\n\nconst modelBrand = makeBrand<ModelSource>('ore:model');\n\nexport const isModelSource = modelBrand.is;\n\n/** @internal Default value reader for `model()` (see template/instantiator.ts). */\nexport const modelDefaultGet = (el: HTMLElement): unknown => {\n if (el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')) return el.checked;\n\n return (el as HTMLInputElement).value;\n};\n\n/** @internal Default write-back event for `model()` (see template/instantiator.ts). */\nexport const modelDefaultEvent = (el: HTMLElement): string =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')\n ? 'change'\n : el instanceof HTMLSelectElement\n ? 'change'\n : 'input';\n\n/**\n * Two-way binding for a form control: `value=\"${model(signal)}\"`.\n *\n * Writes the signal to the control's `value`/`checked` property on every change,\n * and writes user input back into the signal on the control's change event\n * (`'change'` for checkboxes/radios/selects, `'input'` otherwise). Writes are skipped\n * while the control holds an in-progress user edit the signal has not seen yet.\n *\n * The signal is written with whatever `get` reads back (a string by default for\n * text inputs), so declare the signal with the matching type or pass `get`.\n *\n * @example\n * ```ts\n * const text = signal('');\n * const agree = signal(false);\n *\n * html`\n * <input value=\"${model(text)}\" />\n * <input type=\"checkbox\" checked=\"${model(agree)}\" />\n * <input type=\"number\" value=\"${model(count, { get: (el) => Number(el.value) })}\" />\n * `\n * ```\n */\nexport const model = <T>(source: Signal<T>, options?: ModelOptions<T>): ModelSource<T> =>\n modelBrand.stamp({ event: options?.event, get: options?.get, source }) as ModelSource<T>;\n"],"mappings":"AAmCA,IAAMC,E,4BAAaD,CAAAA,CAAAA,UAAuB,WAAW,EAExCE,EAAgBD,EAAW,GAG3BE,EAAmB,GAC1BC,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,SAAiBA,EAAG,QAEzFA,EAAwB,MAIrBC,EAAqB,GAChCD,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,UAErEA,aAAc,kBADd,SAGE,QAyBKE,GAAY,EAAmB,IAC1CL,EAAW,MAAM,CAAE,MAAOM,GAAS,MAAO,IAAKA,GAAS,IAAK,QAAO,CAAC,E"}
@@ -24,9 +24,8 @@ export declare const isModelSource: (value: unknown) => value is ModelSource<unk
24
24
  *
25
25
  * Writes the signal to the control's `value`/`checked` property on every change,
26
26
  * and writes user input back into the signal on the control's change event
27
- * (`'change'` for checkboxes/radios/selects, `'input'` otherwise). This is the
28
- * complete controlled-input loop: the `live()` + manual `@input` handler pair it
29
- * replaces was easy to get half-wired.
27
+ * (`'change'` for checkboxes/radios/selects, `'input'` otherwise). Writes are skipped
28
+ * while the control holds an in-progress user edit the signal has not seen yet.
30
29
  *
31
30
  * The signal is written with whatever `get` reads back (a string by default for
32
31
  * text inputs), so declare the signal with the matching type or pass `get`.
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/directives/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG/C;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,CAAC,CAAC;CAC9B,CAAC;AAgBF,eAAO,MAAM,aAAa,mDAAgB,CAAC;AAiB3C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,YAAY,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CACK,CAAC"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/directives/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG/C;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,CAAC,CAAC;CAC9B,CAAC;AAgBF,eAAO,MAAM,aAAa,mDAAgB,CAAC;AAiB3C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,YAAY,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CACK,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","names":["modelBrand","makeBrand","isModelSource","modelDefaultGet","el","modelDefaultEvent","model","options"],"sources":["../../src/directives/model.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\nimport { makeBrand } from '../utils/brand';\n\n/**\n * Options for `model()`. Every form control reads and writes differently; the\n * defaults cover native inputs, and `get`/`event` override them for anything else.\n */\nexport type ModelOptions<T = unknown> = {\n /**\n * Event that triggers the write-back from the control to the signal.\n * Defaults to `'change'` for checkboxes, radios, and `<select>`, and\n * `'input'` for everything else.\n */\n event?: string;\n /**\n * Reads the current value out of the element on each write-back event.\n * Defaults to `el.checked` for checkboxes/radios and `el.value` otherwise\n * (always a string for native text/number inputs: pass a `get` like\n * `(el) => Number(el.value)` to keep a numeric signal numeric).\n */\n get?: (el: HTMLElement) => T;\n};\n\n/**\n * Per-binding marker produced by `model()`. Like `live()`, the two-way behavior\n * belongs to this one binding site, never to the signal itself.\n *\n * @internal Consumed by the template binding layer (see `template/instantiator.ts`).\n */\nexport type ModelSource<T = unknown> = {\n readonly event?: string;\n readonly get?: (el: HTMLElement) => T;\n readonly source: Signal<T>;\n};\n\nconst modelBrand = makeBrand<ModelSource>('ore:model');\n\nexport const isModelSource = modelBrand.is;\n\n/** @internal Default value reader for `model()` (see template/instantiator.ts). */\nexport const modelDefaultGet = (el: HTMLElement): unknown => {\n if (el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')) return el.checked;\n\n return (el as HTMLInputElement).value;\n};\n\n/** @internal Default write-back event for `model()` (see template/instantiator.ts). */\nexport const modelDefaultEvent = (el: HTMLElement): string =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')\n ? 'change'\n : el instanceof HTMLSelectElement\n ? 'change'\n : 'input';\n\n/**\n * Two-way binding for a form control: `value=\"${model(signal)}\"`.\n *\n * Writes the signal to the control's `value`/`checked` property on every change,\n * and writes user input back into the signal on the control's change event\n * (`'change'` for checkboxes/radios/selects, `'input'` otherwise). This is the\n * complete controlled-input loop: the `live()` + manual `@input` handler pair it\n * replaces was easy to get half-wired.\n *\n * The signal is written with whatever `get` reads back (a string by default for\n * text inputs), so declare the signal with the matching type or pass `get`.\n *\n * @example\n * ```ts\n * const text = signal('');\n * const agree = signal(false);\n *\n * html`\n * <input value=\"${model(text)}\" />\n * <input type=\"checkbox\" checked=\"${model(agree)}\" />\n * <input type=\"number\" value=\"${model(count, { get: (el) => Number(el.value) })}\" />\n * `\n * ```\n */\nexport const model = <T>(source: Signal<T>, options?: ModelOptions<T>): ModelSource<T> =>\n modelBrand.stamp({ event: options?.event, get: options?.get, source }) as ModelSource<T>;\n"],"mappings":"A,8CAmCA,IAAMA,EAAaC,EAAuB,WAAW,EAExCC,EAAgBF,EAAW,GAG3BG,EAAmB,GAC1BC,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,SAAiBA,EAAG,QAEzFA,EAAwB,MAIrBC,EAAqB,GAChCD,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,UAErEA,aAAc,kBADd,SAGE,QA0BKE,GAAY,EAAmB,IAC1CN,EAAW,MAAM,CAAE,MAAOO,GAAS,MAAO,IAAKA,GAAS,IAAK,QAAO,CAAC,E"}
1
+ {"version":3,"file":"model.js","names":["modelBrand","makeBrand","isModelSource","modelDefaultGet","el","modelDefaultEvent","model","options"],"sources":["../../src/directives/model.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\nimport { makeBrand } from '../utils/brand';\n\n/**\n * Options for `model()`. Every form control reads and writes differently; the\n * defaults cover native inputs, and `get`/`event` override them for anything else.\n */\nexport type ModelOptions<T = unknown> = {\n /**\n * Event that triggers the write-back from the control to the signal.\n * Defaults to `'change'` for checkboxes, radios, and `<select>`, and\n * `'input'` for everything else.\n */\n event?: string;\n /**\n * Reads the current value out of the element on each write-back event.\n * Defaults to `el.checked` for checkboxes/radios and `el.value` otherwise\n * (always a string for native text/number inputs: pass a `get` like\n * `(el) => Number(el.value)` to keep a numeric signal numeric).\n */\n get?: (el: HTMLElement) => T;\n};\n\n/**\n * Per-binding marker produced by `model()`. The two-way behavior belongs to this\n * one binding site, never to the signal itself.\n *\n * @internal Consumed by the template binding layer (see `template/instantiator.ts`).\n */\nexport type ModelSource<T = unknown> = {\n readonly event?: string;\n readonly get?: (el: HTMLElement) => T;\n readonly source: Signal<T>;\n};\n\nconst modelBrand = makeBrand<ModelSource>('ore:model');\n\nexport const isModelSource = modelBrand.is;\n\n/** @internal Default value reader for `model()` (see template/instantiator.ts). */\nexport const modelDefaultGet = (el: HTMLElement): unknown => {\n if (el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')) return el.checked;\n\n return (el as HTMLInputElement).value;\n};\n\n/** @internal Default write-back event for `model()` (see template/instantiator.ts). */\nexport const modelDefaultEvent = (el: HTMLElement): string =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')\n ? 'change'\n : el instanceof HTMLSelectElement\n ? 'change'\n : 'input';\n\n/**\n * Two-way binding for a form control: `value=\"${model(signal)}\"`.\n *\n * Writes the signal to the control's `value`/`checked` property on every change,\n * and writes user input back into the signal on the control's change event\n * (`'change'` for checkboxes/radios/selects, `'input'` otherwise). Writes are skipped\n * while the control holds an in-progress user edit the signal has not seen yet.\n *\n * The signal is written with whatever `get` reads back (a string by default for\n * text inputs), so declare the signal with the matching type or pass `get`.\n *\n * @example\n * ```ts\n * const text = signal('');\n * const agree = signal(false);\n *\n * html`\n * <input value=\"${model(text)}\" />\n * <input type=\"checkbox\" checked=\"${model(agree)}\" />\n * <input type=\"number\" value=\"${model(count, { get: (el) => Number(el.value) })}\" />\n * `\n * ```\n */\nexport const model = <T>(source: Signal<T>, options?: ModelOptions<T>): ModelSource<T> =>\n modelBrand.stamp({ event: options?.event, get: options?.get, source }) as ModelSource<T>;\n"],"mappings":"A,8CAmCA,IAAMA,EAAaC,EAAuB,WAAW,EAExCC,EAAgBF,EAAW,GAG3BG,EAAmB,GAC1BC,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,SAAiBA,EAAG,QAEzFA,EAAwB,MAIrBC,EAAqB,GAChCD,aAAc,mBAAqBA,EAAG,OAAS,YAAcA,EAAG,OAAS,UAErEA,aAAc,kBADd,SAGE,QAyBKE,GAAY,EAAmB,IAC1CN,EAAW,MAAM,CAAE,MAAOO,GAAS,MAAO,IAAKA,GAAS,IAAK,QAAO,CAAC,E"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("../template/result.cjs"),n=require("../utils/dom.cjs");let r=require("@vielzeug/ripple");var i=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function a(a){let o=typeof a==`function`?(0,r.computed)(a):(0,r.isReactive)(a)?a:null;return t.createDirectiveResult((t,s)=>{let c=t.parentNode;e.invariant(c,`unsafeHtml() anchor comment has no parent node`);let l=document.createComment(`unsafe-html/end`);if(c.insertBefore(l,t.nextSibling),o){let e=n.createReplaceableSlot(),t=(0,r.effect)(()=>{e.clear();let t=l.parentNode;t&&e.setNodes(i(o.value,t,l))});s(()=>t.dispose()),s(()=>{e.clear(),l.remove()})}else i(a,c,l),s(()=>l.remove())})}exports.unsafeHtml=a;
1
+ const e=require("../errors.cjs"),t=require("../template/result.cjs"),n=require("../utils/dom.cjs");let r=require("@vielzeug/ripple");var i=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function a(a){let o=typeof a==`function`?(0,r.computed)(a):(0,r.isReactive)(a)?a:null;return t.createDirectiveResult(t=>{let s=t.parentNode;e.invariant(s,`unsafeHtml() anchor comment has no parent node`);let c=document.createComment(`unsafe-html/end`);if(s.insertBefore(c,t.nextSibling),o){let e=n.createReplaceableSlot(),t=(0,r.effect)(()=>{let t=c.parentNode;t?e.render(()=>{e.setNodes(i(o.value,t,c))}):e.clear()});(0,r.onCleanup)(()=>{t.dispose(),e.clear(),c.remove()})}else i(a,s,c),(0,r.onCleanup)(()=>c.remove())})}exports.unsafeHtml=a;
2
2
  //# sourceMappingURL=unsafe-html.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"unsafe-html.cjs","names":["computed","isReactive","createDirectiveResult","createReplaceableSlot","rawEffect","require_errors","require_result","require_dom","_vielzeug_ripple","parseHtml","tpl","html","nodes","node","parent","insertBefore","unsafeHtml","src","value","initialParent","anchor","endMarker","slot","stop","registerCleanup"],"sources":["../../src/directives/unsafe-html.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, type Signal } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult } from '../template/result';\nimport { createReplaceableSlot } from '../utils/dom';\n\nconst parseHtml = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders HTML without escaping as a DirectiveResult.\n *\n * This is intentionally named `unsafeHtml`: sanitization is an application\n * boundary, not mutable process-wide framework configuration. Sanitize\n * untrusted content before passing it here.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function unsafeHtml(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n const isFn = typeof value === 'function';\n const src: Readable<string> | null = isFn ? computed(value as () => string) : isReactive(value) ? value : null;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, 'unsafeHtml() anchor comment has no parent node');\n\n const endMarker = document.createComment('unsafe-html/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n if (src) {\n const slot = createReplaceableSlot();\n\n const stop = rawEffect(() => {\n slot.clear();\n\n // Resolved live: an unsafeHtml() inside an each() item moves with the\n // item's range; the mount-time parent would be stale after a reorder.\n const parent = endMarker.parentNode;\n\n if (!parent) return;\n\n slot.setNodes(parseHtml(src.value, parent, endMarker));\n });\n\n registerCleanup(() => stop.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n } else {\n parseHtml(value as string, initialParent, endMarker);\n\n registerCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"A,qIAMA,IAAMS,GAAa,EAAc,EAAoB,IAA+B,CAClF,IAAMC,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAYC,EAEhB,IAAMC,EAAQ,MAAM,KAAKF,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAMG,KAAQD,EAAOE,EAAO,aAAaD,EAAME,CAAY,EAEhE,OAAOH,CACT,EAYA,SAAgBI,EAAW,EAAqF,CAE9G,IAAMC,EADO,OAAOC,GAAU,YAAA,EACclB,EAAAA,SAAAA,CAASkB,CAAqB,GAAA,EAAIjB,EAAAA,WAAAA,CAAWiB,CAAK,EAAIA,EAAQ,KAE1G,OAAOhB,EAAAA,uBAAuB,EAAQ,IAAoB,CACxD,IAAMiB,EAAgBC,EAAO,WAE7Bf,EAAA,UAAUc,EAAe,gDAAgD,EAEzE,IAAME,EAAY,SAAS,cAAc,iBAAiB,EAI1D,GAFAF,EAAc,aAAaE,EAAWD,EAAO,WAAW,EAEpDH,EAAK,CACP,IAAMK,EAAOnB,EAAAA,sBAAsB,EAE7BoB,GAAA,EAAOnB,EAAAA,OAAAA,KAAgB,CAC3BkB,EAAK,MAAM,EAIX,IAAMR,EAASO,EAAU,WAEpBP,GAELQ,EAAK,SAASb,EAAUQ,EAAI,MAAOH,EAAQO,CAAS,CAAC,CACvD,CAAC,EAEDG,MAAsBD,EAAK,QAAQ,CAAC,EACpCC,MAAsB,CACpBF,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,MACEZ,EAAUS,EAAiBC,EAAeE,CAAS,EAEnDG,MAAsBH,EAAU,OAAO,CAAC,CAE5C,CAAC,CACH,C"}
1
+ {"version":3,"file":"unsafe-html.cjs","names":["computed","isReactive","createDirectiveResult","createReplaceableSlot","rawEffect","require_errors","require_result","require_dom","_vielzeug_ripple","parseHtml","tpl","html","nodes","node","parent","insertBefore","unsafeHtml","src","value","initialParent","anchor","endMarker","slot","stop"],"sources":["../../src/directives/unsafe-html.ts"],"sourcesContent":["import { computed, isReactive, onCleanup, type Readable, effect as rawEffect, type Signal } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult } from '../template/result';\nimport { createReplaceableSlot } from '../utils/dom';\n\nconst parseHtml = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders HTML without escaping as a DirectiveResult.\n *\n * This is intentionally named `unsafeHtml`: sanitization is an application\n * boundary, not mutable process-wide framework configuration. Sanitize\n * untrusted content before passing it here.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function unsafeHtml(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n const isFn = typeof value === 'function';\n const src: Readable<string> | null = isFn ? computed(value as () => string) : isReactive(value) ? value : null;\n\n return createDirectiveResult((anchor) => {\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, 'unsafeHtml() anchor comment has no parent node');\n\n const endMarker = document.createComment('unsafe-html/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n if (src) {\n const slot = createReplaceableSlot();\n\n const stop = rawEffect(() => {\n // Resolved live: an unsafeHtml() inside an each() item moves with the\n // item's range; the mount-time parent would be stale after a reorder.\n const parent = endMarker.parentNode;\n\n if (!parent) {\n slot.clear();\n\n return;\n }\n\n slot.render(() => {\n slot.setNodes(parseHtml(src.value, parent, endMarker));\n });\n });\n\n onCleanup(() => {\n stop.dispose();\n slot.clear();\n endMarker.remove();\n });\n } else {\n parseHtml(value as string, initialParent, endMarker);\n\n onCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"A,qIAMA,IAAMS,GAAa,EAAc,EAAoB,IAA+B,CAClF,IAAMC,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAYC,EAEhB,IAAMC,EAAQ,MAAM,KAAKF,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAMG,KAAQD,EAAOE,EAAO,aAAaD,EAAME,CAAY,EAEhE,OAAOH,CACT,EAYA,SAAgBI,EAAW,EAAqF,CAE9G,IAAMC,EADO,OAAOC,GAAU,YAAA,EACclB,EAAAA,SAAAA,CAASkB,CAAqB,GAAA,EAAIjB,EAAAA,WAAAA,CAAWiB,CAAK,EAAIA,EAAQ,KAE1G,OAAOhB,EAAAA,sBAAuB,GAAW,CACvC,IAAMiB,EAAgBC,EAAO,WAE7Bf,EAAA,UAAUc,EAAe,gDAAgD,EAEzE,IAAME,EAAY,SAAS,cAAc,iBAAiB,EAI1D,GAFAF,EAAc,aAAaE,EAAWD,EAAO,WAAW,EAEpDH,EAAK,CACP,IAAMK,EAAOnB,EAAAA,sBAAsB,EAE7BoB,GAAA,EAAOnB,EAAAA,OAAAA,KAAgB,CAG3B,IAAMU,EAASO,EAAU,WAEpBP,EAMLQ,EAAK,WAAa,CAChBA,EAAK,SAASb,EAAUQ,EAAI,MAAOH,EAAQO,CAAS,CAAC,CACvD,CAAC,EAPCC,EAAK,MAAM,CAQf,CAAC,GAED,EAAAd,EAAA,UAAA,KAAgB,CACde,EAAK,QAAQ,EACbD,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,MACEZ,EAAUS,EAAiBC,EAAeE,CAAS,GAEnD,EAAAb,EAAA,UAAA,KAAgBa,EAAU,OAAO,CAAC,CAEtC,CAAC,CACH,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"unsafe-html.d.ts","sourceRoot":"","sources":["../../src/directives/unsafe-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,QAAQ,EAAuB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGzG,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAejF;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,eAAe,CAuC9G"}
1
+ {"version":3,"file":"unsafe-html.d.ts","sourceRoot":"","sources":["../../src/directives/unsafe-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,QAAQ,EAAuB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGpH,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAejF;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,eAAe,CA2C9G"}
@@ -1,2 +1,2 @@
1
- import{invariant as e}from"../errors.js";import{createDirectiveResult as t}from"../template/result.js";import{createReplaceableSlot as n}from"../utils/dom.js";import{computed as r,effect as i,isReactive as a}from"@vielzeug/ripple";var o=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function s(s){let c=typeof s==`function`?r(s):a(s)?s:null;return t((t,r)=>{let a=t.parentNode;e(a,`unsafeHtml() anchor comment has no parent node`);let l=document.createComment(`unsafe-html/end`);if(a.insertBefore(l,t.nextSibling),c){let e=n(),t=i(()=>{e.clear();let t=l.parentNode;t&&e.setNodes(o(c.value,t,l))});r(()=>t.dispose()),r(()=>{e.clear(),l.remove()})}else o(s,a,l),r(()=>l.remove())})}export{s as unsafeHtml};
1
+ import{invariant as e}from"../errors.js";import{createDirectiveResult as t}from"../template/result.js";import{createReplaceableSlot as n}from"../utils/dom.js";import{computed as r,effect as i,isReactive as a,onCleanup as o}from"@vielzeug/ripple";var s=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function c(c){let l=typeof c==`function`?r(c):a(c)?c:null;return t(t=>{let r=t.parentNode;e(r,`unsafeHtml() anchor comment has no parent node`);let a=document.createComment(`unsafe-html/end`);if(r.insertBefore(a,t.nextSibling),l){let e=n(),t=i(()=>{let t=a.parentNode;t?e.render(()=>{e.setNodes(s(l.value,t,a))}):e.clear()});o(()=>{t.dispose(),e.clear(),a.remove()})}else s(c,r,a),o(()=>a.remove())})}export{c as unsafeHtml};
2
2
  //# sourceMappingURL=unsafe-html.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"unsafe-html.js","names":["rawEffect","parseHtml","tpl","html","nodes","node","parent","insertBefore","unsafeHtml","src","value","computed","isReactive","createDirectiveResult","initialParent","anchor","invariant","endMarker","slot","createReplaceableSlot","stop","effect","registerCleanup"],"sources":["../../src/directives/unsafe-html.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, type Signal } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult } from '../template/result';\nimport { createReplaceableSlot } from '../utils/dom';\n\nconst parseHtml = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders HTML without escaping as a DirectiveResult.\n *\n * This is intentionally named `unsafeHtml`: sanitization is an application\n * boundary, not mutable process-wide framework configuration. Sanitize\n * untrusted content before passing it here.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function unsafeHtml(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n const isFn = typeof value === 'function';\n const src: Readable<string> | null = isFn ? computed(value as () => string) : isReactive(value) ? value : null;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, 'unsafeHtml() anchor comment has no parent node');\n\n const endMarker = document.createComment('unsafe-html/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n if (src) {\n const slot = createReplaceableSlot();\n\n const stop = rawEffect(() => {\n slot.clear();\n\n // Resolved live: an unsafeHtml() inside an each() item moves with the\n // item's range; the mount-time parent would be stale after a reorder.\n const parent = endMarker.parentNode;\n\n if (!parent) return;\n\n slot.setNodes(parseHtml(src.value, parent, endMarker));\n });\n\n registerCleanup(() => stop.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n } else {\n parseHtml(value as string, initialParent, endMarker);\n\n registerCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"A,uOAMA,IAAMC,GAAa,EAAc,EAAoB,IAA+B,CAClF,IAAMC,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAYC,EAEhB,IAAMC,EAAQ,MAAM,KAAKF,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAMG,KAAQD,EAAOE,EAAO,aAAaD,EAAME,CAAY,EAEhE,OAAOH,CACT,EAYA,SAAgBI,EAAW,EAAqF,CAE9G,IAAMC,EADO,OAAOC,GAAU,WACcC,EAASD,CAAqB,EAAIE,EAAWF,CAAK,EAAIA,EAAQ,KAE1G,OAAOG,GAAuB,EAAQ,IAAoB,CACxD,IAAMC,EAAgBC,EAAO,WAE7BC,EAAUF,EAAe,gDAAgD,EAEzE,IAAMG,EAAY,SAAS,cAAc,iBAAiB,EAI1D,GAFAH,EAAc,aAAaG,EAAWF,EAAO,WAAW,EAEpDN,EAAK,CACP,IAAMS,EAAOC,EAAsB,EAE7BC,EAAOpB,MAAgB,CAC3BkB,EAAK,MAAM,EAIX,IAAMZ,EAASW,EAAU,WAEpBX,GAELY,EAAK,SAASjB,EAAUQ,EAAI,MAAOH,EAAQW,CAAS,CAAC,CACvD,CAAC,EAEDK,MAAsBF,EAAK,QAAQ,CAAC,EACpCE,MAAsB,CACpBJ,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,MACEhB,EAAUS,EAAiBI,EAAeG,CAAS,EAEnDK,MAAsBL,EAAU,OAAO,CAAC,CAE5C,CAAC,CACH,C"}
1
+ {"version":3,"file":"unsafe-html.js","names":["rawEffect","parseHtml","tpl","html","nodes","node","parent","insertBefore","unsafeHtml","src","value","computed","isReactive","createDirectiveResult","initialParent","anchor","invariant","endMarker","slot","createReplaceableSlot","stop","effect","onCleanup"],"sources":["../../src/directives/unsafe-html.ts"],"sourcesContent":["import { computed, isReactive, onCleanup, type Readable, effect as rawEffect, type Signal } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult } from '../template/result';\nimport { createReplaceableSlot } from '../utils/dom';\n\nconst parseHtml = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders HTML without escaping as a DirectiveResult.\n *\n * This is intentionally named `unsafeHtml`: sanitization is an application\n * boundary, not mutable process-wide framework configuration. Sanitize\n * untrusted content before passing it here.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function unsafeHtml(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n const isFn = typeof value === 'function';\n const src: Readable<string> | null = isFn ? computed(value as () => string) : isReactive(value) ? value : null;\n\n return createDirectiveResult((anchor) => {\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, 'unsafeHtml() anchor comment has no parent node');\n\n const endMarker = document.createComment('unsafe-html/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n if (src) {\n const slot = createReplaceableSlot();\n\n const stop = rawEffect(() => {\n // Resolved live: an unsafeHtml() inside an each() item moves with the\n // item's range; the mount-time parent would be stale after a reorder.\n const parent = endMarker.parentNode;\n\n if (!parent) {\n slot.clear();\n\n return;\n }\n\n slot.render(() => {\n slot.setNodes(parseHtml(src.value, parent, endMarker));\n });\n });\n\n onCleanup(() => {\n stop.dispose();\n slot.clear();\n endMarker.remove();\n });\n } else {\n parseHtml(value as string, initialParent, endMarker);\n\n onCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"A,sPAMA,IAAMC,GAAa,EAAc,EAAoB,IAA+B,CAClF,IAAMC,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAYC,EAEhB,IAAMC,EAAQ,MAAM,KAAKF,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAMG,KAAQD,EAAOE,EAAO,aAAaD,EAAME,CAAY,EAEhE,OAAOH,CACT,EAYA,SAAgBI,EAAW,EAAqF,CAE9G,IAAMC,EADO,OAAOC,GAAU,WACcC,EAASD,CAAqB,EAAIE,EAAWF,CAAK,EAAIA,EAAQ,KAE1G,OAAOG,EAAuB,GAAW,CACvC,IAAMC,EAAgBC,EAAO,WAE7BC,EAAUF,EAAe,gDAAgD,EAEzE,IAAMG,EAAY,SAAS,cAAc,iBAAiB,EAI1D,GAFAH,EAAc,aAAaG,EAAWF,EAAO,WAAW,EAEpDN,EAAK,CACP,IAAMS,EAAOC,EAAsB,EAE7BC,EAAOpB,MAAgB,CAG3B,IAAMM,EAASW,EAAU,WAEpBX,EAMLY,EAAK,WAAa,CAChBA,EAAK,SAASjB,EAAUQ,EAAI,MAAOH,EAAQW,CAAS,CAAC,CACvD,CAAC,EAPCC,EAAK,MAAM,CAQf,CAAC,EAEDI,MAAgB,CACdF,EAAK,QAAQ,EACbF,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,MACEhB,EAAUS,EAAiBI,EAAeG,CAAS,EAEnDK,MAAgBL,EAAU,OAAO,CAAC,CAEtC,CAAC,CACH,C"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("../template/result.cjs"),n=require("../utils/dom.cjs");let r=require("@vielzeug/ripple");var i=`when() anchor comment has no parent node`;function a(a,o,s){return typeof a!=`function`&&!(0,r.isReactive)(a)?t.createDirectiveResult((r,c)=>{let l=a?o():s?s():null;if(!l||!t.isHtmlResult(l))return;let u=r.parentNode;e.invariant(u,i);let d=l.mount(u,r,c);c(()=>n.removeNodes(d))}):t.createDirectiveResult((c,l)=>{let u=(typeof a==`function`?(0,r.computed)(a):null)??a,d=c.parentNode;e.invariant(d,i);let f=document.createComment(`when/end`);d.insertBefore(f,c.nextSibling);let p=n.createReplaceableSlot(),m=(0,r.effect)(()=>{let e=u.value;p.clear();let n=e?o():s?s():null;if(!n||!t.isHtmlResult(n))return;let i=f.parentNode;i&&p.setNodes((0,r.untrack)(()=>n.mount(i,f,p.registerCleanup)))});l(()=>m.dispose()),l(()=>{p.clear(),f.remove()})})}exports.when=a;
1
+ const e=require("../errors.cjs"),t=require("../template/result.cjs"),n=require("../utils/dom.cjs");let r=require("@vielzeug/ripple");var i=`when() anchor comment has no parent node`;function a(a,o,s){return typeof a!=`function`&&!(0,r.isReactive)(a)?t.createDirectiveResult(c=>{let l=a?o():s?s():null;if(!l||!t.isHtmlResult(l))return;let u=c.parentNode;e.invariant(u,i);let d=l.mount(u,c);(0,r.onCleanup)(()=>n.removeNodes(d))}):t.createDirectiveResult(c=>{let l=(typeof a==`function`?(0,r.computed)(a):null)??a,u=c.parentNode;e.invariant(u,i);let d=document.createComment(`when/end`);u.insertBefore(d,c.nextSibling);let f=n.createReplaceableSlot(),p=(0,r.effect)(()=>{let e=l.value?o():s?s():null;if(!e||!t.isHtmlResult(e)){f.clear();return}let n=d.parentNode;n?(0,r.untrack)(()=>{f.render(()=>{f.setNodes(e.mount(n,d))})}):f.clear()});(0,r.onCleanup)(()=>{p.dispose(),f.clear(),d.remove()})})}exports.when=a;
2
2
  //# sourceMappingURL=when.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"when.cjs","names":["isReactive","createDirectiveResult","isHtmlResult","removeNodes","computed","createReplaceableSlot","rawEffect","untrack","require_errors","require_result","require_dom","_vielzeug_ripple","NO_PARENT_MSG","when","condition","branch","truthy","falsy","parent","anchor","nodes","registerCleanup","conditionSignal","initialParent","endMarker","slot","sub","next"],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, type MaybeReactive, removeNodes } from '../utils/dom';\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst NO_PARENT_MSG = 'when() anchor comment has no parent node';\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor, registerCleanup) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const nodes = branch.mount(parent, anchor, registerCleanup);\n\n registerCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, NO_PARENT_MSG);\n\n const endMarker = document.createComment('when/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n const slot = createReplaceableSlot();\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n\n slot.clear();\n\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n // Resolved live: when a `when()` sits inside an each() item, a list\n // reorder moves the whole range (endMarker included) before this effect\n // next runs; the captured mount-time parent would be stale.\n const parent = endMarker.parentNode;\n\n if (!parent) return;\n\n slot.setNodes(untrack(() => branch.mount(parent, endMarker, slot.registerCleanup)));\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n });\n}\n"],"mappings":"A,qIAQA,IAAMY,EAAgB,2CActB,SAAgBC,EACd,EACA,EACA,EACiB,CAiBjB,OAhBI,OAAOC,GAAc,YAAc,EAAA,EAACd,EAAAA,WAAAA,CAAWc,CAAS,EACnDb,EAAAA,uBAAuB,EAAQ,IAAoB,CACxD,IAAMc,EAASD,EAAYE,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAExD,GAAI,CAACF,GAAU,CAACb,EAAAA,aAAaa,CAAM,EAAG,OAEtC,IAAMG,EAASC,EAAO,WAEtBX,EAAA,UAAUU,EAAQN,CAAa,EAE/B,IAAMQ,EAAQL,EAAO,MAAMG,EAAQC,EAAQE,CAAe,EAE1DA,MAAsBlB,EAAAA,YAAYiB,CAAK,CAAC,CAC1C,CAAC,EAGInB,EAAAA,uBAAuB,EAAQ,IAAoB,CAExD,IAAMqB,GADgB,OAAOR,GAAc,YAAA,EAAaV,EAAAA,SAAAA,CAASU,CAA0B,EAAI,OACrDA,EAEpCS,EAAgBJ,EAAO,WAE7BX,EAAA,UAAUe,EAAeX,CAAa,EAEtC,IAAMY,EAAY,SAAS,cAAc,UAAU,EAEnDD,EAAc,aAAaC,EAAWL,EAAO,WAAW,EAExD,IAAMM,EAAOpB,EAAAA,sBAAsB,EAE7BqB,GAAA,EAAMpB,EAAAA,OAAAA,KAAgB,CAC1B,IAAMqB,EAAOL,EAAgB,MAE7BG,EAAK,MAAM,EAEX,IAAMV,EAASY,EAAOX,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAEnD,GAAI,CAACF,GAAU,CAACb,EAAAA,aAAaa,CAAM,EAAG,OAKtC,IAAMG,EAASM,EAAU,WAEpBN,GAELO,EAAK,UAAA,EAASlB,EAAAA,QAAAA,KAAcQ,EAAO,MAAMG,EAAQM,EAAWC,EAAK,eAAe,CAAC,CAAC,CACpF,CAAC,EAEDJ,MAAsBK,EAAI,QAAQ,CAAC,EACnCL,MAAsB,CACpBI,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH,C"}
1
+ {"version":3,"file":"when.cjs","names":["isReactive","createDirectiveResult","isHtmlResult","removeNodes","computed","createReplaceableSlot","rawEffect","require_errors","require_result","require_dom","_vielzeug_ripple","NO_PARENT_MSG","when","condition","branch","truthy","falsy","parent","anchor","nodes","conditionSignal","initialParent","endMarker","slot","sub"],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, onCleanup, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, type MaybeReactive, removeNodes } from '../utils/dom';\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst NO_PARENT_MSG = 'when() anchor comment has no parent node';\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const nodes = branch.mount(parent, anchor);\n\n onCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, NO_PARENT_MSG);\n\n const endMarker = document.createComment('when/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n const slot = createReplaceableSlot();\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) {\n slot.clear();\n\n return;\n }\n\n // Resolved live: when a `when()` sits inside an each() item, a list\n // reorder moves the whole range (endMarker included) before this effect\n // next runs; the captured mount-time parent would be stale.\n const parent = endMarker.parentNode;\n\n if (!parent) {\n slot.clear();\n\n return;\n }\n\n untrack(() => {\n slot.render(() => {\n slot.setNodes(branch.mount(parent, endMarker));\n });\n });\n });\n\n onCleanup(() => {\n sub.dispose();\n slot.clear();\n endMarker.remove();\n });\n });\n}\n"],"mappings":"A,qIAQA,IAAMW,EAAgB,2CActB,SAAgBC,EACd,EACA,EACA,EACiB,CAiBjB,OAhBI,OAAOC,GAAc,YAAc,EAAA,EAACb,EAAAA,WAAAA,CAAWa,CAAS,EACnDZ,EAAAA,sBAAuB,GAAW,CACvC,IAAMa,EAASD,EAAYE,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAExD,GAAI,CAACF,GAAU,CAACZ,EAAAA,aAAaY,CAAM,EAAG,OAEtC,IAAMG,EAASC,EAAO,WAEtBX,EAAA,UAAUU,EAAQN,CAAa,EAE/B,IAAMQ,EAAQL,EAAO,MAAMG,EAAQC,CAAM,GAEzC,EAAAR,EAAA,UAAA,KAAgBP,EAAAA,YAAYgB,CAAK,CAAC,CACpC,CAAC,EAGIlB,EAAAA,sBAAuB,GAAW,CAEvC,IAAMmB,GADgB,OAAOP,GAAc,YAAA,EAAaT,EAAAA,SAAAA,CAASS,CAA0B,EAAI,OACrDA,EAEpCQ,EAAgBH,EAAO,WAE7BX,EAAA,UAAUc,EAAeV,CAAa,EAEtC,IAAMW,EAAY,SAAS,cAAc,UAAU,EAEnDD,EAAc,aAAaC,EAAWJ,EAAO,WAAW,EAExD,IAAMK,EAAOlB,EAAAA,sBAAsB,EAE7BmB,GAAA,EAAMlB,EAAAA,OAAAA,KAAgB,CAE1B,IAAMQ,EADOM,EAAgB,MACPL,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAEnD,GAAI,CAACF,GAAU,CAACZ,EAAAA,aAAaY,CAAM,EAAG,CACpCS,EAAK,MAAM,EAEX,MACF,CAKA,IAAMN,EAASK,EAAU,WAEpBL,GAML,EAAAP,EAAA,QAAA,KAAc,CACZa,EAAK,WAAa,CAChBA,EAAK,SAAST,EAAO,MAAMG,EAAQK,CAAS,CAAC,CAC/C,CAAC,CACH,CAAC,EATCC,EAAK,MAAM,CAUf,CAAC,GAED,EAAAb,EAAA,UAAA,KAAgB,CACdc,EAAI,QAAQ,EACZD,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"when.d.ts","sourceRoot":"","sources":["../../src/directives/when.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,UAAU,EAAgB,MAAM,oBAAoB,CAAC;AAChH,OAAO,EAAyB,KAAK,aAAa,EAAe,MAAM,cAAc,CAAC;AAEtF,KAAK,cAAc,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC;AAI5D;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,EACjC,MAAM,EAAE,MAAM,cAAc,EAC5B,KAAK,CAAC,EAAE,MAAM,cAAc,GAC3B,eAAe,CAwDjB"}
1
+ {"version":3,"file":"when.d.ts","sourceRoot":"","sources":["../../src/directives/when.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,UAAU,EAAgB,MAAM,oBAAoB,CAAC;AAChH,OAAO,EAAyB,KAAK,aAAa,EAAe,MAAM,cAAc,CAAC;AAEtF,KAAK,cAAc,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC;AAI5D;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,EACjC,MAAM,EAAE,MAAM,cAAc,EAC5B,KAAK,CAAC,EAAE,MAAM,cAAc,GAC3B,eAAe,CAiEjB"}
@@ -1,2 +1,2 @@
1
- import{invariant as e}from"../errors.js";import{createDirectiveResult as t,isHtmlResult as n}from"../template/result.js";import{createReplaceableSlot as r,removeNodes as i}from"../utils/dom.js";import{computed as a,effect as o,isReactive as s,untrack as c}from"@vielzeug/ripple";var l=`when() anchor comment has no parent node`;function u(u,d,f){return typeof u!=`function`&&!s(u)?t((t,r)=>{let a=u?d():f?f():null;if(!a||!n(a))return;let o=t.parentNode;e(o,l);let s=a.mount(o,t,r);r(()=>i(s))}):t((t,i)=>{let s=(typeof u==`function`?a(u):null)??u,p=t.parentNode;e(p,l);let m=document.createComment(`when/end`);p.insertBefore(m,t.nextSibling);let h=r(),g=o(()=>{let e=s.value;h.clear();let t=e?d():f?f():null;if(!t||!n(t))return;let r=m.parentNode;r&&h.setNodes(c(()=>t.mount(r,m,h.registerCleanup)))});i(()=>g.dispose()),i(()=>{h.clear(),m.remove()})})}export{u as when};
1
+ import{invariant as e}from"../errors.js";import{createDirectiveResult as t,isHtmlResult as n}from"../template/result.js";import{createReplaceableSlot as r,removeNodes as i}from"../utils/dom.js";import{computed as a,effect as o,isReactive as s,onCleanup as c,untrack as l}from"@vielzeug/ripple";var u=`when() anchor comment has no parent node`;function d(d,f,p){return typeof d!=`function`&&!s(d)?t(t=>{let r=d?f():p?p():null;if(!r||!n(r))return;let a=t.parentNode;e(a,u);let o=r.mount(a,t);c(()=>i(o))}):t(t=>{let i=(typeof d==`function`?a(d):null)??d,s=t.parentNode;e(s,u);let m=document.createComment(`when/end`);s.insertBefore(m,t.nextSibling);let h=r(),g=o(()=>{let e=i.value?f():p?p():null;if(!e||!n(e)){h.clear();return}let t=m.parentNode;t?l(()=>{h.render(()=>{h.setNodes(e.mount(t,m))})}):h.clear()});c(()=>{g.dispose(),h.clear(),m.remove()})})}export{d as when};
2
2
  //# sourceMappingURL=when.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"when.js","names":["rawEffect","NO_PARENT_MSG","when","condition","isReactive","createDirectiveResult","branch","truthy","falsy","isHtmlResult","parent","anchor","invariant","nodes","registerCleanup","removeNodes","conditionSignal","computed","initialParent","endMarker","slot","createReplaceableSlot","sub","effect","next","untrack"],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, type MaybeReactive, removeNodes } from '../utils/dom';\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst NO_PARENT_MSG = 'when() anchor comment has no parent node';\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor, registerCleanup) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const nodes = branch.mount(parent, anchor, registerCleanup);\n\n registerCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, NO_PARENT_MSG);\n\n const endMarker = document.createComment('when/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n const slot = createReplaceableSlot();\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n\n slot.clear();\n\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n // Resolved live: when a `when()` sits inside an each() item, a list\n // reorder moves the whole range (endMarker included) before this effect\n // next runs; the captured mount-time parent would be stale.\n const parent = endMarker.parentNode;\n\n if (!parent) return;\n\n slot.setNodes(untrack(() => branch.mount(parent, endMarker, slot.registerCleanup)));\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n });\n}\n"],"mappings":"A,uRAQA,IAAMC,EAAgB,2CActB,SAAgBC,EACd,EACA,EACA,EACiB,CAiBjB,OAhBI,OAAOC,GAAc,YAAc,CAACC,EAAWD,CAAS,EACnDE,GAAuB,EAAQ,IAAoB,CACxD,IAAMC,EAASH,EAAYI,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAExD,GAAI,CAACF,GAAU,CAACG,EAAaH,CAAM,EAAG,OAEtC,IAAMI,EAASC,EAAO,WAEtBC,EAAUF,EAAQT,CAAa,EAE/B,IAAMY,EAAQP,EAAO,MAAMI,EAAQC,EAAQG,CAAe,EAE1DA,MAAsBC,EAAYF,CAAK,CAAC,CAC1C,CAAC,EAGIR,GAAuB,EAAQ,IAAoB,CAExD,IAAMW,GADgB,OAAOb,GAAc,WAAac,EAASd,CAA0B,EAAI,OACrDA,EAEpCe,EAAgBP,EAAO,WAE7BC,EAAUM,EAAejB,CAAa,EAEtC,IAAMkB,EAAY,SAAS,cAAc,UAAU,EAEnDD,EAAc,aAAaC,EAAWR,EAAO,WAAW,EAExD,IAAMS,EAAOC,EAAsB,EAE7BC,EAAMtB,MAAgB,CAC1B,IAAMwB,EAAOR,EAAgB,MAE7BI,EAAK,MAAM,EAEX,IAAMd,EAASkB,EAAOjB,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAEnD,GAAI,CAACF,GAAU,CAACG,EAAaH,CAAM,EAAG,OAKtC,IAAMI,EAASS,EAAU,WAEpBT,GAELU,EAAK,SAASK,MAAcnB,EAAO,MAAMI,EAAQS,EAAWC,EAAK,eAAe,CAAC,CAAC,CACpF,CAAC,EAEDN,MAAsBQ,EAAI,QAAQ,CAAC,EACnCR,MAAsB,CACpBM,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH,C"}
1
+ {"version":3,"file":"when.js","names":["rawEffect","NO_PARENT_MSG","when","condition","isReactive","createDirectiveResult","branch","truthy","falsy","isHtmlResult","parent","anchor","invariant","nodes","onCleanup","removeNodes","conditionSignal","computed","initialParent","endMarker","slot","createReplaceableSlot","sub","effect","untrack"],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, onCleanup, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, type MaybeReactive, removeNodes } from '../utils/dom';\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst NO_PARENT_MSG = 'when() anchor comment has no parent node';\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const nodes = branch.mount(parent, anchor);\n\n onCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n const initialParent = anchor.parentNode;\n\n invariant(initialParent, NO_PARENT_MSG);\n\n const endMarker = document.createComment('when/end');\n\n initialParent.insertBefore(endMarker, anchor.nextSibling);\n\n const slot = createReplaceableSlot();\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) {\n slot.clear();\n\n return;\n }\n\n // Resolved live: when a `when()` sits inside an each() item, a list\n // reorder moves the whole range (endMarker included) before this effect\n // next runs; the captured mount-time parent would be stale.\n const parent = endMarker.parentNode;\n\n if (!parent) {\n slot.clear();\n\n return;\n }\n\n untrack(() => {\n slot.render(() => {\n slot.setNodes(branch.mount(parent, endMarker));\n });\n });\n });\n\n onCleanup(() => {\n sub.dispose();\n slot.clear();\n endMarker.remove();\n });\n });\n}\n"],"mappings":"A,sSAQA,IAAMC,EAAgB,2CActB,SAAgBC,EACd,EACA,EACA,EACiB,CAiBjB,OAhBI,OAAOC,GAAc,YAAc,CAACC,EAAWD,CAAS,EACnDE,EAAuB,GAAW,CACvC,IAAMC,EAASH,EAAYI,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAExD,GAAI,CAACF,GAAU,CAACG,EAAaH,CAAM,EAAG,OAEtC,IAAMI,EAASC,EAAO,WAEtBC,EAAUF,EAAQT,CAAa,EAE/B,IAAMY,EAAQP,EAAO,MAAMI,EAAQC,CAAM,EAEzCG,MAAgBC,EAAYF,CAAK,CAAC,CACpC,CAAC,EAGIR,EAAuB,GAAW,CAEvC,IAAMW,GADgB,OAAOb,GAAc,WAAac,EAASd,CAA0B,EAAI,OACrDA,EAEpCe,EAAgBP,EAAO,WAE7BC,EAAUM,EAAejB,CAAa,EAEtC,IAAMkB,EAAY,SAAS,cAAc,UAAU,EAEnDD,EAAc,aAAaC,EAAWR,EAAO,WAAW,EAExD,IAAMS,EAAOC,EAAsB,EAE7BC,EAAMtB,MAAgB,CAE1B,IAAMM,EADOU,EAAgB,MACPT,EAAO,EAAIC,EAAQA,EAAM,EAAI,KAEnD,GAAI,CAACF,GAAU,CAACG,EAAaH,CAAM,EAAG,CACpCc,EAAK,MAAM,EAEX,MACF,CAKA,IAAMV,EAASS,EAAU,WAEpBT,EAMLc,MAAc,CACZJ,EAAK,WAAa,CAChBA,EAAK,SAASd,EAAO,MAAMI,EAAQS,CAAS,CAAC,CAC/C,CAAC,CACH,CAAC,EATCC,EAAK,MAAM,CAUf,CAAC,EAEDN,MAAgB,CACdQ,EAAI,QAAQ,EACZF,EAAK,MAAM,EACXD,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH,C"}
package/dist/errors.cjs CHANGED
@@ -1,3 +1,3 @@
1
- require("./_dev.cjs");var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice: custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifierUnknown:(e,t)=>`@${e}.${t}: unknown event modifier "${t}". Supported: .stop, .prevent, .once, .capture`,injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}": listener not attached`,modelRequiresValueBinding:e=>`model(): "${e}" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${e}=\${model(sig)}, not ?${e}=\${model(sig)}.`,propBindingForbiddenName:e=>`html\`...\`: ".${e}" is a forbidden property-binding name (prototype pollution guard)`,propBindingHtmlInjectionName:e=>`html\`...\`: ".${e}" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,propInvalidReflect:`Structured props cannot use reflect:true: use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
1
+ require("./_dev.cjs");var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice: custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifierUnknown:(e,t)=>`@${e}.${t}: unknown event modifier "${t}". Supported: .stop, .prevent, .once, .capture`,htmlResultReused:"html`...` result was already rendered: each result renders once. Call the template function again for another copy.",injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}": listener not attached`,modelRequiresValueBinding:e=>`model(): "${e}" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${e}=\${model(sig)}, not ?${e}=\${model(sig)}.`,notAnHtmlResult:"expected an html`...` result",propBindingForbiddenName:e=>`html\`...\`: ".${e}" is a forbidden property-binding name (prototype pollution guard)`,propBindingHtmlInjectionName:e=>`html\`...\`: ".${e}" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,propInvalidReflect:`Structured props cannot use reflect:true: use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
2
2
  `)}`};function o(e,t){if(!e)throw new n(a.invariantViolated(t))}exports.ORE_ERRORS=a,exports.OreApiError=t,exports.OreError=e,exports.OreInternalError=n,exports.OreLifecycleError=r,exports.invariant=o,exports.reportRuntimeError=i;
3
3
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":["error","OreError","message","opts","OreApiError","OreInternalError","OreLifecycleError","options","reportRuntimeError","error$1","target","ORE_ERRORS","tag","key","index","eventName","modifier","name","errors","invariant","condition"],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package: decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails: e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'`: synchronous setup() threw\n * - `'mounted'`: an onMounted callback threw\n * - `'form-reset'`: an onFormReset callback threw\n * - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice: custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifierUnknown: (eventName: string, modifier: string): string =>\n `@${eventName}.${modifier}: unknown event modifier \"${modifier}\". Supported: .stop, .prevent, .once, .capture`,\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\": listener not attached`,\n modelRequiresValueBinding: (name: string): string =>\n `model(): \"${name}\" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${name}=\\${model(sig)}, not ?${name}=\\${model(sig)}.`,\n propBindingForbiddenName: (name: string): string =>\n `html\\`...\\`: \".${name}\" is a forbidden property-binding name (prototype pollution guard)`,\n propBindingHtmlInjectionName: (name: string): string =>\n `html\\`...\\`: \".${name}\" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,\n propInvalidReflect: 'Structured props cannot use reflect:true: use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold: e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"A,sBAoBA,IAAaC,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAMC,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGaC,EAAb,cAAiCH,CAAS,CAAC,EAO9BI,EAAb,cAAsCJ,CAAS,CAAC,EAenCK,EAAb,cAAuCL,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAMC,EAAS,CAAE,MAAOK,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAYA,EAAQ,UACzB,KAAK,MAAQA,EAAQ,KACvB,CACF,EAiBA,SAAgBC,EAAmB,EAA0B,EAA2B,CAC7E,GAAIR,EAAM,UAAV,EAAgDA,EAAM,MAAtD,EAAiEA,EAAM,MAEhFU,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQV,CACV,CAAC,CACH,CACF,CAIA,IAAaW,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAWC,EAAI,oDACzD,kCAAoC,GAClC,+BAA+BA,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkCC,EAAI,aAAaC,IAC7G,sBAAuB,EAAmB,IACxC,IAAIC,EAAU,GAAGC,EAAS,4BAA4BA,EAAS,gDACjE,oBAAqB,EAAa,IAAwB,yCAAyCH,EAAI,QAAQD,EAAI,GACnH,kBAAoB,GAA4B,uBAAuBV,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2Da,EAAU,0BACvE,0BAA4B,GAC1B,aAAaE,EAAK,yFAAyFA,EAAK,wBAAwBA,EAAK,kBAC/I,yBAA2B,GACzB,kBAAkBA,EAAK,oEACzB,6BAA+B,GAC7B,kBAAkBA,EAAK,sIACzB,mBAAoB,+EACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqCL,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0BA,EAAI,MAAMM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgBC,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAACC,EAAW,MAAM,IAAIf,EAAiBM,EAAW,kBAAkBT,CAAO,CAAC,CAClF,C"}
1
+ {"version":3,"file":"errors.cjs","names":["error","OreError","message","opts","OreApiError","OreInternalError","OreLifecycleError","options","reportRuntimeError","error$1","target","ORE_ERRORS","tag","key","index","eventName","modifier","name","errors","invariant","condition"],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package: decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails: e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'`: synchronous setup() threw\n * - `'mounted'`: an onMounted callback threw\n * - `'form-reset'`: an onFormReset callback threw\n * - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice: custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifierUnknown: (eventName: string, modifier: string): string =>\n `@${eventName}.${modifier}: unknown event modifier \"${modifier}\". Supported: .stop, .prevent, .once, .capture`,\n htmlResultReused:\n 'html`...` result was already rendered: each result renders once. Call the template function again for another copy.',\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\": listener not attached`,\n modelRequiresValueBinding: (name: string): string =>\n `model(): \"${name}\" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${name}=\\${model(sig)}, not ?${name}=\\${model(sig)}.`,\n notAnHtmlResult: 'expected an html`...` result',\n propBindingForbiddenName: (name: string): string =>\n `html\\`...\\`: \".${name}\" is a forbidden property-binding name (prototype pollution guard)`,\n propBindingHtmlInjectionName: (name: string): string =>\n `html\\`...\\`: \".${name}\" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,\n propInvalidReflect: 'Structured props cannot use reflect:true: use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold: e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"A,sBAoBA,IAAaC,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAMC,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGaC,EAAb,cAAiCH,CAAS,CAAC,EAO9BI,EAAb,cAAsCJ,CAAS,CAAC,EAenCK,EAAb,cAAuCL,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAMC,EAAS,CAAE,MAAOK,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAYA,EAAQ,UACzB,KAAK,MAAQA,EAAQ,KACvB,CACF,EAiBA,SAAgBC,EAAmB,EAA0B,EAA2B,CAC7E,GAAIR,EAAM,UAAV,EAAgDA,EAAM,MAAtD,EAAiEA,EAAM,MAEhFU,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQV,CACV,CAAC,CACH,CACF,CAIA,IAAaW,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAWC,EAAI,oDACzD,kCAAoC,GAClC,+BAA+BA,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkCC,EAAI,aAAaC,IAC7G,sBAAuB,EAAmB,IACxC,IAAIC,EAAU,GAAGC,EAAS,4BAA4BA,EAAS,gDACjE,iBACE,sHACF,oBAAqB,EAAa,IAAwB,yCAAyCH,EAAI,QAAQD,EAAI,GACnH,kBAAoB,GAA4B,uBAAuBV,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2Da,EAAU,0BACvE,0BAA4B,GAC1B,aAAaE,EAAK,yFAAyFA,EAAK,wBAAwBA,EAAK,kBAC/I,gBAAiB,+BACjB,yBAA2B,GACzB,kBAAkBA,EAAK,oEACzB,6BAA+B,GAC7B,kBAAkBA,EAAK,sIACzB,mBAAoB,+EACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqCL,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0BA,EAAI,MAAMM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgBC,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAACC,EAAW,MAAM,IAAIf,EAAiBM,EAAW,kBAAkBT,CAAO,CAAC,CAClF,C"}
package/dist/errors.d.ts CHANGED
@@ -56,11 +56,13 @@ export declare const ORE_ERRORS: {
56
56
  readonly defineRequiresTag: "define() requires a tag name";
57
57
  readonly eachDuplicateKey: (key: string, index: number) => string;
58
58
  readonly eventModifierUnknown: (eventName: string, modifier: string) => string;
59
+ readonly htmlResultReused: "html`...` result was already rendered: each result renders once. Call the template function again for another copy.";
59
60
  readonly injectStrictFailed: (key: string, tag: string) => string;
60
61
  readonly invariantViolated: (message: string) => string;
61
62
  readonly lifecycleOutsideSetup: "Lifecycle hooks must be called during component setup";
62
63
  readonly listenNullTarget: (eventName: string) => string;
63
64
  readonly modelRequiresValueBinding: (name: string) => string;
65
+ readonly notAnHtmlResult: "expected an html`...` result";
64
66
  readonly propBindingForbiddenName: (name: string) => string;
65
67
  readonly propBindingHtmlInjectionName: (name: string) => string;
66
68
  readonly propInvalidReflect: "Structured props cannot use reflect:true: use prop.json() with reflect:false";
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAmBA,kGAAkG;AAClG,qBAAa,QAAS,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,sGAAsG;AACtG,qBAAa,WAAY,SAAQ,QAAQ;CAAG;AAE5C;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;CAAG;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;AAElF;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE;CAKhG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAUtF;AAID,eAAO,MAAM,UAAU;;oCAEE,MAAM,KAAG,MAAM;sDACG,MAAM,KAAG,MAAM;;qCAGhC,MAAM,SAAS,MAAM,KAAG,MAAM;+CACpB,MAAM,YAAY,MAAM,KAAG,MAAM;uCAEzC,MAAM,OAAO,MAAM,KAAG,MAAM;0CACzB,MAAM,KAAG,MAAM;;2CAEd,MAAM,KAAG,MAAM;+CAEX,MAAM,KAAG,MAAM;8CAEhB,MAAM,KAAG,MAAM;kDAEX,MAAM,KAAG,MAAM;;;0CAKvB,MAAM,KAAG,MAAM;qCAEpB,MAAM,UAAU,MAAM,EAAE,KAAG,MAAM;CACjD,CAAC;AAEX;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAEhF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAmBA,kGAAkG;AAClG,qBAAa,QAAS,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,sGAAsG;AACtG,qBAAa,WAAY,SAAQ,QAAQ;CAAG;AAE5C;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;CAAG;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;AAElF;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE;CAKhG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAUtF;AAID,eAAO,MAAM,UAAU;;oCAEE,MAAM,KAAG,MAAM;sDACG,MAAM,KAAG,MAAM;;qCAGhC,MAAM,SAAS,MAAM,KAAG,MAAM;+CACpB,MAAM,YAAY,MAAM,KAAG,MAAM;;uCAIzC,MAAM,OAAO,MAAM,KAAG,MAAM;0CACzB,MAAM,KAAG,MAAM;;2CAEd,MAAM,KAAG,MAAM;+CAEX,MAAM,KAAG,MAAM;;8CAGhB,MAAM,KAAG,MAAM;kDAEX,MAAM,KAAG,MAAM;;;0CAKvB,MAAM,KAAG,MAAM;qCAEpB,MAAM,UAAU,MAAM,EAAE,KAAG,MAAM;CACjD,CAAC;AAEX;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAEhF"}
package/dist/errors.js CHANGED
@@ -1,3 +1,3 @@
1
- import"./_dev.js";var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice: custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifierUnknown:(e,t)=>`@${e}.${t}: unknown event modifier "${t}". Supported: .stop, .prevent, .once, .capture`,injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}": listener not attached`,modelRequiresValueBinding:e=>`model(): "${e}" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${e}=\${model(sig)}, not ?${e}=\${model(sig)}.`,propBindingForbiddenName:e=>`html\`...\`: ".${e}" is a forbidden property-binding name (prototype pollution guard)`,propBindingHtmlInjectionName:e=>`html\`...\`: ".${e}" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,propInvalidReflect:`Structured props cannot use reflect:true: use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
1
+ import"./_dev.js";var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice: custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifierUnknown:(e,t)=>`@${e}.${t}: unknown event modifier "${t}". Supported: .stop, .prevent, .once, .capture`,htmlResultReused:"html`...` result was already rendered: each result renders once. Call the template function again for another copy.",injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}": listener not attached`,modelRequiresValueBinding:e=>`model(): "${e}" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${e}=\${model(sig)}, not ?${e}=\${model(sig)}.`,notAnHtmlResult:"expected an html`...` result",propBindingForbiddenName:e=>`html\`...\`: ".${e}" is a forbidden property-binding name (prototype pollution guard)`,propBindingHtmlInjectionName:e=>`html\`...\`: ".${e}" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,propInvalidReflect:`Structured props cannot use reflect:true: use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
2
2
  `)}`};function o(e,t){if(!e)throw new n(a.invariantViolated(t))}export{a as ORE_ERRORS,t as OreApiError,e as OreError,n as OreInternalError,r as OreLifecycleError,o as invariant,i as reportRuntimeError};
3
3
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":["error","OreError","message","opts","OreApiError","OreInternalError","OreLifecycleError","options","reportRuntimeError","error$1","target","ORE_ERRORS","tag","key","index","eventName","modifier","name","errors","invariant","condition"],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package: decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails: e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'`: synchronous setup() threw\n * - `'mounted'`: an onMounted callback threw\n * - `'form-reset'`: an onFormReset callback threw\n * - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice: custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifierUnknown: (eventName: string, modifier: string): string =>\n `@${eventName}.${modifier}: unknown event modifier \"${modifier}\". Supported: .stop, .prevent, .once, .capture`,\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\": listener not attached`,\n modelRequiresValueBinding: (name: string): string =>\n `model(): \"${name}\" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${name}=\\${model(sig)}, not ?${name}=\\${model(sig)}.`,\n propBindingForbiddenName: (name: string): string =>\n `html\\`...\\`: \".${name}\" is a forbidden property-binding name (prototype pollution guard)`,\n propBindingHtmlInjectionName: (name: string): string =>\n `html\\`...\\`: \".${name}\" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,\n propInvalidReflect: 'Structured props cannot use reflect:true: use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold: e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"A,kBAoBA,IAAaC,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAMC,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGaC,EAAb,cAAiCH,CAAS,CAAC,EAO9BI,EAAb,cAAsCJ,CAAS,CAAC,EAenCK,EAAb,cAAuCL,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAMC,EAAS,CAAE,MAAOK,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAYA,EAAQ,UACzB,KAAK,MAAQA,EAAQ,KACvB,CACF,EAiBA,SAAgBC,EAAmB,EAA0B,EAA2B,CAC7E,GAAIR,EAAM,UAAV,EAAgDA,EAAM,MAAtD,EAAiEA,EAAM,MAEhFU,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQV,CACV,CAAC,CACH,CACF,CAIA,IAAaW,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAWC,EAAI,oDACzD,kCAAoC,GAClC,+BAA+BA,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkCC,EAAI,aAAaC,IAC7G,sBAAuB,EAAmB,IACxC,IAAIC,EAAU,GAAGC,EAAS,4BAA4BA,EAAS,gDACjE,oBAAqB,EAAa,IAAwB,yCAAyCH,EAAI,QAAQD,EAAI,GACnH,kBAAoB,GAA4B,uBAAuBV,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2Da,EAAU,0BACvE,0BAA4B,GAC1B,aAAaE,EAAK,yFAAyFA,EAAK,wBAAwBA,EAAK,kBAC/I,yBAA2B,GACzB,kBAAkBA,EAAK,oEACzB,6BAA+B,GAC7B,kBAAkBA,EAAK,sIACzB,mBAAoB,+EACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqCL,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0BA,EAAI,MAAMM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgBC,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAACC,EAAW,MAAM,IAAIf,EAAiBM,EAAW,kBAAkBT,CAAO,CAAC,CAClF,C"}
1
+ {"version":3,"file":"errors.js","names":["error","OreError","message","opts","OreApiError","OreInternalError","OreLifecycleError","options","reportRuntimeError","error$1","target","ORE_ERRORS","tag","key","index","eventName","modifier","name","errors","invariant","condition"],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package: decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails: e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'`: synchronous setup() threw\n * - `'mounted'`: an onMounted callback threw\n * - `'form-reset'`: an onFormReset callback threw\n * - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice: custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifierUnknown: (eventName: string, modifier: string): string =>\n `@${eventName}.${modifier}: unknown event modifier \"${modifier}\". Supported: .stop, .prevent, .once, .capture`,\n htmlResultReused:\n 'html`...` result was already rendered: each result renders once. Call the template function again for another copy.',\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\": listener not attached`,\n modelRequiresValueBinding: (name: string): string =>\n `model(): \"${name}\" must be a plain value/checked attribute binding, not a boolean attribute. Bind it as ${name}=\\${model(sig)}, not ?${name}=\\${model(sig)}.`,\n notAnHtmlResult: 'expected an html`...` result',\n propBindingForbiddenName: (name: string): string =>\n `html\\`...\\`: \".${name}\" is a forbidden property-binding name (prototype pollution guard)`,\n propBindingHtmlInjectionName: (name: string): string =>\n `html\\`...\\`: \".${name}\" parses raw HTML and is not a supported property binding. Sanitize untrusted content, then bind it with the unsafeHtml() directive.`,\n propInvalidReflect: 'Structured props cannot use reflect:true: use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold: e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"A,kBAoBA,IAAaC,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAMC,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGaC,EAAb,cAAiCH,CAAS,CAAC,EAO9BI,EAAb,cAAsCJ,CAAS,CAAC,EAenCK,EAAb,cAAuCL,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAMC,EAAS,CAAE,MAAOK,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAYA,EAAQ,UACzB,KAAK,MAAQA,EAAQ,KACvB,CACF,EAiBA,SAAgBC,EAAmB,EAA0B,EAA2B,CAC7E,GAAIR,EAAM,UAAV,EAAgDA,EAAM,MAAtD,EAAiEA,EAAM,MAEhFU,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQV,CACV,CAAC,CACH,CACF,CAIA,IAAaW,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAWC,EAAI,oDACzD,kCAAoC,GAClC,+BAA+BA,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkCC,EAAI,aAAaC,IAC7G,sBAAuB,EAAmB,IACxC,IAAIC,EAAU,GAAGC,EAAS,4BAA4BA,EAAS,gDACjE,iBACE,sHACF,oBAAqB,EAAa,IAAwB,yCAAyCH,EAAI,QAAQD,EAAI,GACnH,kBAAoB,GAA4B,uBAAuBV,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2Da,EAAU,0BACvE,0BAA4B,GAC1B,aAAaE,EAAK,yFAAyFA,EAAK,wBAAwBA,EAAK,kBAC/I,gBAAiB,+BACjB,yBAA2B,GACzB,kBAAkBA,EAAK,oEACzB,6BAA+B,GAC7B,kBAAkBA,EAAK,sIACzB,mBAAoB,+EACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqCL,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0BA,EAAI,MAAMM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgBC,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAACC,EAAW,MAAM,IAAIf,EAAiBM,EAAW,kBAAkBT,CAAO,CAAC,CAClF,C"}
@@ -1,2 +1,2 @@
1
- const e=require("./runtime.cjs"),t=require("./utils/aria.cjs"),n=require("./utils/dom.cjs");let r=require("@vielzeug/ripple");var i=(t,r)=>{let i=r?.target??e.getHost(),o=[];if(t.attr)for(let[e,n]of Object.entries(t.attr)){let t=s(i,a(e),n);t&&o.push(t)}if(t.class&&o.push(l(i,t.class)),t.style)for(let[e,n]of Object.entries(t.style)){let t=c(i,e,n);t&&o.push(t)}if(t.on){let{target:e,...a}=r??{};for(let e of Object.keys(t.on)){let r=t.on[e];r&&o.push(n.listen(i,e,r,a))}}let u=()=>{for(let e of o)e()};return e.tryRegisterCleanup(u),u},a=t.normalizeHostAttrKey,o=(t,n)=>{if(typeof t==`function`)return e.watchEffect(()=>{n(t())});if((0,r.isReactive)(t))return e.watchEffect(()=>{n(t.value)});n(t)};function s(e,t,r){return o(r,r=>n.setAttr(e,t,r))}function c(e,t,r){let i=n.cssDeclarationName(t);if(!i)return;let a=!1;return o(r,t=>{let r=n.cssDeclarationValue(t);r?(a=!0,e.style.setProperty(i,r)):a&&e.style.removeProperty(i)})}function l(t,r){let i=typeof r==`function`?r:()=>Object.fromEntries([...n.resolveClassTokens(r)].map(e=>[e,!0])),a=new Set;return e.watchEffect(()=>{let e=new Set;for(let[n,r]of Object.entries(i()))r&&(e.add(n),a.has(n)||t.classList.add(n));for(let n of a)e.has(n)||t.classList.remove(n);a=e})}exports.bind=i;
1
+ const e=require("./runtime.cjs"),t=require("./utils/aria.cjs"),n=require("./utils/dom.cjs");let r=require("@vielzeug/ripple");var i=(t,i)=>{let o=i?.target??e.getHost(),s=[];if(t.attr)for(let[e,n]of Object.entries(t.attr)){let t=c(o,a(e),n);t&&s.push(t)}if(t.class&&s.push(u(o,t.class)),t.style)for(let[e,n]of Object.entries(t.style)){let t=l(o,e,n);t&&s.push(t)}if(t.on){let{target:e,...r}=i??{};for(let e of Object.keys(t.on)){let i=t.on[e];i&&s.push(n.listen(o,e,i,r))}}let d=()=>{for(let e of s)e()};return e.hasSetupContext()&&(0,r.onCleanup)(d),d},a=t.normalizeHostAttrKey,o=e=>{let t=(0,r.effect)(()=>{e()});return()=>t.dispose()},s=(e,t)=>{if(typeof e==`function`)return o(()=>t(e()));if((0,r.isReactive)(e))return o(()=>t(e.value));t(e)};function c(e,t,r){return s(r,r=>n.setAttr(e,t,r))}function l(e,t,r){let i=n.cssDeclarationName(t);if(!i)return;let a=!1;return s(r,t=>{let r=n.cssDeclarationValue(t);r?(a=!0,e.style.setProperty(i,r)):a&&e.style.removeProperty(i)})}function u(e,t){let r=typeof t==`function`?t:()=>Object.fromEntries([...n.resolveClassTokens(t)].map(e=>[e,!0])),i=new Set;return o(()=>{let t=new Set;for(let[n,a]of Object.entries(r()))a&&(t.add(n),i.has(n)||e.classList.add(n));for(let n of i)t.has(n)||e.classList.remove(n);i=t})}exports.bind=i;
2
2
  //# sourceMappingURL=host-bind.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-bind.cjs","names":["getHost","listen","normalizeHostAttrKey","watchEffect","isReactive","setAttr","cssDeclarationName","cssDeclarationValue","resolveClassTokens","require_runtime","require_aria","require_dom","_vielzeug_ripple","bind","el","options","disposers","config","key","value","dispose","applyAttribute","toHostAttr","applyClassMap","applyStyle","_t","listenerOptions","event","listener","cleanup","applyReactiveBinding","updater","host","name","next","cssName","owned","declaration","v","getMap","cls","prev","active"],"sources":["../src/host-bind.ts"],"sourcesContent":["/**\n * Host element binding API: reactive attr, class, style, and event bindings\n * applied directly to the component's host element or any target element.\n */\n\nimport { isReactive } from '@vielzeug/ripple';\n\nimport { getHost, tryRegisterCleanup, watchEffect } from './runtime';\nimport { normalizeHostAttrKey } from './utils/aria';\nimport {\n cssDeclarationName,\n cssDeclarationValue,\n listen,\n type MaybeReactive,\n resolveClassTokens,\n setAttr,\n} from './utils/dom';\n\n/**\n * Describes a reactive or static host binding value.\n */\nexport type HostBindingValue = MaybeReactive<string | number | boolean | null | undefined>;\n\n/**\n * Configuration for host attribute bindings.\n */\nexport type AttributeBindings = Record<string, HostBindingValue>;\n\ntype HostClassBindingValue = MaybeReactive<boolean>;\n// Bivariant callback allows consumers to use narrower event types.\ntype HostEventListener = { bivarianceHack(event: Event): void }['bivarianceHack'];\n\nexport type HostBindConfig = {\n attr?: AttributeBindings;\n class?: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>;\n on?: Record<string, HostEventListener | undefined>;\n style?: Record<string, HostBindingValue>;\n};\n\nexport type BindOptions = AddEventListenerOptions & {\n /**\n * Target element to bind to. Defaults to the host element when called\n * via `bind()`. Pass an explicit element to bind to any other element\n * (e.g. a slotted trigger or an internally-referenced child element).\n * When a target is provided, cleanup is always auto-registered with the\n * component scope if one is active.\n */\n target?: Element;\n};\n\n/**\n * Apply reactive or static bindings to an element's attributes, classes, styles,\n * and events. Defaults to the current component's host element; pass\n * `options.target` to bind to any other element (e.g. a slotted trigger, an\n * internally-referenced child).\n */\nexport const bind = (config: HostBindConfig, options?: BindOptions): (() => void) => {\n const el = (options?.target as HTMLElement | undefined) ?? getHost();\n const disposers: Array<() => void> = [];\n\n if (config.attr) {\n for (const [key, value] of Object.entries(config.attr)) {\n const name = toHostAttr(key);\n const dispose = applyAttribute(el, name, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.class) {\n disposers.push(applyClassMap(el, config.class));\n }\n\n if (config.style) {\n for (const [key, value] of Object.entries(config.style)) {\n const dispose = applyStyle(el, key, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.on) {\n const { target: _t, ...listenerOptions } = options ?? {};\n\n for (const event of Object.keys(config.on) as Array<keyof typeof config.on>) {\n const listener = config.on[event];\n\n if (!listener) continue;\n\n disposers.push(listen(el, event as string, listener as EventListener, listenerOptions));\n }\n }\n\n const cleanup = (): void => {\n for (const dispose of disposers) dispose();\n };\n\n tryRegisterCleanup(cleanup);\n\n return cleanup;\n};\n\nconst toHostAttr = normalizeHostAttrKey;\n\nconst applyReactiveBinding = (\n value: HostBindingValue,\n updater: (next: string | number | boolean | null | undefined) => void,\n): (() => void) | undefined => {\n if (typeof value === 'function') {\n return watchEffect(() => {\n updater(value());\n return undefined;\n });\n }\n\n if (isReactive(value)) {\n return watchEffect(() => {\n updater(value.value);\n return undefined;\n });\n }\n\n updater(value);\n};\n\nfunction applyAttribute(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n return applyReactiveBinding(value, (next) => setAttr(host, name, next));\n}\n\nfunction applyStyle(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n const cssName = cssDeclarationName(name);\n\n if (!cssName) return;\n\n let owned = false;\n const setStyle = (v: string | number | boolean | null | undefined): void => {\n const declaration = cssDeclarationValue(v);\n\n if (declaration) {\n owned = true;\n host.style.setProperty(cssName, declaration);\n } else if (owned) host.style.removeProperty(cssName);\n };\n\n return applyReactiveBinding(value, setStyle);\n}\n\nfunction applyClassMap(\n host: HTMLElement,\n value: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>,\n): () => void {\n const getMap =\n typeof value === 'function'\n ? value\n : (): Record<string, boolean> => Object.fromEntries([...resolveClassTokens(value)].map((cls) => [cls, true]));\n\n let prev = new Set<string>();\n\n const sub = watchEffect(() => {\n const next = new Set<string>();\n\n for (const [cls, active] of Object.entries(getMap())) {\n if (!active) continue;\n\n next.add(cls);\n\n if (!prev.has(cls)) host.classList.add(cls);\n }\n for (const cls of prev) {\n if (!next.has(cls)) host.classList.remove(cls);\n }\n prev = next;\n });\n\n return sub;\n}\n"],"mappings":"A,8HAwDA,IAAaa,GAAQ,EAAwB,IAAwC,CACnF,IAAMC,EAAMC,GAAS,QAAsCf,EAAAA,QAAQ,EAC7DgB,EAA+B,CAAC,EAEtC,GAAIC,EAAO,KACT,IAAK,GAAM,CAACC,EAAKC,KAAU,OAAO,QAAQF,EAAO,IAAI,EAAG,CAEtD,IAAMG,EAAUC,EAAeP,EADlBQ,EAAWJ,CACW,EAAMC,CAAK,EAE1CC,GAASJ,EAAU,KAAKI,CAAO,CACrC,CAOF,GAJIH,EAAO,OACTD,EAAU,KAAKO,EAAcT,EAAIG,EAAO,KAAK,CAAC,EAG5CA,EAAO,MACT,IAAK,GAAM,CAACC,EAAKC,KAAU,OAAO,QAAQF,EAAO,KAAK,EAAG,CACvD,IAAMG,EAAUI,EAAWV,EAAII,EAAKC,CAAK,EAErCC,GAASJ,EAAU,KAAKI,CAAO,CACrC,CAGF,GAAIH,EAAO,GAAI,CACb,GAAM,CAAE,OAAQQ,EAAI,GAAGC,GAAoBX,GAAW,CAAC,EAEvD,IAAK,IAAMY,KAAS,OAAO,KAAKV,EAAO,EAAE,EAAoC,CAC3E,IAAMW,EAAWX,EAAO,GAAGU,GAEtBC,GAELZ,EAAU,KAAKf,EAAAA,OAAOa,EAAIa,EAAiBC,EAA2BF,CAAe,CAAC,CACxF,CACF,CAEA,IAAMG,MAAsB,CAC1B,IAAK,IAAMT,KAAWJ,EAAWI,EAAQ,CAC3C,EAIA,OAFAX,EAAA,mBAAmBoB,CAAO,EAEnBA,CACT,EAEMP,EAAapB,EAAAA,qBAEb4B,GACJ,EACA,IAC6B,CAC7B,GAAI,OAAOX,GAAU,WACnB,OAAOhB,EAAAA,gBAAkB,CACvB4B,EAAQZ,EAAM,CAAC,CAEjB,CAAC,EAGH,IAAA,EAAIf,EAAAA,WAAAA,CAAWe,CAAK,EAClB,OAAOhB,EAAAA,gBAAkB,CACvB4B,EAAQZ,EAAM,KAAK,CAErB,CAAC,EAGHY,EAAQZ,CAAK,CACf,EAEA,SAASE,EAAe,EAAmB,EAAc,EAAmD,CAC1G,OAAOS,EAAqBX,EAAQ,GAASd,EAAAA,QAAQ2B,EAAMC,EAAMC,CAAI,CAAC,CACxE,CAEA,SAASV,EAAW,EAAmB,EAAc,EAAmD,CACtG,IAAMW,EAAU7B,EAAAA,mBAAmB2B,CAAI,EAEvC,GAAI,CAACE,EAAS,OAEd,IAAIC,EAAQ,GAUZ,OAAON,EAAqBX,EATV,GAA0D,CAC1E,IAAMkB,EAAc9B,EAAAA,oBAAoB+B,CAAC,EAErCD,GACF,EAAQ,GACRL,EAAK,MAAM,YAAYG,EAASE,CAAW,GAClCD,GAAOJ,EAAK,MAAM,eAAeG,CAAO,CACrD,CAE2C,CAC7C,CAEA,SAASZ,EACP,EACA,EACY,CACZ,IAAMgB,EACJ,OAAOpB,GAAU,WACbA,MAC+B,OAAO,YAAY,CAAC,GAAGX,EAAAA,mBAAmBW,CAAK,CAAC,CAAC,CAAC,IAAK,GAAQ,CAACqB,EAAK,EAAI,CAAC,CAAC,EAE5GC,EAAO,IAAI,IAkBf,OAhBYtC,EAAAA,gBAAkB,CAC5B,IAAM+B,EAAO,IAAI,IAEjB,IAAK,GAAM,CAACM,EAAKE,KAAW,OAAO,QAAQH,EAAO,CAAC,EAC5CG,IAELR,EAAK,IAAIM,CAAG,EAEPC,EAAK,IAAID,CAAG,GAAGR,EAAK,UAAU,IAAIQ,CAAG,GAE5C,IAAK,IAAMA,KAAOC,EACXP,EAAK,IAAIM,CAAG,GAAGR,EAAK,UAAU,OAAOQ,CAAG,EAE/C,EAAON,CACT,CAEO,CACT,C"}
1
+ {"version":3,"file":"host-bind.cjs","names":["getHost","listen","hasSetupContext","normalizeHostAttrKey","effect","isReactive","setAttr","cssDeclarationName","cssDeclarationValue","resolveClassTokens","require_runtime","require_aria","require_dom","_vielzeug_ripple","bind","el","options","disposers","config","key","value","dispose","applyAttribute","toHostAttr","applyClassMap","applyStyle","_t","listenerOptions","event","listener","cleanup","reactive","sub","fn","applyReactiveBinding","updater","host","name","next","cssName","owned","declaration","v","getMap","cls","prev","active"],"sources":["../src/host-bind.ts"],"sourcesContent":["/**\n * Host element binding API: reactive attr, class, style, and event bindings\n * applied directly to the component's host element or any target element.\n */\n\nimport { effect, isReactive, onCleanup } from '@vielzeug/ripple';\n\nimport { getHost, hasSetupContext } from './runtime';\nimport { normalizeHostAttrKey } from './utils/aria';\nimport {\n cssDeclarationName,\n cssDeclarationValue,\n listen,\n type MaybeReactive,\n resolveClassTokens,\n setAttr,\n} from './utils/dom';\n\n/**\n * Describes a reactive or static host binding value.\n */\nexport type HostBindingValue = MaybeReactive<string | number | boolean | null | undefined>;\n\n/**\n * Configuration for host attribute bindings.\n */\nexport type AttributeBindings = Record<string, HostBindingValue>;\n\ntype HostClassBindingValue = MaybeReactive<boolean>;\n// Bivariant callback allows consumers to use narrower event types.\ntype HostEventListener = { bivarianceHack(event: Event): void }['bivarianceHack'];\n\nexport type HostBindConfig = {\n attr?: AttributeBindings;\n class?: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>;\n on?: Record<string, HostEventListener | undefined>;\n style?: Record<string, HostBindingValue>;\n};\n\nexport type BindOptions = AddEventListenerOptions & {\n /**\n * Target element to bind to. Defaults to the host element when called\n * via `bind()`. Pass an explicit element to bind to any other element\n * (e.g. a slotted trigger or an internally-referenced child element).\n * With a target, `bind()` also works outside setup (e.g. from an event\n * handler); there, only the returned disposer stops it.\n */\n target?: Element;\n};\n\n/**\n * Apply reactive or static bindings to an element's attributes, classes, styles,\n * and events. Defaults to the current component's host element; pass\n * `options.target` to bind to any other element (e.g. a slotted trigger, an\n * internally-referenced child).\n */\nexport const bind = (config: HostBindConfig, options?: BindOptions): (() => void) => {\n const el = (options?.target as HTMLElement | undefined) ?? getHost();\n const disposers: Array<() => void> = [];\n\n if (config.attr) {\n for (const [key, value] of Object.entries(config.attr)) {\n const name = toHostAttr(key);\n const dispose = applyAttribute(el, name, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.class) {\n disposers.push(applyClassMap(el, config.class));\n }\n\n if (config.style) {\n for (const [key, value] of Object.entries(config.style)) {\n const dispose = applyStyle(el, key, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.on) {\n const { target: _t, ...listenerOptions } = options ?? {};\n\n for (const event of Object.keys(config.on) as Array<keyof typeof config.on>) {\n const listener = config.on[event];\n\n if (!listener) continue;\n\n disposers.push(listen(el, event as string, listener as EventListener, listenerOptions));\n }\n }\n\n const cleanup = (): void => {\n for (const dispose of disposers) dispose();\n };\n\n if (hasSetupContext()) onCleanup(cleanup);\n\n return cleanup;\n};\n\nconst toHostAttr = normalizeHostAttrKey;\n\n/** Run `fn` reactively; the returned disposer is the only owner callers need to track. */\nconst reactive = (fn: () => void): (() => void) => {\n const sub = effect(() => {\n fn();\n return undefined;\n });\n\n return () => sub.dispose();\n};\n\nconst applyReactiveBinding = (\n value: HostBindingValue,\n updater: (next: string | number | boolean | null | undefined) => void,\n): (() => void) | undefined => {\n if (typeof value === 'function') return reactive(() => updater(value()));\n if (isReactive(value)) return reactive(() => updater(value.value));\n\n updater(value);\n};\n\nfunction applyAttribute(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n return applyReactiveBinding(value, (next) => setAttr(host, name, next));\n}\n\nfunction applyStyle(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n const cssName = cssDeclarationName(name);\n\n if (!cssName) return;\n\n let owned = false;\n const setStyle = (v: string | number | boolean | null | undefined): void => {\n const declaration = cssDeclarationValue(v);\n\n if (declaration) {\n owned = true;\n host.style.setProperty(cssName, declaration);\n } else if (owned) host.style.removeProperty(cssName);\n };\n\n return applyReactiveBinding(value, setStyle);\n}\n\nfunction applyClassMap(\n host: HTMLElement,\n value: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>,\n): () => void {\n const getMap =\n typeof value === 'function'\n ? value\n : (): Record<string, boolean> => Object.fromEntries([...resolveClassTokens(value)].map((cls) => [cls, true]));\n\n let prev = new Set<string>();\n\n return reactive(() => {\n const next = new Set<string>();\n\n for (const [cls, active] of Object.entries(getMap())) {\n if (!active) continue;\n\n next.add(cls);\n\n if (!prev.has(cls)) host.classList.add(cls);\n }\n for (const cls of prev) {\n if (!next.has(cls)) host.classList.remove(cls);\n }\n prev = next;\n });\n}\n"],"mappings":"A,8HAwDA,IAAac,GAAQ,EAAwB,IAAwC,CACnF,IAAMC,EAAMC,GAAS,QAAsChB,EAAAA,QAAQ,EAC7DiB,EAA+B,CAAC,EAEtC,GAAIC,EAAO,KACT,IAAK,GAAM,CAACC,EAAKC,KAAU,OAAO,QAAQF,EAAO,IAAI,EAAG,CAEtD,IAAMG,EAAUC,EAAeP,EADlBQ,EAAWJ,CACW,EAAMC,CAAK,EAE1CC,GAASJ,EAAU,KAAKI,CAAO,CACrC,CAOF,GAJIH,EAAO,OACTD,EAAU,KAAKO,EAAcT,EAAIG,EAAO,KAAK,CAAC,EAG5CA,EAAO,MACT,IAAK,GAAM,CAACC,EAAKC,KAAU,OAAO,QAAQF,EAAO,KAAK,EAAG,CACvD,IAAMG,EAAUI,EAAWV,EAAII,EAAKC,CAAK,EAErCC,GAASJ,EAAU,KAAKI,CAAO,CACrC,CAGF,GAAIH,EAAO,GAAI,CACb,GAAM,CAAE,OAAQQ,EAAI,GAAGC,GAAoBX,GAAW,CAAC,EAEvD,IAAK,IAAMY,KAAS,OAAO,KAAKV,EAAO,EAAE,EAAoC,CAC3E,IAAMW,EAAWX,EAAO,GAAGU,GAEtBC,GAELZ,EAAU,KAAKhB,EAAAA,OAAOc,EAAIa,EAAiBC,EAA2BF,CAAe,CAAC,CACxF,CACF,CAEA,IAAMG,MAAsB,CAC1B,IAAK,IAAMT,KAAWJ,EAAWI,EAAQ,CAC3C,EAIA,OAFInB,EAAAA,gBAAgB,IAAG,EAAAW,EAAA,UAAA,CAAUiB,CAAO,EAEjCA,CACT,EAEMP,EAAapB,EAAAA,qBAGb4B,EAAY,GAAiC,CACjD,IAAMC,GAAA,EAAM5B,EAAAA,OAAAA,KAAa,CACvB6B,EAAG,CAEL,CAAC,EAED,UAAaD,EAAI,QAAQ,CAC3B,EAEME,GACJ,EACA,IAC6B,CAC7B,GAAI,OAAOd,GAAU,WAAY,OAAOW,MAAeI,EAAQf,EAAM,CAAC,CAAC,EACvE,IAAA,EAAIf,EAAAA,WAAAA,CAAWe,CAAK,EAAG,OAAOW,MAAeI,EAAQf,EAAM,KAAK,CAAC,EAEjEe,EAAQf,CAAK,CACf,EAEA,SAASE,EAAe,EAAmB,EAAc,EAAmD,CAC1G,OAAOY,EAAqBd,EAAQ,GAASd,EAAAA,QAAQ8B,EAAMC,EAAMC,CAAI,CAAC,CACxE,CAEA,SAASb,EAAW,EAAmB,EAAc,EAAmD,CACtG,IAAMc,EAAUhC,EAAAA,mBAAmB8B,CAAI,EAEvC,GAAI,CAACE,EAAS,OAEd,IAAIC,EAAQ,GAUZ,OAAON,EAAqBd,EATV,GAA0D,CAC1E,IAAMqB,EAAcjC,EAAAA,oBAAoBkC,CAAC,EAErCD,GACF,EAAQ,GACRL,EAAK,MAAM,YAAYG,EAASE,CAAW,GAClCD,GAAOJ,EAAK,MAAM,eAAeG,CAAO,CACrD,CAE2C,CAC7C,CAEA,SAASf,EACP,EACA,EACY,CACZ,IAAMmB,EACJ,OAAOvB,GAAU,WACbA,MAC+B,OAAO,YAAY,CAAC,GAAGX,EAAAA,mBAAmBW,CAAK,CAAC,CAAC,CAAC,IAAK,GAAQ,CAACwB,EAAK,EAAI,CAAC,CAAC,EAE5GC,EAAO,IAAI,IAEf,OAAOd,MAAe,CACpB,IAAMO,EAAO,IAAI,IAEjB,IAAK,GAAM,CAACM,EAAKE,KAAW,OAAO,QAAQH,EAAO,CAAC,EAC5CG,IAELR,EAAK,IAAIM,CAAG,EAEPC,EAAK,IAAID,CAAG,GAAGR,EAAK,UAAU,IAAIQ,CAAG,GAE5C,IAAK,IAAMA,KAAOC,EACXP,EAAK,IAAIM,CAAG,GAAGR,EAAK,UAAU,OAAOQ,CAAG,EAE/C,EAAON,CACT,CAAC,CACH,C"}
@@ -26,8 +26,8 @@ export type BindOptions = AddEventListenerOptions & {
26
26
  * Target element to bind to. Defaults to the host element when called
27
27
  * via `bind()`. Pass an explicit element to bind to any other element
28
28
  * (e.g. a slotted trigger or an internally-referenced child element).
29
- * When a target is provided, cleanup is always auto-registered with the
30
- * component scope if one is active.
29
+ * With a target, `bind()` also works outside setup (e.g. from an event
30
+ * handler); there, only the returned disposer stops it.
31
31
  */
32
32
  target?: Element;
33
33
  };
package/dist/host-bind.js CHANGED
@@ -1,2 +1,2 @@
1
- import{getHost as e,tryRegisterCleanup as t,watchEffect as n}from"./runtime.js";import{normalizeHostAttrKey as r}from"./utils/aria.js";import{cssDeclarationName as i,cssDeclarationValue as a,listen as o,resolveClassTokens as s,setAttr as c}from"./utils/dom.js";import{isReactive as l}from"@vielzeug/ripple";var u=(n,r)=>{let i=r?.target??e(),a=[];if(n.attr)for(let[e,t]of Object.entries(n.attr)){let n=p(i,d(e),t);n&&a.push(n)}if(n.class&&a.push(h(i,n.class)),n.style)for(let[e,t]of Object.entries(n.style)){let n=m(i,e,t);n&&a.push(n)}if(n.on){let{target:e,...t}=r??{};for(let e of Object.keys(n.on)){let r=n.on[e];r&&a.push(o(i,e,r,t))}}let s=()=>{for(let e of a)e()};return t(s),s},d=r,f=(e,t)=>{if(typeof e==`function`)return n(()=>{t(e())});if(l(e))return n(()=>{t(e.value)});t(e)};function p(e,t,n){return f(n,n=>c(e,t,n))}function m(e,t,n){let r=i(t);if(!r)return;let o=!1;return f(n,t=>{let n=a(t);n?(o=!0,e.style.setProperty(r,n)):o&&e.style.removeProperty(r)})}function h(e,t){let r=typeof t==`function`?t:()=>Object.fromEntries([...s(t)].map(e=>[e,!0])),i=/* @__PURE__ */ new Set;return n(()=>{let t=/* @__PURE__ */ new Set;for(let[n,a]of Object.entries(r()))a&&(t.add(n),i.has(n)||e.classList.add(n));for(let n of i)t.has(n)||e.classList.remove(n);i=t})}export{u as bind};
1
+ import{getHost as e,hasSetupContext as t}from"./runtime.js";import{normalizeHostAttrKey as n}from"./utils/aria.js";import{cssDeclarationName as r,cssDeclarationValue as i,listen as a,resolveClassTokens as o,setAttr as s}from"./utils/dom.js";import{effect as c,isReactive as l,onCleanup as u}from"@vielzeug/ripple";var d=(n,r)=>{let i=r?.target??e(),o=[];if(n.attr)for(let[e,t]of Object.entries(n.attr)){let n=h(i,f(e),t);n&&o.push(n)}if(n.class&&o.push(_(i,n.class)),n.style)for(let[e,t]of Object.entries(n.style)){let n=g(i,e,t);n&&o.push(n)}if(n.on){let{target:e,...t}=r??{};for(let e of Object.keys(n.on)){let r=n.on[e];r&&o.push(a(i,e,r,t))}}let s=()=>{for(let e of o)e()};return t()&&u(s),s},f=n,p=e=>{let t=c(()=>{e()});return()=>t.dispose()},m=(e,t)=>{if(typeof e==`function`)return p(()=>t(e()));if(l(e))return p(()=>t(e.value));t(e)};function h(e,t,n){return m(n,n=>s(e,t,n))}function g(e,t,n){let a=r(t);if(!a)return;let o=!1;return m(n,t=>{let n=i(t);n?(o=!0,e.style.setProperty(a,n)):o&&e.style.removeProperty(a)})}function _(e,t){let n=typeof t==`function`?t:()=>Object.fromEntries([...o(t)].map(e=>[e,!0])),r=/* @__PURE__ */ new Set;return p(()=>{let t=/* @__PURE__ */ new Set;for(let[i,a]of Object.entries(n()))a&&(t.add(i),r.has(i)||e.classList.add(i));for(let n of r)t.has(n)||e.classList.remove(n);r=t})}export{d as bind};
2
2
  //# sourceMappingURL=host-bind.js.map