@vielzeug/ore 2.0.2 → 2.0.5

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 (102) hide show
  1. package/dist/_component-class.cjs.map +1 -1
  2. package/dist/_component-class.js.map +1 -1
  3. package/dist/base-element.cjs +1 -1
  4. package/dist/base-element.cjs.map +1 -1
  5. package/dist/base-element.d.ts.map +1 -1
  6. package/dist/base-element.js +1 -1
  7. package/dist/base-element.js.map +1 -1
  8. package/dist/context.cjs.map +1 -1
  9. package/dist/context.js.map +1 -1
  10. package/dist/define.cjs.map +1 -1
  11. package/dist/define.d.ts +3 -3
  12. package/dist/define.d.ts.map +1 -1
  13. package/dist/define.js.map +1 -1
  14. package/dist/directives/each.cjs.map +1 -1
  15. package/dist/directives/each.d.ts.map +1 -1
  16. package/dist/directives/each.js.map +1 -1
  17. package/dist/directives/unsafe-html.cjs.map +1 -1
  18. package/dist/directives/unsafe-html.d.ts.map +1 -1
  19. package/dist/directives/unsafe-html.js.map +1 -1
  20. package/dist/directives/when.cjs.map +1 -1
  21. package/dist/directives/when.d.ts.map +1 -1
  22. package/dist/directives/when.js.map +1 -1
  23. package/dist/errors.cjs +1 -1
  24. package/dist/errors.cjs.map +1 -1
  25. package/dist/errors.d.ts +0 -2
  26. package/dist/errors.d.ts.map +1 -1
  27. package/dist/errors.js +1 -1
  28. package/dist/errors.js.map +1 -1
  29. package/dist/forms/field.cjs +1 -1
  30. package/dist/forms/field.cjs.map +1 -1
  31. package/dist/forms/field.d.ts +1 -1
  32. package/dist/forms/field.d.ts.map +1 -1
  33. package/dist/forms/field.js +1 -1
  34. package/dist/forms/field.js.map +1 -1
  35. package/dist/host-bind.cjs +1 -1
  36. package/dist/host-bind.cjs.map +1 -1
  37. package/dist/host-bind.js +1 -1
  38. package/dist/host-bind.js.map +1 -1
  39. package/dist/index.cjs +1 -1
  40. package/dist/index.d.ts +14 -14
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/ore.cjs +2 -2
  44. package/dist/ore.cjs.map +1 -1
  45. package/dist/ore.iife.js +2 -2
  46. package/dist/ore.iife.js.map +1 -1
  47. package/dist/ore.js +2 -2
  48. package/dist/ore.js.map +1 -1
  49. package/dist/props.cjs.map +1 -1
  50. package/dist/props.js.map +1 -1
  51. package/dist/runtime.cjs.map +1 -1
  52. package/dist/runtime.d.ts +3 -3
  53. package/dist/runtime.d.ts.map +1 -1
  54. package/dist/runtime.js.map +1 -1
  55. package/dist/slots.cjs.map +1 -1
  56. package/dist/slots.d.ts.map +1 -1
  57. package/dist/slots.js.map +1 -1
  58. package/dist/template/binding-types.d.ts +3 -3
  59. package/dist/template/binding-types.d.ts.map +1 -1
  60. package/dist/template/bindings.cjs +1 -1
  61. package/dist/template/bindings.cjs.map +1 -1
  62. package/dist/template/bindings.d.ts +1 -1
  63. package/dist/template/bindings.d.ts.map +1 -1
  64. package/dist/template/bindings.js +1 -1
  65. package/dist/template/bindings.js.map +1 -1
  66. package/dist/template/compiler.cjs +1 -1
  67. package/dist/template/compiler.cjs.map +1 -1
  68. package/dist/template/compiler.d.ts.map +1 -1
  69. package/dist/template/compiler.js +1 -1
  70. package/dist/template/compiler.js.map +1 -1
  71. package/dist/template/instantiator.cjs +1 -1
  72. package/dist/template/instantiator.cjs.map +1 -1
  73. package/dist/template/instantiator.js +1 -1
  74. package/dist/template/instantiator.js.map +1 -1
  75. package/dist/template/result.cjs.map +1 -1
  76. package/dist/template/result.d.ts.map +1 -1
  77. package/dist/template/result.js.map +1 -1
  78. package/dist/testing/index.d.ts +3 -3
  79. package/dist/testing/index.d.ts.map +1 -1
  80. package/dist/testing/mount.cjs +1 -1
  81. package/dist/testing/mount.cjs.map +1 -1
  82. package/dist/testing/mount.d.ts +11 -11
  83. package/dist/testing/mount.d.ts.map +1 -1
  84. package/dist/testing/mount.js +1 -1
  85. package/dist/testing/mount.js.map +1 -1
  86. package/dist/testing/render-hook.cjs.map +1 -1
  87. package/dist/testing/render-hook.d.ts +2 -2
  88. package/dist/testing/render-hook.d.ts.map +1 -1
  89. package/dist/testing/render-hook.js.map +1 -1
  90. package/dist/testing.cjs.map +1 -1
  91. package/dist/testing.js.map +1 -1
  92. package/dist/utils/dom.cjs +1 -1
  93. package/dist/utils/dom.cjs.map +1 -1
  94. package/dist/utils/dom.js +1 -1
  95. package/dist/utils/dom.js.map +1 -1
  96. package/dist/utils/emit.cjs.map +1 -1
  97. package/dist/utils/emit.d.ts +1 -1
  98. package/dist/utils/emit.d.ts.map +1 -1
  99. package/dist/utils/emit.js.map +1 -1
  100. package/package.json +28 -28
  101. package/dist/template/binding-types.cjs +0 -1
  102. package/dist/template/binding-types.js +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bindings.js","names":[],"sources":["../../src/template/bindings.ts"],"sourcesContent":["/**\n * template/bindings.ts — Runtime binding appliers.\n *\n * Responsibilities:\n * - Apply each Binding variant to the live DOM (attr, event, html, ref,\n * directive).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n * - Own the signal-to-form-control write path (`syncFormControl`) for regular\n * attribute bindings.\n */\n\nimport { computed, effect as rawEffect, isReactive, type Readable, untrack } from '@vielzeug/ripple';\n\nimport { isLiveBinding } from '../directives/live';\nimport { invariant } from '../errors';\nimport { getPropMeta, type PropMeta } from '../props';\nimport { createReplaceableSlot, isStructuredValue, listen, setAttr } from '../utils/dom';\nimport {\n type AttrBinding,\n type Binding,\n type DirectiveBinding,\n type EventBinding,\n type HtmlBinding,\n type HtmlBindingValue,\n type RefBinding,\n} from './binding-types';\nimport { isHtmlResult } from './result';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type RegisterCleanup = (fn: () => void) => void;\n\n// ─── Signal helpers ───────────────────────────────────────────────────────────\n\nconst signalEffect = (\n signal: Readable<unknown>,\n update: (v: unknown) => void,\n registerCleanup: RegisterCleanup,\n): void => {\n const sub = rawEffect(() => update(signal.value));\n\n registerCleanup(() => sub.dispose());\n};\n\n// ─── Form control value sync ──────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\nconst isCheckableInput = (el: HTMLElement): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\ntype LiveWriteState = { last: unknown };\n\n/**\n * The single write path from a reactive value to a form control's `value`/`checked`\n * property for regular attribute bindings.\n *\n * Live-write: when the binding was created from `live(source)`, a write is skipped\n * if the DOM value has diverged from this binding's last write (in-progress user\n * input) — unless the incoming value already matches the DOM (write would be a no-op).\n */\nexport const syncFormControl = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n isLive?: boolean,\n state: LiveWriteState = { last: undefined },\n): void => {\n const checkable = isCheckableInput(el);\n const next: boolean | string = checkable ? Boolean(value) : value == null ? '' : String(value);\n const current: boolean | string = checkable ? (el as HTMLInputElement).checked : el.value;\n\n if (isLive && state.last !== undefined && !Object.is(current, state.last) && !Object.is(current, next)) return;\n\n if (checkable) (el as HTMLInputElement).checked = next as boolean;\n else el.value = next as string;\n\n if (isLive) state.last = next;\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst syncRegisteredProp = (el: HTMLElement, meta: PropMeta, binding: AttrBinding, value: unknown): void => {\n const parsed = isStructuredValue(value)\n ? value\n : meta.parse(\n binding.mode === 'bool' ? (value ? '' : null) : value == null || value === false ? null : String(value),\n );\n\n if (\n !Object.is(\n untrack(() => meta.signal.value),\n parsed,\n )\n ) {\n meta.signal.value = parsed;\n }\n\n if (!meta.reflect) {\n if (isStructuredValue(value)) return;\n\n if (binding.mode === 'bool') el.toggleAttribute(binding.name, Boolean(value));\n else setAttr(el, binding.name, value);\n }\n};\n\nexport const applyAttrBinding = (binding: AttrBinding, registerCleanup: RegisterCleanup): void => {\n const { el, mode, name, propMeta } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const update = (value: unknown): void => {\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if (!isReactive(value) && isStructuredValue(value)) {\n if (name !== '__proto__' && name !== 'constructor' && name !== 'prototype') {\n (el as unknown as Record<string, unknown>)[name] = value;\n }\n\n return;\n }\n\n if ((name === 'value' && isNativeFormInput(el)) || (name === 'checked' && el instanceof HTMLInputElement)) {\n syncFormControl(el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value, binding.live, liveState);\n\n return;\n }\n\n if (mode === 'bool') el.toggleAttribute(name, Boolean(value));\n else setAttr(el, name, value);\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Events ───────────────────────────────────────────────────────────────────\n\nconst applyEventBinding = (binding: EventBinding, registerCleanup: RegisterCleanup): void => {\n registerCleanup(listen(binding.el, binding.name, binding.handler));\n};\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nconst applyRefBinding = (binding: RefBinding, registerCleanup: RegisterCleanup): void => {\n const { el, ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n registerCleanup(() => ref(null as never));\n\n return;\n }\n\n ref.value = el as never;\n registerCleanup(() => {\n ref.value = null as never;\n });\n};\n\n// ─── HTML (reactive) ─────────────────────────────────────────────────────────\n\nconst insertHtmlValues = (\n values: HtmlBindingValue[],\n insertBefore: ChildNode,\n registerCleanup: RegisterCleanup,\n): Node[] => {\n const nodes: Node[] = [];\n const parent = insertBefore.parentNode;\n\n invariant(parent, 'html binding anchor has no parent node');\n\n for (const v of values) {\n if (isHtmlResult(v)) {\n const captured = Array.from(v.fragment.childNodes);\n\n parent.insertBefore(v.fragment, insertBefore);\n v.apply(registerCleanup);\n nodes.push(...captured);\n } else if (v != null && v !== false) {\n const text = document.createTextNode(String(v));\n\n parent.insertBefore(text, insertBefore);\n nodes.push(text);\n }\n }\n\n return nodes;\n};\n\nexport const applyHtmlBinding = (binding: HtmlBinding, registerCleanup: RegisterCleanup): void => {\n const { anchor, signal } = binding;\n\n const slot = createReplaceableSlot();\n\n const stop = rawEffect(() => {\n const raw = signal.value;\n\n slot.clear();\n\n if (raw == null || raw.length === 0) return;\n\n untrack(() => {\n slot.setNodes(insertHtmlValues(raw, anchor, slot.registerCleanup));\n });\n });\n\n registerCleanup(() => {\n stop.dispose();\n slot.clear();\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding, registerCleanup: RegisterCleanup): void => {\n binding.directive.mount(binding.anchor, registerCleanup);\n};\n\n// ─── Binding dispatch ─────────────────────────────────────────────────────────\n\nexport const applyBinding = (binding: Binding, registerCleanup: RegisterCleanup): void => {\n switch (binding.type) {\n case 'attr':\n applyAttrBinding(binding, registerCleanup);\n break;\n case 'directive':\n applyDirectiveBinding(binding, registerCleanup);\n break;\n case 'event':\n applyEventBinding(binding, registerCleanup);\n break;\n case 'html':\n applyHtmlBinding(binding, registerCleanup);\n break;\n case 'ref':\n applyRefBinding(binding, registerCleanup);\n break;\n }\n};\n\n// ─── Attr binding factory (used by instantiator) ──────────────────────────────\n\nexport const createAttrBindingFromValue = (\n el: HTMLElement,\n mode: 'attr' | 'bool',\n name: string,\n value: unknown,\n): AttrBinding => {\n const propMeta = getPropMeta(el, name);\n\n if (isLiveBinding(value)) {\n return { el, live: true, mode, name, propMeta, signal: value.source, type: 'attr' };\n }\n\n if (typeof value === 'function') {\n return { el, mode, name, propMeta, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, propMeta, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, propMeta, type: 'attr', value };\n};\n\nexport const resolveStaticText = (value: unknown): string => {\n if (value == null) return '';\n\n return String(value);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"bindings.js","names":[],"sources":["../../src/template/bindings.ts"],"sourcesContent":["/**\n * template/bindings.ts — Runtime binding appliers.\n *\n * Responsibilities:\n * - Apply each Binding variant to the live DOM (attr, event, html, ref,\n * directive).\n * - Manage reactive effects and cleanup registration.\n * - Expose `applyBinding()` as the single dispatch entry point.\n * - Own the signal-to-form-control write path (`syncFormControl`) for regular\n * attribute bindings.\n */\n\nimport { computed, isReactive, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { isLiveBinding } from '../directives/live';\nimport { invariant } from '../errors';\nimport { getPropMeta, type PropMeta } from '../props';\nimport { createReplaceableSlot, isStructuredValue, listen, setAttr } from '../utils/dom';\nimport type {\n AttrBinding,\n Binding,\n DirectiveBinding,\n EventBinding,\n HtmlBinding,\n HtmlBindingValue,\n RefBinding,\n} from './binding-types';\nimport { isHtmlResult } from './result';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type RegisterCleanup = (fn: () => void) => void;\n\n// ─── Signal helpers ───────────────────────────────────────────────────────────\n\nconst signalEffect = (\n signal: Readable<unknown>,\n update: (v: unknown) => void,\n registerCleanup: RegisterCleanup,\n): void => {\n const sub = rawEffect(() => {\n update(signal.value);\n return undefined;\n });\n\n registerCleanup(() => sub.dispose());\n};\n\n// ─── Form control value sync ──────────────────────────────────────────────────\n\nconst isNativeFormInput = (el: HTMLElement): el is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;\n\nconst isCheckableInput = (el: HTMLElement): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\ntype LiveWriteState = { last: unknown };\n\n/**\n * The single write path from a reactive value to a form control's `value`/`checked`\n * property for regular attribute bindings.\n *\n * Live-write: when the binding was created from `live(source)`, a write is skipped\n * if the DOM value has diverged from this binding's last write (in-progress user\n * input) — unless the incoming value already matches the DOM (write would be a no-op).\n */\nexport const syncFormControl = (\n el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n value: unknown,\n isLive?: boolean,\n state: LiveWriteState = { last: undefined },\n): void => {\n const checkable = isCheckableInput(el);\n const next: boolean | string = checkable ? Boolean(value) : value == null ? '' : String(value);\n const current: boolean | string = checkable ? (el as HTMLInputElement).checked : el.value;\n\n if (isLive && state.last !== undefined && !Object.is(current, state.last) && !Object.is(current, next)) return;\n\n if (checkable) (el as HTMLInputElement).checked = next as boolean;\n else el.value = next as string;\n\n if (isLive) state.last = next;\n};\n\n// ─── Attributes ───────────────────────────────────────────────────────────────\n\nconst syncRegisteredProp = (el: HTMLElement, meta: PropMeta, binding: AttrBinding, value: unknown): void => {\n const parsed = isStructuredValue(value)\n ? value\n : meta.parse(\n binding.mode === 'bool' ? (value ? '' : null) : value == null || value === false ? null : String(value),\n );\n\n if (\n !Object.is(\n untrack(() => meta.signal.value),\n parsed,\n )\n ) {\n meta.signal.value = parsed;\n }\n\n if (!meta.reflect) {\n if (isStructuredValue(value)) return;\n\n if (binding.mode === 'bool') el.toggleAttribute(binding.name, Boolean(value));\n else setAttr(el, binding.name, value);\n }\n};\n\nexport const applyAttrBinding = (binding: AttrBinding, registerCleanup: RegisterCleanup): void => {\n const { el, mode, name, propMeta } = binding;\n const liveState: LiveWriteState = { last: undefined };\n\n const update = (value: unknown): void => {\n if (propMeta) {\n syncRegisteredProp(el, propMeta, binding, value);\n\n return;\n }\n\n if (!isReactive(value) && isStructuredValue(value)) {\n if (name !== '__proto__' && name !== 'constructor' && name !== 'prototype') {\n (el as unknown as Record<string, unknown>)[name] = value;\n }\n\n return;\n }\n\n if ((name === 'value' && isNativeFormInput(el)) || (name === 'checked' && el instanceof HTMLInputElement)) {\n syncFormControl(el as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value, binding.live, liveState);\n\n return;\n }\n\n if (mode === 'bool') el.toggleAttribute(name, Boolean(value));\n else setAttr(el, name, value);\n };\n\n if ('signal' in binding) {\n signalEffect(binding.signal, update, registerCleanup);\n } else {\n update(binding.value);\n }\n};\n\n// ─── Events ───────────────────────────────────────────────────────────────────\n\nconst applyEventBinding = (binding: EventBinding, registerCleanup: RegisterCleanup): void => {\n registerCleanup(listen(binding.el, binding.name, binding.handler));\n};\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nconst applyRefBinding = (binding: RefBinding, registerCleanup: RegisterCleanup): void => {\n const { el, ref } = binding;\n\n if (typeof ref === 'function') {\n ref(el as never);\n registerCleanup(() => ref(null as never));\n\n return;\n }\n\n ref.value = el as never;\n registerCleanup(() => {\n ref.value = null as never;\n });\n};\n\n// ─── HTML (reactive) ─────────────────────────────────────────────────────────\n\nconst insertHtmlValues = (\n values: HtmlBindingValue[],\n insertBefore: ChildNode,\n registerCleanup: RegisterCleanup,\n): Node[] => {\n const nodes: Node[] = [];\n const parent = insertBefore.parentNode;\n\n invariant(parent, 'html binding anchor has no parent node');\n\n for (const v of values) {\n if (isHtmlResult(v)) {\n const captured = Array.from(v.fragment.childNodes);\n\n parent.insertBefore(v.fragment, insertBefore);\n v.apply(registerCleanup);\n nodes.push(...captured);\n } else if (v != null && v !== false) {\n const text = document.createTextNode(String(v));\n\n parent.insertBefore(text, insertBefore);\n nodes.push(text);\n }\n }\n\n return nodes;\n};\n\nexport const applyHtmlBinding = (binding: HtmlBinding, registerCleanup: RegisterCleanup): void => {\n const { anchor, signal } = binding;\n\n const slot = createReplaceableSlot();\n\n const stop = rawEffect(() => {\n const raw = signal.value;\n\n slot.clear();\n\n if (raw == null || raw.length === 0) return;\n\n untrack(() => {\n slot.setNodes(insertHtmlValues(raw, anchor, slot.registerCleanup));\n });\n });\n\n registerCleanup(() => {\n stop.dispose();\n slot.clear();\n });\n};\n\n// ─── Directives ───────────────────────────────────────────────────────────────\n\nconst applyDirectiveBinding = (binding: DirectiveBinding, registerCleanup: RegisterCleanup): void => {\n binding.directive.mount(binding.anchor, registerCleanup);\n};\n\n// ─── Binding dispatch ─────────────────────────────────────────────────────────\n\nexport const applyBinding = (binding: Binding, registerCleanup: RegisterCleanup): void => {\n switch (binding.type) {\n case 'attr':\n applyAttrBinding(binding, registerCleanup);\n break;\n case 'directive':\n applyDirectiveBinding(binding, registerCleanup);\n break;\n case 'event':\n applyEventBinding(binding, registerCleanup);\n break;\n case 'html':\n applyHtmlBinding(binding, registerCleanup);\n break;\n case 'ref':\n applyRefBinding(binding, registerCleanup);\n break;\n }\n};\n\n// ─── Attr binding factory (used by instantiator) ──────────────────────────────\n\nexport const createAttrBindingFromValue = (\n el: HTMLElement,\n mode: 'attr' | 'bool',\n name: string,\n value: unknown,\n): AttrBinding => {\n const propMeta = getPropMeta(el, name);\n\n if (isLiveBinding(value)) {\n return { el, live: true, mode, name, propMeta, signal: value.source, type: 'attr' };\n }\n\n if (typeof value === 'function') {\n return { el, mode, name, propMeta, signal: computed(value as () => unknown), type: 'attr' };\n }\n\n if (isReactive(value)) {\n return { el, mode, name, propMeta, signal: value as Readable<unknown>, type: 'attr' };\n }\n\n return { el, mode, name, propMeta, type: 'attr', value };\n};\n\nexport const resolveStaticText = (value: unknown): string => {\n if (value == null) return '';\n\n return String(value);\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs");var t={ATTR:`attr`,BOOL_ATTR:`boolAttr`,EVENT:`event`,NODE:`node`,REF:`ref`},n=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,r=/\s+ref\s*=\s*["']?$/,i=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,a=/\s+([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,o=o=>{let s;if(s=n.exec(o)){let n=o.slice(0,-s[0].length),[r,...i]=s[1].split(`.`);if(i.length>0)throw new e.OreApiError(e.ORE_ERRORS.eventModifiersUnsupported(s[1]));return{kind:t.EVENT,name:r,prefix:n}}if(s=r.exec(o))return{kind:t.REF,prefix:o.slice(0,-s[0].length)};if(s=i.exec(o))return{kind:t.BOOL_ATTR,name:s[1],prefix:o.slice(0,-s[0].length)};if(s=a.exec(o))return{kind:t.ATTR,name:s[1],prefix:o.slice(0,-s[0].length)};let c=o.lastIndexOf(`<`);if(c>o.lastIndexOf(`>`)&&o[c+1]!==`/`)throw new e.OreApiError(e.ORE_ERRORS.templateInterpolationInTag);return{kind:t.NODE,prefix:o}},s=new WeakMap,c=/[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\s*=\s*$/,l=e=>{let t=Array.from(e),n=!1;for(let e=0;e<t.length-1;e++){let r=t[e],i=r[r.length-1];for(let e of r)e===`<`?n=!0:e===`>`&&(n=!1);if((i===`"`||i===`'`)&&n&&c.test(r.slice(0,-1))){t[e]=r.slice(0,-1);let n=t[e+1];n.startsWith(i)&&(t[e+1]=n.slice(1))}}return t},u=`data-ore-b`,d=/^ore:(\d+)$/,f=(e,t,n,r)=>{if(e.nodeType===Node.ELEMENT_NODE){let r=e,i=r.getAttribute(u);i!==null&&(n.set(Number(i),[...t]),r.removeAttribute(u))}else if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue,i=n===null?null:d.exec(n);i&&r.set(Number(i[1]),[...t])}let i=e.childNodes;for(let e=0;e<i.length;e++)f(i[e],[...t,e],n,r)},p=e=>{let n=l(e),r=``,i,a=0,s=0,c=[];for(let e=0;e<n.length-1;e++){let l=n[e],d=o(l);if(d.kind===t.NODE)r+=d.prefix+`<!--ore:${s}-->`,c.push({commentId:s,kind:t.NODE}),s++,i=void 0;else{i===void 0||d.prefix.lastIndexOf(`<`)>d.prefix.lastIndexOf(`>`)?(i=a++,r+=`${d.prefix} ${u}="${i}"`):r+=d.prefix;let e=d.kind===t.BOOL_ATTR?`bool`:d.kind===t.ATTR?`attr`:void 0;c.push({elementId:i,kind:d.kind,mode:e,name:d.name})}}r+=n[n.length-1]??``;let d=document.createElement(`template`);d.innerHTML=r;let p=new Map,m=new Map,h=d.content.childNodes;for(let e=0;e<h.length;e++)f(h[e],[e],p,m);return{commentPaths:m,element:d,elementPaths:p,slots:c}},m=e=>{let t=s.get(e);return t||(t=p(e),s.set(e,t)),t},h=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};exports.SlotKind=t,exports.followPath=h,exports.getStaticTemplate=m;
1
+ const e=require("../errors.cjs");var t={ATTR:`attr`,BOOL_ATTR:`boolAttr`,EVENT:`event`,NODE:`node`,REF:`ref`},n=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,r=/\s+ref\s*=\s*["']?$/,i=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,a=/\s+([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,o=o=>{let s;if(s=n.exec(o)){let n=o.slice(0,-s[0].length),[r,...i]=s[1].split(`.`);if(i.length>0)throw new e.OreApiError(e.ORE_ERRORS.eventModifiersUnsupported(s[1]));return{kind:t.EVENT,name:r,prefix:n}}if(s=r.exec(o))return{kind:t.REF,prefix:o.slice(0,-s[0].length)};if(s=i.exec(o))return{kind:t.BOOL_ATTR,name:s[1],prefix:o.slice(0,-s[0].length)};if(s=a.exec(o))return{kind:t.ATTR,name:s[1],prefix:o.slice(0,-s[0].length)};let c=o.lastIndexOf(`<`);if(c>o.lastIndexOf(`>`)&&o[c+1]!==`/`)throw new e.OreApiError(e.ORE_ERRORS.templateInterpolationInTag);return{kind:t.NODE,prefix:o}},s=new WeakMap,c=/[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\s*=\s*$/,l=e=>{let t=Array.from(e),n=!1;for(let e=0;e<t.length-1;e++){let r=t[e],i=r[r.length-1];for(let e of r)e===`<`?n=!0:e===`>`&&(n=!1);if((i===`"`||i===`'`)&&n&&c.test(r.slice(0,-1))){t[e]=r.slice(0,-1);let n=t[e+1];n.startsWith(i)&&(t[e+1]=n.slice(1))}}return t},u=`data-ore-b`,d=/^ore:(\d+)$/,f=(e,t,n,r)=>{if(e.nodeType===Node.ELEMENT_NODE){let r=e,i=r.getAttribute(u);i!==null&&(n.set(Number(i),[...t]),r.removeAttribute(u))}else if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue,i=n===null?null:d.exec(n);i&&r.set(Number(i[1]),[...t])}let i=e.childNodes;for(let e=0;e<i.length;e++)f(i[e],[...t,e],n,r)},p=e=>{let n=l(e),r=``,i,a=0,s=0,c=[];for(let e=0;e<n.length-1;e++){let l=n[e],d=o(l);if(d.kind===t.NODE)r+=`${d.prefix}<!--ore:${s}-->`,c.push({commentId:s,kind:t.NODE}),s++,i=void 0;else{i===void 0||d.prefix.lastIndexOf(`<`)>d.prefix.lastIndexOf(`>`)?(i=a++,r+=`${d.prefix} ${u}="${i}"`):r+=d.prefix;let e=d.kind===t.BOOL_ATTR?`bool`:d.kind===t.ATTR?`attr`:void 0;c.push({elementId:i,kind:d.kind,mode:e,name:d.name})}}r+=n[n.length-1]??``;let d=document.createElement(`template`);d.innerHTML=r;let p=new Map,m=new Map,h=d.content.childNodes;for(let e=0;e<h.length;e++)f(h[e],[e],p,m);return{commentPaths:m,element:d,elementPaths:p,slots:c}},m=e=>{let t=s.get(e);return t||(t=p(e),s.set(e,t)),t},h=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};exports.SlotKind=t,exports.followPath=h,exports.getStaticTemplate=m;
2
2
  //# sourceMappingURL=compiler.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.cjs","names":[],"sources":["../../src/template/compiler.ts"],"sourcesContent":["/**\n * template/compiler.ts — HTML template string parser and static template cache.\n *\n * Responsibilities:\n * - Parse TemplateStringsArray into slot metadata (slot detection).\n * - Build a cached HTMLTemplateElement with path indices for efficient node lookup.\n * - Expose `getStaticTemplate()` for use by the instantiator.\n */\n\n// ─── Slot kinds ───────────────────────────────────────────────────────────────\n// Const object + derived union, same pattern as `ComponentPhase`/`LIFECYCLE_EVENTS`\n// in types.ts — used here (rather than plain string literals) because the kind\n// crosses a module boundary (compiler.ts produces it, instantiator.ts consumes\n// it): importing `SlotKind` gives autocomplete and a single rename point at the\n// consuming site, where a bare string literal wouldn't.\n\nexport const SlotKind = {\n ATTR: 'attr',\n BOOL_ATTR: 'boolAttr',\n EVENT: 'event',\n NODE: 'node',\n REF: 'ref',\n} as const;\n\nexport type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];\n\ntype DetectedSlot = {\n kind: DetectedSlotKind;\n name?: string;\n prefix: string;\n};\n\n// ─── Static template types ────────────────────────────────────────────────────\n\nexport type NodePath = readonly number[];\n\nexport type SlotMeta = {\n commentId?: number;\n elementId?: number;\n kind: DetectedSlotKind;\n mode?: 'attr' | 'bool';\n name?: string;\n};\n\nexport type CompiledStaticTemplate = {\n commentPaths: ReadonlyMap<number, NodePath>;\n element: HTMLTemplateElement;\n elementPaths: ReadonlyMap<number, NodePath>;\n slots: SlotMeta[];\n};\n\n// ─── Slot detection regexes ───────────────────────────────────────────────────\n\nconst EVENT_RE = /\\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\\s*=\\s*[\"']?$/;\nconst REF_RE = /\\s+ref\\s*=\\s*[\"']?$/;\nconst BOOL_ATTR_RE = /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\nconst ATTR_RE = /\\s+([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\n\nconst detectSlot = (str: string): DetectedSlot => {\n let m: RegExpExecArray | null;\n\n if ((m = EVENT_RE.exec(str))) {\n const prefix = str.slice(0, -m[0].length);\n const [name, ...modifiers] = m[1].split('.');\n\n if (modifiers.length > 0) throw new OreApiError(ORE_ERRORS.eventModifiersUnsupported(m[1]));\n\n return { kind: SlotKind.EVENT, name, prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: SlotKind.REF, prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: SlotKind.BOOL_ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: SlotKind.ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n const lastOpen = str.lastIndexOf('<');\n const lastClose = str.lastIndexOf('>');\n\n if (lastOpen > lastClose && str[lastOpen + 1] !== '/') {\n throw new OreApiError(ORE_ERRORS.templateInterpolationInTag);\n }\n\n return { kind: SlotKind.NODE, prefix: str };\n};\n\n// ─── Static template cache ────────────────────────────────────────────────────\n\nconst templateCache = new WeakMap<TemplateStringsArray, CompiledStaticTemplate>();\n\n/**\n * Matches a string that ends in an attribute-assignment context, e.g. `...attr=`,\n * `...@click=`, `...?disabled=`. Used together with tag-context\n * tracking (see below) to decide whether a quote immediately before an\n * interpolation is an attribute-value quote (strip it) or a literal text quote\n * (keep it) — previously every adjacent quote was stripped, so both\n * `` html`\"${value}\"` `` and prose like `area = \"${area}\"` lost their quotes.\n */\nconst ATTR_VALUE_CONTEXT_RE = /[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\\s*=\\s*$/;\n\n/**\n * Pre-process template strings to strip surrounding attribute quotes. This lets\n * the main loop operate on clean strings with no per-iteration state flags.\n *\n * Quote stripping requires BOTH an attr-assignment tail AND start-tag context\n * (tracked by replaying the raw strings): `class = \"${c}\"` inside a tag is\n * stripped; `area = \"${a}\"` in prose is not.\n */\nconst normalizeTemplateStrings = (strings: TemplateStringsArray): string[] => {\n const out = Array.from(strings);\n let insideTag = false;\n\n for (let i = 0; i < out.length - 1; i++) {\n const s = out[i];\n const lastChar = s[s.length - 1];\n\n // Tag context at the interpolation boundary is determined by all raw string\n // content up to it — including this string's own text before its final quote\n // (last angle bracket wins; attribute values containing '<'/'>' are outside\n // the supported syntax either way).\n for (const ch of s) {\n if (ch === '<') insideTag = true;\n else if (ch === '>') insideTag = false;\n }\n\n // Strip wrapping attribute quotes: attr=\"${value}\" → attr=${value}\n if ((lastChar === '\"' || lastChar === \"'\") && insideTag && ATTR_VALUE_CONTEXT_RE.test(s.slice(0, -1))) {\n out[i] = s.slice(0, -1);\n\n const next = out[i + 1];\n\n if (next.startsWith(lastChar)) out[i + 1] = next.slice(1);\n }\n }\n\n return out;\n};\n\n/**\n * Attribute names that mark a binding target element, and the comment prefix for\n * node-slot anchors. Namespaced (`data-ore-*` / `ore:N`) so user-authored markup in\n * static template regions can never collide with them — a plain `u` attribute or a\n * numeric comment was previously hijacked as a binding marker and stripped.\n */\nconst ELEMENT_MARKER_ATTR = 'data-ore-b';\nconst COMMENT_MARKER_RE = /^ore:(\\d+)$/;\n\nconst walkNode = (\n node: Node,\n path: number[],\n elementPaths: Map<number, NodePath>,\n commentPaths: Map<number, NodePath>,\n): void => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n const marker = el.getAttribute(ELEMENT_MARKER_ATTR);\n\n if (marker !== null) {\n elementPaths.set(Number(marker), [...path]);\n el.removeAttribute(ELEMENT_MARKER_ATTR);\n }\n } else if (node.nodeType === Node.COMMENT_NODE) {\n const content = (node as Comment).nodeValue;\n const m = content !== null ? COMMENT_MARKER_RE.exec(content) : null;\n\n if (m) {\n commentPaths.set(Number(m[1]), [...path]);\n }\n }\n\n const children = node.childNodes;\n\n for (let i = 0; i < children.length; i++) walkNode(children[i], [...path, i], elementPaths, commentPaths);\n};\n\nconst buildStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n const normalized = normalizeTemplateStrings(strings);\n let html = '';\n let activeElementId: number | undefined;\n let elementCounter = 0;\n let commentCounter = 0;\n const slots: SlotMeta[] = [];\n\n for (let i = 0; i < normalized.length - 1; i++) {\n const raw = normalized[i];\n const slot = detectSlot(raw);\n\n if (slot.kind === SlotKind.NODE) {\n html += slot.prefix + `<!--ore:${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: SlotKind.NODE });\n commentCounter++;\n activeElementId = undefined;\n } else {\n const needsNewMarker =\n activeElementId === undefined || slot.prefix.lastIndexOf('<') > slot.prefix.lastIndexOf('>');\n\n if (needsNewMarker) {\n activeElementId = elementCounter++;\n html += `${slot.prefix} ${ELEMENT_MARKER_ATTR}=\"${activeElementId}\"`;\n } else {\n html += slot.prefix;\n }\n\n const mode: 'attr' | 'bool' | undefined =\n slot.kind === SlotKind.BOOL_ATTR ? 'bool' : slot.kind === SlotKind.ATTR ? 'attr' : undefined;\n\n slots.push({ elementId: activeElementId, kind: slot.kind, mode, name: slot.name });\n }\n }\n\n html += normalized[normalized.length - 1] ?? '';\n\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const elementPaths = new Map<number, NodePath>();\n const commentPaths = new Map<number, NodePath>();\n const topChildren = tpl.content.childNodes;\n\n for (let i = 0; i < topChildren.length; i++) walkNode(topChildren[i], [i], elementPaths, commentPaths);\n\n return { commentPaths, element: tpl, elementPaths, slots };\n};\n\nexport const getStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n let tpl = templateCache.get(strings);\n\n if (!tpl) {\n tpl = buildStaticTemplate(strings);\n templateCache.set(strings, tpl);\n }\n\n return tpl;\n};\n\n// ─── Path navigation (used by instantiator) ───────────────────────────────────\n\nexport const followPath = (root: Node, path: NodePath): Node => {\n let node: Node = root;\n\n for (const i of path) node = node.childNodes[i];\n\n return node;\n};\nimport { OreApiError, ORE_ERRORS } from '../errors';\n"],"mappings":"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"}
1
+ {"version":3,"file":"compiler.cjs","names":[],"sources":["../../src/template/compiler.ts"],"sourcesContent":["/**\n * template/compiler.ts — HTML template string parser and static template cache.\n *\n * Responsibilities:\n * - Parse TemplateStringsArray into slot metadata (slot detection).\n * - Build a cached HTMLTemplateElement with path indices for efficient node lookup.\n * - Expose `getStaticTemplate()` for use by the instantiator.\n */\n\nimport { ORE_ERRORS, OreApiError } from '../errors';\n\n// ─── Slot kinds ───────────────────────────────────────────────────────────────\n// Const object + derived union, same pattern as `ComponentPhase`/`LIFECYCLE_EVENTS`\n// in types.ts — used here (rather than plain string literals) because the kind\n// crosses a module boundary (compiler.ts produces it, instantiator.ts consumes\n// it): importing `SlotKind` gives autocomplete and a single rename point at the\n// consuming site, where a bare string literal wouldn't.\n\nexport const SlotKind = {\n ATTR: 'attr',\n BOOL_ATTR: 'boolAttr',\n EVENT: 'event',\n NODE: 'node',\n REF: 'ref',\n} as const;\n\nexport type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];\n\ntype DetectedSlot = {\n kind: DetectedSlotKind;\n name?: string;\n prefix: string;\n};\n\n// ─── Static template types ────────────────────────────────────────────────────\n\nexport type NodePath = readonly number[];\n\nexport type SlotMeta = {\n commentId?: number;\n elementId?: number;\n kind: DetectedSlotKind;\n mode?: 'attr' | 'bool';\n name?: string;\n};\n\nexport type CompiledStaticTemplate = {\n commentPaths: ReadonlyMap<number, NodePath>;\n element: HTMLTemplateElement;\n elementPaths: ReadonlyMap<number, NodePath>;\n slots: SlotMeta[];\n};\n\n// ─── Slot detection regexes ───────────────────────────────────────────────────\n\nconst EVENT_RE = /\\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\\s*=\\s*[\"']?$/;\nconst REF_RE = /\\s+ref\\s*=\\s*[\"']?$/;\nconst BOOL_ATTR_RE = /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\nconst ATTR_RE = /\\s+([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\n\nconst detectSlot = (str: string): DetectedSlot => {\n let m: RegExpExecArray | null;\n\n if ((m = EVENT_RE.exec(str))) {\n const prefix = str.slice(0, -m[0].length);\n const [name, ...modifiers] = m[1].split('.');\n\n if (modifiers.length > 0) throw new OreApiError(ORE_ERRORS.eventModifiersUnsupported(m[1]));\n\n return { kind: SlotKind.EVENT, name, prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: SlotKind.REF, prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: SlotKind.BOOL_ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: SlotKind.ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n const lastOpen = str.lastIndexOf('<');\n const lastClose = str.lastIndexOf('>');\n\n if (lastOpen > lastClose && str[lastOpen + 1] !== '/') {\n throw new OreApiError(ORE_ERRORS.templateInterpolationInTag);\n }\n\n return { kind: SlotKind.NODE, prefix: str };\n};\n\n// ─── Static template cache ────────────────────────────────────────────────────\n\nconst templateCache = new WeakMap<TemplateStringsArray, CompiledStaticTemplate>();\n\n/**\n * Matches a string that ends in an attribute-assignment context, e.g. `...attr=`,\n * `...@click=`, `...?disabled=`. Used together with tag-context\n * tracking (see below) to decide whether a quote immediately before an\n * interpolation is an attribute-value quote (strip it) or a literal text quote\n * (keep it) — previously every adjacent quote was stripped, so both\n * `` html`\"${value}\"` `` and prose like `area = \"${area}\"` lost their quotes.\n */\nconst ATTR_VALUE_CONTEXT_RE = /[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\\s*=\\s*$/;\n\n/**\n * Pre-process template strings to strip surrounding attribute quotes. This lets\n * the main loop operate on clean strings with no per-iteration state flags.\n *\n * Quote stripping requires BOTH an attr-assignment tail AND start-tag context\n * (tracked by replaying the raw strings): `class = \"${c}\"` inside a tag is\n * stripped; `area = \"${a}\"` in prose is not.\n */\nconst normalizeTemplateStrings = (strings: TemplateStringsArray): string[] => {\n const out = Array.from(strings);\n let insideTag = false;\n\n for (let i = 0; i < out.length - 1; i++) {\n const s = out[i];\n const lastChar = s[s.length - 1];\n\n // Tag context at the interpolation boundary is determined by all raw string\n // content up to it — including this string's own text before its final quote\n // (last angle bracket wins; attribute values containing '<'/'>' are outside\n // the supported syntax either way).\n for (const ch of s) {\n if (ch === '<') insideTag = true;\n else if (ch === '>') insideTag = false;\n }\n\n // Strip wrapping attribute quotes: attr=\"${value}\" → attr=${value}\n if ((lastChar === '\"' || lastChar === \"'\") && insideTag && ATTR_VALUE_CONTEXT_RE.test(s.slice(0, -1))) {\n out[i] = s.slice(0, -1);\n\n const next = out[i + 1];\n\n if (next.startsWith(lastChar)) out[i + 1] = next.slice(1);\n }\n }\n\n return out;\n};\n\n/**\n * Attribute names that mark a binding target element, and the comment prefix for\n * node-slot anchors. Namespaced (`data-ore-*` / `ore:N`) so user-authored markup in\n * static template regions can never collide with them — a plain `u` attribute or a\n * numeric comment was previously hijacked as a binding marker and stripped.\n */\nconst ELEMENT_MARKER_ATTR = 'data-ore-b';\nconst COMMENT_MARKER_RE = /^ore:(\\d+)$/;\n\nconst walkNode = (\n node: Node,\n path: number[],\n elementPaths: Map<number, NodePath>,\n commentPaths: Map<number, NodePath>,\n): void => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n const marker = el.getAttribute(ELEMENT_MARKER_ATTR);\n\n if (marker !== null) {\n elementPaths.set(Number(marker), [...path]);\n el.removeAttribute(ELEMENT_MARKER_ATTR);\n }\n } else if (node.nodeType === Node.COMMENT_NODE) {\n const content = (node as Comment).nodeValue;\n const m = content !== null ? COMMENT_MARKER_RE.exec(content) : null;\n\n if (m) {\n commentPaths.set(Number(m[1]), [...path]);\n }\n }\n\n const children = node.childNodes;\n\n for (let i = 0; i < children.length; i++) walkNode(children[i], [...path, i], elementPaths, commentPaths);\n};\n\nconst buildStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n const normalized = normalizeTemplateStrings(strings);\n let html = '';\n let activeElementId: number | undefined;\n let elementCounter = 0;\n let commentCounter = 0;\n const slots: SlotMeta[] = [];\n\n for (let i = 0; i < normalized.length - 1; i++) {\n const raw = normalized[i];\n const slot = detectSlot(raw);\n\n if (slot.kind === SlotKind.NODE) {\n html += `${slot.prefix}<!--ore:${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: SlotKind.NODE });\n commentCounter++;\n activeElementId = undefined;\n } else {\n const needsNewMarker =\n activeElementId === undefined || slot.prefix.lastIndexOf('<') > slot.prefix.lastIndexOf('>');\n\n if (needsNewMarker) {\n activeElementId = elementCounter++;\n html += `${slot.prefix} ${ELEMENT_MARKER_ATTR}=\"${activeElementId}\"`;\n } else {\n html += slot.prefix;\n }\n\n const mode: 'attr' | 'bool' | undefined =\n slot.kind === SlotKind.BOOL_ATTR ? 'bool' : slot.kind === SlotKind.ATTR ? 'attr' : undefined;\n\n slots.push({ elementId: activeElementId, kind: slot.kind, mode, name: slot.name });\n }\n }\n\n html += normalized[normalized.length - 1] ?? '';\n\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const elementPaths = new Map<number, NodePath>();\n const commentPaths = new Map<number, NodePath>();\n const topChildren = tpl.content.childNodes;\n\n for (let i = 0; i < topChildren.length; i++) walkNode(topChildren[i], [i], elementPaths, commentPaths);\n\n return { commentPaths, element: tpl, elementPaths, slots };\n};\n\nexport const getStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n let tpl = templateCache.get(strings);\n\n if (!tpl) {\n tpl = buildStaticTemplate(strings);\n templateCache.set(strings, tpl);\n }\n\n return tpl;\n};\n\n// ─── Path navigation (used by instantiator) ───────────────────────────────────\n\nexport const followPath = (root: Node, path: NodePath): Node => {\n let node: Node = root;\n\n for (const i of path) node = node.childNodes[i];\n\n return node;\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,eAAO,MAAM,QAAQ;;;;;;CAMX,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAUxE,MAAM,MAAM,QAAQ,GAAG,SAAS,MAAM,EAAE,CAAC;AAEzC,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,OAAO,EAAE,mBAAmB,CAAC;IAC7B,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAsLF,eAAO,MAAM,iBAAiB,GAAI,SAAS,oBAAoB,KAAG,sBASjE,CAAC;AAIF,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,MAAM,QAAQ,KAAG,IAMvD,CAAC"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,eAAO,MAAM,QAAQ;;;;;;CAMX,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAUxE,MAAM,MAAM,QAAQ,GAAG,SAAS,MAAM,EAAE,CAAC;AAEzC,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,OAAO,EAAE,mBAAmB,CAAC;IAC7B,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAsLF,eAAO,MAAM,iBAAiB,GAAI,SAAS,oBAAoB,KAAG,sBASjE,CAAC;AAIF,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,MAAM,QAAQ,KAAG,IAMvD,CAAC"}
@@ -1,2 +1,2 @@
1
- import{ORE_ERRORS as e,OreApiError as t}from"../errors.js";var n={ATTR:`attr`,BOOL_ATTR:`boolAttr`,EVENT:`event`,NODE:`node`,REF:`ref`},r=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,i=/\s+ref\s*=\s*["']?$/,a=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,o=/\s+([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,s=s=>{let c;if(c=r.exec(s)){let r=s.slice(0,-c[0].length),[i,...a]=c[1].split(`.`);if(a.length>0)throw new t(e.eventModifiersUnsupported(c[1]));return{kind:n.EVENT,name:i,prefix:r}}if(c=i.exec(s))return{kind:n.REF,prefix:s.slice(0,-c[0].length)};if(c=a.exec(s))return{kind:n.BOOL_ATTR,name:c[1],prefix:s.slice(0,-c[0].length)};if(c=o.exec(s))return{kind:n.ATTR,name:c[1],prefix:s.slice(0,-c[0].length)};let l=s.lastIndexOf(`<`);if(l>s.lastIndexOf(`>`)&&s[l+1]!==`/`)throw new t(e.templateInterpolationInTag);return{kind:n.NODE,prefix:s}},c=new WeakMap,l=/[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\s*=\s*$/,u=e=>{let t=Array.from(e),n=!1;for(let e=0;e<t.length-1;e++){let r=t[e],i=r[r.length-1];for(let e of r)e===`<`?n=!0:e===`>`&&(n=!1);if((i===`"`||i===`'`)&&n&&l.test(r.slice(0,-1))){t[e]=r.slice(0,-1);let n=t[e+1];n.startsWith(i)&&(t[e+1]=n.slice(1))}}return t},d=`data-ore-b`,f=/^ore:(\d+)$/,p=(e,t,n,r)=>{if(e.nodeType===Node.ELEMENT_NODE){let r=e,i=r.getAttribute(d);i!==null&&(n.set(Number(i),[...t]),r.removeAttribute(d))}else if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue,i=n===null?null:f.exec(n);i&&r.set(Number(i[1]),[...t])}let i=e.childNodes;for(let e=0;e<i.length;e++)p(i[e],[...t,e],n,r)},m=e=>{let t=u(e),r=``,i,a=0,o=0,c=[];for(let e=0;e<t.length-1;e++){let l=t[e],u=s(l);if(u.kind===n.NODE)r+=u.prefix+`<!--ore:${o}-->`,c.push({commentId:o,kind:n.NODE}),o++,i=void 0;else{i===void 0||u.prefix.lastIndexOf(`<`)>u.prefix.lastIndexOf(`>`)?(i=a++,r+=`${u.prefix} ${d}="${i}"`):r+=u.prefix;let e=u.kind===n.BOOL_ATTR?`bool`:u.kind===n.ATTR?`attr`:void 0;c.push({elementId:i,kind:u.kind,mode:e,name:u.name})}}r+=t[t.length-1]??``;let l=document.createElement(`template`);l.innerHTML=r;let f=new Map,m=new Map,h=l.content.childNodes;for(let e=0;e<h.length;e++)p(h[e],[e],f,m);return{commentPaths:m,element:l,elementPaths:f,slots:c}},h=e=>{let t=c.get(e);return t||(t=m(e),c.set(e,t)),t},g=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};export{n as SlotKind,g as followPath,h as getStaticTemplate};
1
+ import{ORE_ERRORS as e,OreApiError as t}from"../errors.js";var n={ATTR:`attr`,BOOL_ATTR:`boolAttr`,EVENT:`event`,NODE:`node`,REF:`ref`},r=/\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\s*=\s*["']?$/,i=/\s+ref\s*=\s*["']?$/,a=/\s+\?([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,o=/\s+([a-zA-Z_][-a-zA-Z0-9_]*)\s*=\s*["']?$/,s=s=>{let c;if(c=r.exec(s)){let r=s.slice(0,-c[0].length),[i,...a]=c[1].split(`.`);if(a.length>0)throw new t(e.eventModifiersUnsupported(c[1]));return{kind:n.EVENT,name:i,prefix:r}}if(c=i.exec(s))return{kind:n.REF,prefix:s.slice(0,-c[0].length)};if(c=a.exec(s))return{kind:n.BOOL_ATTR,name:c[1],prefix:s.slice(0,-c[0].length)};if(c=o.exec(s))return{kind:n.ATTR,name:c[1],prefix:s.slice(0,-c[0].length)};let l=s.lastIndexOf(`<`);if(l>s.lastIndexOf(`>`)&&s[l+1]!==`/`)throw new t(e.templateInterpolationInTag);return{kind:n.NODE,prefix:s}},c=new WeakMap,l=/[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\s*=\s*$/,u=e=>{let t=Array.from(e),n=!1;for(let e=0;e<t.length-1;e++){let r=t[e],i=r[r.length-1];for(let e of r)e===`<`?n=!0:e===`>`&&(n=!1);if((i===`"`||i===`'`)&&n&&l.test(r.slice(0,-1))){t[e]=r.slice(0,-1);let n=t[e+1];n.startsWith(i)&&(t[e+1]=n.slice(1))}}return t},d=`data-ore-b`,f=/^ore:(\d+)$/,p=(e,t,n,r)=>{if(e.nodeType===Node.ELEMENT_NODE){let r=e,i=r.getAttribute(d);i!==null&&(n.set(Number(i),[...t]),r.removeAttribute(d))}else if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue,i=n===null?null:f.exec(n);i&&r.set(Number(i[1]),[...t])}let i=e.childNodes;for(let e=0;e<i.length;e++)p(i[e],[...t,e],n,r)},m=e=>{let t=u(e),r=``,i,a=0,o=0,c=[];for(let e=0;e<t.length-1;e++){let l=t[e],u=s(l);if(u.kind===n.NODE)r+=`${u.prefix}<!--ore:${o}-->`,c.push({commentId:o,kind:n.NODE}),o++,i=void 0;else{i===void 0||u.prefix.lastIndexOf(`<`)>u.prefix.lastIndexOf(`>`)?(i=a++,r+=`${u.prefix} ${d}="${i}"`):r+=u.prefix;let e=u.kind===n.BOOL_ATTR?`bool`:u.kind===n.ATTR?`attr`:void 0;c.push({elementId:i,kind:u.kind,mode:e,name:u.name})}}r+=t[t.length-1]??``;let l=document.createElement(`template`);l.innerHTML=r;let f=new Map,m=new Map,h=l.content.childNodes;for(let e=0;e<h.length;e++)p(h[e],[e],f,m);return{commentPaths:m,element:l,elementPaths:f,slots:c}},h=e=>{let t=c.get(e);return t||(t=m(e),c.set(e,t)),t},g=(e,t)=>{let n=e;for(let e of t)n=n.childNodes[e];return n};export{n as SlotKind,g as followPath,h as getStaticTemplate};
2
2
  //# sourceMappingURL=compiler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.js","names":[],"sources":["../../src/template/compiler.ts"],"sourcesContent":["/**\n * template/compiler.ts — HTML template string parser and static template cache.\n *\n * Responsibilities:\n * - Parse TemplateStringsArray into slot metadata (slot detection).\n * - Build a cached HTMLTemplateElement with path indices for efficient node lookup.\n * - Expose `getStaticTemplate()` for use by the instantiator.\n */\n\n// ─── Slot kinds ───────────────────────────────────────────────────────────────\n// Const object + derived union, same pattern as `ComponentPhase`/`LIFECYCLE_EVENTS`\n// in types.ts — used here (rather than plain string literals) because the kind\n// crosses a module boundary (compiler.ts produces it, instantiator.ts consumes\n// it): importing `SlotKind` gives autocomplete and a single rename point at the\n// consuming site, where a bare string literal wouldn't.\n\nexport const SlotKind = {\n ATTR: 'attr',\n BOOL_ATTR: 'boolAttr',\n EVENT: 'event',\n NODE: 'node',\n REF: 'ref',\n} as const;\n\nexport type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];\n\ntype DetectedSlot = {\n kind: DetectedSlotKind;\n name?: string;\n prefix: string;\n};\n\n// ─── Static template types ────────────────────────────────────────────────────\n\nexport type NodePath = readonly number[];\n\nexport type SlotMeta = {\n commentId?: number;\n elementId?: number;\n kind: DetectedSlotKind;\n mode?: 'attr' | 'bool';\n name?: string;\n};\n\nexport type CompiledStaticTemplate = {\n commentPaths: ReadonlyMap<number, NodePath>;\n element: HTMLTemplateElement;\n elementPaths: ReadonlyMap<number, NodePath>;\n slots: SlotMeta[];\n};\n\n// ─── Slot detection regexes ───────────────────────────────────────────────────\n\nconst EVENT_RE = /\\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\\s*=\\s*[\"']?$/;\nconst REF_RE = /\\s+ref\\s*=\\s*[\"']?$/;\nconst BOOL_ATTR_RE = /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\nconst ATTR_RE = /\\s+([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\n\nconst detectSlot = (str: string): DetectedSlot => {\n let m: RegExpExecArray | null;\n\n if ((m = EVENT_RE.exec(str))) {\n const prefix = str.slice(0, -m[0].length);\n const [name, ...modifiers] = m[1].split('.');\n\n if (modifiers.length > 0) throw new OreApiError(ORE_ERRORS.eventModifiersUnsupported(m[1]));\n\n return { kind: SlotKind.EVENT, name, prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: SlotKind.REF, prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: SlotKind.BOOL_ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: SlotKind.ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n const lastOpen = str.lastIndexOf('<');\n const lastClose = str.lastIndexOf('>');\n\n if (lastOpen > lastClose && str[lastOpen + 1] !== '/') {\n throw new OreApiError(ORE_ERRORS.templateInterpolationInTag);\n }\n\n return { kind: SlotKind.NODE, prefix: str };\n};\n\n// ─── Static template cache ────────────────────────────────────────────────────\n\nconst templateCache = new WeakMap<TemplateStringsArray, CompiledStaticTemplate>();\n\n/**\n * Matches a string that ends in an attribute-assignment context, e.g. `...attr=`,\n * `...@click=`, `...?disabled=`. Used together with tag-context\n * tracking (see below) to decide whether a quote immediately before an\n * interpolation is an attribute-value quote (strip it) or a literal text quote\n * (keep it) — previously every adjacent quote was stripped, so both\n * `` html`\"${value}\"` `` and prose like `area = \"${area}\"` lost their quotes.\n */\nconst ATTR_VALUE_CONTEXT_RE = /[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\\s*=\\s*$/;\n\n/**\n * Pre-process template strings to strip surrounding attribute quotes. This lets\n * the main loop operate on clean strings with no per-iteration state flags.\n *\n * Quote stripping requires BOTH an attr-assignment tail AND start-tag context\n * (tracked by replaying the raw strings): `class = \"${c}\"` inside a tag is\n * stripped; `area = \"${a}\"` in prose is not.\n */\nconst normalizeTemplateStrings = (strings: TemplateStringsArray): string[] => {\n const out = Array.from(strings);\n let insideTag = false;\n\n for (let i = 0; i < out.length - 1; i++) {\n const s = out[i];\n const lastChar = s[s.length - 1];\n\n // Tag context at the interpolation boundary is determined by all raw string\n // content up to it — including this string's own text before its final quote\n // (last angle bracket wins; attribute values containing '<'/'>' are outside\n // the supported syntax either way).\n for (const ch of s) {\n if (ch === '<') insideTag = true;\n else if (ch === '>') insideTag = false;\n }\n\n // Strip wrapping attribute quotes: attr=\"${value}\" → attr=${value}\n if ((lastChar === '\"' || lastChar === \"'\") && insideTag && ATTR_VALUE_CONTEXT_RE.test(s.slice(0, -1))) {\n out[i] = s.slice(0, -1);\n\n const next = out[i + 1];\n\n if (next.startsWith(lastChar)) out[i + 1] = next.slice(1);\n }\n }\n\n return out;\n};\n\n/**\n * Attribute names that mark a binding target element, and the comment prefix for\n * node-slot anchors. Namespaced (`data-ore-*` / `ore:N`) so user-authored markup in\n * static template regions can never collide with them — a plain `u` attribute or a\n * numeric comment was previously hijacked as a binding marker and stripped.\n */\nconst ELEMENT_MARKER_ATTR = 'data-ore-b';\nconst COMMENT_MARKER_RE = /^ore:(\\d+)$/;\n\nconst walkNode = (\n node: Node,\n path: number[],\n elementPaths: Map<number, NodePath>,\n commentPaths: Map<number, NodePath>,\n): void => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n const marker = el.getAttribute(ELEMENT_MARKER_ATTR);\n\n if (marker !== null) {\n elementPaths.set(Number(marker), [...path]);\n el.removeAttribute(ELEMENT_MARKER_ATTR);\n }\n } else if (node.nodeType === Node.COMMENT_NODE) {\n const content = (node as Comment).nodeValue;\n const m = content !== null ? COMMENT_MARKER_RE.exec(content) : null;\n\n if (m) {\n commentPaths.set(Number(m[1]), [...path]);\n }\n }\n\n const children = node.childNodes;\n\n for (let i = 0; i < children.length; i++) walkNode(children[i], [...path, i], elementPaths, commentPaths);\n};\n\nconst buildStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n const normalized = normalizeTemplateStrings(strings);\n let html = '';\n let activeElementId: number | undefined;\n let elementCounter = 0;\n let commentCounter = 0;\n const slots: SlotMeta[] = [];\n\n for (let i = 0; i < normalized.length - 1; i++) {\n const raw = normalized[i];\n const slot = detectSlot(raw);\n\n if (slot.kind === SlotKind.NODE) {\n html += slot.prefix + `<!--ore:${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: SlotKind.NODE });\n commentCounter++;\n activeElementId = undefined;\n } else {\n const needsNewMarker =\n activeElementId === undefined || slot.prefix.lastIndexOf('<') > slot.prefix.lastIndexOf('>');\n\n if (needsNewMarker) {\n activeElementId = elementCounter++;\n html += `${slot.prefix} ${ELEMENT_MARKER_ATTR}=\"${activeElementId}\"`;\n } else {\n html += slot.prefix;\n }\n\n const mode: 'attr' | 'bool' | undefined =\n slot.kind === SlotKind.BOOL_ATTR ? 'bool' : slot.kind === SlotKind.ATTR ? 'attr' : undefined;\n\n slots.push({ elementId: activeElementId, kind: slot.kind, mode, name: slot.name });\n }\n }\n\n html += normalized[normalized.length - 1] ?? '';\n\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const elementPaths = new Map<number, NodePath>();\n const commentPaths = new Map<number, NodePath>();\n const topChildren = tpl.content.childNodes;\n\n for (let i = 0; i < topChildren.length; i++) walkNode(topChildren[i], [i], elementPaths, commentPaths);\n\n return { commentPaths, element: tpl, elementPaths, slots };\n};\n\nexport const getStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n let tpl = templateCache.get(strings);\n\n if (!tpl) {\n tpl = buildStaticTemplate(strings);\n templateCache.set(strings, tpl);\n }\n\n return tpl;\n};\n\n// ─── Path navigation (used by instantiator) ───────────────────────────────────\n\nexport const followPath = (root: Node, path: NodePath): Node => {\n let node: Node = root;\n\n for (const i of path) node = node.childNodes[i];\n\n return node;\n};\nimport { OreApiError, ORE_ERRORS } from '../errors';\n"],"mappings":"2DAgBA,IAAa,EAAW,CACtB,KAAM,OACN,UAAW,WACX,MAAO,QACP,KAAM,OACN,IAAK,KACP,EA+BM,EAAW,+CACX,EAAS,sBACT,EAAe,8CACf,EAAU,4CAEV,EAAc,GAA8B,CAChD,IAAI,EAEJ,GAAK,EAAI,EAAS,KAAK,CAAG,EAAI,CAC5B,IAAM,EAAS,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,EAClC,CAAC,EAAM,GAAG,GAAa,EAAE,EAAE,CAAC,MAAM,GAAG,EAE3C,GAAI,EAAU,OAAS,EAAG,MAAM,IAAI,EAAY,EAAW,0BAA0B,EAAE,EAAE,CAAC,EAE1F,MAAO,CAAE,KAAM,EAAS,MAAO,OAAM,QAAO,CAC9C,CAEA,GAAK,EAAI,EAAO,KAAK,CAAG,EACtB,MAAO,CAAE,KAAM,EAAS,IAAK,OAAQ,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAE,EAGlE,GAAK,EAAI,EAAa,KAAK,CAAG,EAC5B,MAAO,CAAE,KAAM,EAAS,UAAW,KAAM,EAAE,GAAI,OAAQ,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAE,EAGpF,GAAK,EAAI,EAAQ,KAAK,CAAG,EACvB,MAAO,CAAE,KAAM,EAAS,KAAM,KAAM,EAAE,GAAI,OAAQ,EAAI,MAAM,EAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAE,EAG/E,IAAM,EAAW,EAAI,YAAY,GAAG,EAGpC,GAAI,EAFc,EAAI,YAAY,GAEnB,GAAa,EAAI,EAAW,KAAO,IAChD,MAAM,IAAI,EAAY,EAAW,0BAA0B,EAG7D,MAAO,CAAE,KAAM,EAAS,KAAM,OAAQ,CAAI,CAC5C,EAIM,EAAgB,IAAI,QAUpB,EAAwB,wCAUxB,EAA4B,GAA4C,CAC5E,IAAM,EAAM,MAAM,KAAK,CAAO,EAC1B,EAAY,GAEhB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,OAAS,EAAG,IAAK,CACvC,IAAM,EAAI,EAAI,GACR,EAAW,EAAE,EAAE,OAAS,GAM9B,IAAK,IAAM,KAAM,EACX,IAAO,IAAK,EAAY,GACnB,IAAO,MAAK,EAAY,IAInC,IAAK,IAAa,KAAO,IAAa,MAAQ,GAAa,EAAsB,KAAK,EAAE,MAAM,EAAG,EAAE,CAAC,EAAG,CACrG,EAAI,GAAK,EAAE,MAAM,EAAG,EAAE,EAEtB,IAAM,EAAO,EAAI,EAAI,GAEjB,EAAK,WAAW,CAAQ,IAAG,EAAI,EAAI,GAAK,EAAK,MAAM,CAAC,EAC1D,CACF,CAEA,OAAO,CACT,EAQM,EAAsB,aACtB,EAAoB,cAEpB,GACJ,EACA,EACA,EACA,IACS,CACT,GAAI,EAAK,WAAa,KAAK,aAAc,CACvC,IAAM,EAAK,EACL,EAAS,EAAG,aAAa,CAAmB,EAE9C,IAAW,OACb,EAAa,IAAI,OAAO,CAAM,EAAG,CAAC,GAAG,CAAI,CAAC,EAC1C,EAAG,gBAAgB,CAAmB,EAE1C,MAAO,GAAI,EAAK,WAAa,KAAK,aAAc,CAC9C,IAAM,EAAW,EAAiB,UAC5B,EAAI,IAAY,KAAyC,KAAlC,EAAkB,KAAK,CAAO,EAEvD,GACF,EAAa,IAAI,OAAO,EAAE,EAAE,EAAG,CAAC,GAAG,CAAI,CAAC,CAE5C,CAEA,IAAM,EAAW,EAAK,WAEtB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,EAAS,EAAS,GAAI,CAAC,GAAG,EAAM,CAAC,EAAG,EAAc,CAAY,CAC1G,EAEM,EAAuB,GAA0D,CACrF,IAAM,EAAa,EAAyB,CAAO,EAC/C,EAAO,GACP,EACA,EAAiB,EACjB,EAAiB,EACf,EAAoB,CAAC,EAE3B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,OAAS,EAAG,IAAK,CAC9C,IAAM,EAAM,EAAW,GACjB,EAAO,EAAW,CAAG,EAE3B,GAAI,EAAK,OAAS,EAAS,KACzB,GAAQ,EAAK,OAAS,WAAW,EAAe,KAChD,EAAM,KAAK,CAAE,UAAW,EAAgB,KAAM,EAAS,IAAK,CAAC,EAC7D,IACA,EAAkB,IAAA,OACb,CAEH,IAAoB,IAAA,IAAa,EAAK,OAAO,YAAY,GAAG,EAAI,EAAK,OAAO,YAAY,GAAG,GAG3F,EAAkB,IAClB,GAAQ,GAAG,EAAK,OAAO,GAAG,EAAoB,IAAI,EAAgB,IAElE,GAAQ,EAAK,OAGf,IAAM,EACJ,EAAK,OAAS,EAAS,UAAY,OAAS,EAAK,OAAS,EAAS,KAAO,OAAS,IAAA,GAErF,EAAM,KAAK,CAAE,UAAW,EAAiB,KAAM,EAAK,KAAM,OAAM,KAAM,EAAK,IAAK,CAAC,CACnF,CACF,CAEA,GAAQ,EAAW,EAAW,OAAS,IAAM,GAE7C,IAAM,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAY,EAEhB,IAAM,EAAe,IAAI,IACnB,EAAe,IAAI,IACnB,EAAc,EAAI,QAAQ,WAEhC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAY,OAAQ,IAAK,EAAS,EAAY,GAAI,CAAC,CAAC,EAAG,EAAc,CAAY,EAErG,MAAO,CAAE,eAAc,QAAS,EAAK,eAAc,OAAM,CAC3D,EAEa,EAAqB,GAA0D,CAC1F,IAAI,EAAM,EAAc,IAAI,CAAO,EAOnC,OALK,IACH,EAAM,EAAoB,CAAO,EACjC,EAAc,IAAI,EAAS,CAAG,GAGzB,CACT,EAIa,GAAc,EAAY,IAAyB,CAC9D,IAAI,EAAa,EAEjB,IAAK,IAAM,KAAK,EAAM,EAAO,EAAK,WAAW,GAE7C,OAAO,CACT"}
1
+ {"version":3,"file":"compiler.js","names":[],"sources":["../../src/template/compiler.ts"],"sourcesContent":["/**\n * template/compiler.ts — HTML template string parser and static template cache.\n *\n * Responsibilities:\n * - Parse TemplateStringsArray into slot metadata (slot detection).\n * - Build a cached HTMLTemplateElement with path indices for efficient node lookup.\n * - Expose `getStaticTemplate()` for use by the instantiator.\n */\n\nimport { ORE_ERRORS, OreApiError } from '../errors';\n\n// ─── Slot kinds ───────────────────────────────────────────────────────────────\n// Const object + derived union, same pattern as `ComponentPhase`/`LIFECYCLE_EVENTS`\n// in types.ts — used here (rather than plain string literals) because the kind\n// crosses a module boundary (compiler.ts produces it, instantiator.ts consumes\n// it): importing `SlotKind` gives autocomplete and a single rename point at the\n// consuming site, where a bare string literal wouldn't.\n\nexport const SlotKind = {\n ATTR: 'attr',\n BOOL_ATTR: 'boolAttr',\n EVENT: 'event',\n NODE: 'node',\n REF: 'ref',\n} as const;\n\nexport type DetectedSlotKind = (typeof SlotKind)[keyof typeof SlotKind];\n\ntype DetectedSlot = {\n kind: DetectedSlotKind;\n name?: string;\n prefix: string;\n};\n\n// ─── Static template types ────────────────────────────────────────────────────\n\nexport type NodePath = readonly number[];\n\nexport type SlotMeta = {\n commentId?: number;\n elementId?: number;\n kind: DetectedSlotKind;\n mode?: 'attr' | 'bool';\n name?: string;\n};\n\nexport type CompiledStaticTemplate = {\n commentPaths: ReadonlyMap<number, NodePath>;\n element: HTMLTemplateElement;\n elementPaths: ReadonlyMap<number, NodePath>;\n slots: SlotMeta[];\n};\n\n// ─── Slot detection regexes ───────────────────────────────────────────────────\n\nconst EVENT_RE = /\\s+@([a-zA-Z_][-a-zA-Z0-9_.-]*)\\s*=\\s*[\"']?$/;\nconst REF_RE = /\\s+ref\\s*=\\s*[\"']?$/;\nconst BOOL_ATTR_RE = /\\s+\\?([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\nconst ATTR_RE = /\\s+([a-zA-Z_][-a-zA-Z0-9_]*)\\s*=\\s*[\"']?$/;\n\nconst detectSlot = (str: string): DetectedSlot => {\n let m: RegExpExecArray | null;\n\n if ((m = EVENT_RE.exec(str))) {\n const prefix = str.slice(0, -m[0].length);\n const [name, ...modifiers] = m[1].split('.');\n\n if (modifiers.length > 0) throw new OreApiError(ORE_ERRORS.eventModifiersUnsupported(m[1]));\n\n return { kind: SlotKind.EVENT, name, prefix };\n }\n\n if ((m = REF_RE.exec(str))) {\n return { kind: SlotKind.REF, prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = BOOL_ATTR_RE.exec(str))) {\n return { kind: SlotKind.BOOL_ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n if ((m = ATTR_RE.exec(str))) {\n return { kind: SlotKind.ATTR, name: m[1], prefix: str.slice(0, -m[0].length) };\n }\n\n const lastOpen = str.lastIndexOf('<');\n const lastClose = str.lastIndexOf('>');\n\n if (lastOpen > lastClose && str[lastOpen + 1] !== '/') {\n throw new OreApiError(ORE_ERRORS.templateInterpolationInTag);\n }\n\n return { kind: SlotKind.NODE, prefix: str };\n};\n\n// ─── Static template cache ────────────────────────────────────────────────────\n\nconst templateCache = new WeakMap<TemplateStringsArray, CompiledStaticTemplate>();\n\n/**\n * Matches a string that ends in an attribute-assignment context, e.g. `...attr=`,\n * `...@click=`, `...?disabled=`. Used together with tag-context\n * tracking (see below) to decide whether a quote immediately before an\n * interpolation is an attribute-value quote (strip it) or a literal text quote\n * (keep it) — previously every adjacent quote was stripped, so both\n * `` html`\"${value}\"` `` and prose like `area = \"${area}\"` lost their quotes.\n */\nconst ATTR_VALUE_CONTEXT_RE = /[@?]?[a-zA-Z_][-a-zA-Z0-9_.]*\\s*=\\s*$/;\n\n/**\n * Pre-process template strings to strip surrounding attribute quotes. This lets\n * the main loop operate on clean strings with no per-iteration state flags.\n *\n * Quote stripping requires BOTH an attr-assignment tail AND start-tag context\n * (tracked by replaying the raw strings): `class = \"${c}\"` inside a tag is\n * stripped; `area = \"${a}\"` in prose is not.\n */\nconst normalizeTemplateStrings = (strings: TemplateStringsArray): string[] => {\n const out = Array.from(strings);\n let insideTag = false;\n\n for (let i = 0; i < out.length - 1; i++) {\n const s = out[i];\n const lastChar = s[s.length - 1];\n\n // Tag context at the interpolation boundary is determined by all raw string\n // content up to it — including this string's own text before its final quote\n // (last angle bracket wins; attribute values containing '<'/'>' are outside\n // the supported syntax either way).\n for (const ch of s) {\n if (ch === '<') insideTag = true;\n else if (ch === '>') insideTag = false;\n }\n\n // Strip wrapping attribute quotes: attr=\"${value}\" → attr=${value}\n if ((lastChar === '\"' || lastChar === \"'\") && insideTag && ATTR_VALUE_CONTEXT_RE.test(s.slice(0, -1))) {\n out[i] = s.slice(0, -1);\n\n const next = out[i + 1];\n\n if (next.startsWith(lastChar)) out[i + 1] = next.slice(1);\n }\n }\n\n return out;\n};\n\n/**\n * Attribute names that mark a binding target element, and the comment prefix for\n * node-slot anchors. Namespaced (`data-ore-*` / `ore:N`) so user-authored markup in\n * static template regions can never collide with them — a plain `u` attribute or a\n * numeric comment was previously hijacked as a binding marker and stripped.\n */\nconst ELEMENT_MARKER_ATTR = 'data-ore-b';\nconst COMMENT_MARKER_RE = /^ore:(\\d+)$/;\n\nconst walkNode = (\n node: Node,\n path: number[],\n elementPaths: Map<number, NodePath>,\n commentPaths: Map<number, NodePath>,\n): void => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n const marker = el.getAttribute(ELEMENT_MARKER_ATTR);\n\n if (marker !== null) {\n elementPaths.set(Number(marker), [...path]);\n el.removeAttribute(ELEMENT_MARKER_ATTR);\n }\n } else if (node.nodeType === Node.COMMENT_NODE) {\n const content = (node as Comment).nodeValue;\n const m = content !== null ? COMMENT_MARKER_RE.exec(content) : null;\n\n if (m) {\n commentPaths.set(Number(m[1]), [...path]);\n }\n }\n\n const children = node.childNodes;\n\n for (let i = 0; i < children.length; i++) walkNode(children[i], [...path, i], elementPaths, commentPaths);\n};\n\nconst buildStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n const normalized = normalizeTemplateStrings(strings);\n let html = '';\n let activeElementId: number | undefined;\n let elementCounter = 0;\n let commentCounter = 0;\n const slots: SlotMeta[] = [];\n\n for (let i = 0; i < normalized.length - 1; i++) {\n const raw = normalized[i];\n const slot = detectSlot(raw);\n\n if (slot.kind === SlotKind.NODE) {\n html += `${slot.prefix}<!--ore:${commentCounter}-->`;\n slots.push({ commentId: commentCounter, kind: SlotKind.NODE });\n commentCounter++;\n activeElementId = undefined;\n } else {\n const needsNewMarker =\n activeElementId === undefined || slot.prefix.lastIndexOf('<') > slot.prefix.lastIndexOf('>');\n\n if (needsNewMarker) {\n activeElementId = elementCounter++;\n html += `${slot.prefix} ${ELEMENT_MARKER_ATTR}=\"${activeElementId}\"`;\n } else {\n html += slot.prefix;\n }\n\n const mode: 'attr' | 'bool' | undefined =\n slot.kind === SlotKind.BOOL_ATTR ? 'bool' : slot.kind === SlotKind.ATTR ? 'attr' : undefined;\n\n slots.push({ elementId: activeElementId, kind: slot.kind, mode, name: slot.name });\n }\n }\n\n html += normalized[normalized.length - 1] ?? '';\n\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const elementPaths = new Map<number, NodePath>();\n const commentPaths = new Map<number, NodePath>();\n const topChildren = tpl.content.childNodes;\n\n for (let i = 0; i < topChildren.length; i++) walkNode(topChildren[i], [i], elementPaths, commentPaths);\n\n return { commentPaths, element: tpl, elementPaths, slots };\n};\n\nexport const getStaticTemplate = (strings: TemplateStringsArray): CompiledStaticTemplate => {\n let tpl = templateCache.get(strings);\n\n if (!tpl) {\n tpl = buildStaticTemplate(strings);\n templateCache.set(strings, tpl);\n }\n\n return tpl;\n};\n\n// ─── Path navigation (used by instantiator) ───────────────────────────────────\n\nexport const followPath = (root: Node, path: NodePath): Node => {\n let node: Node = root;\n\n for (const i of path) node = node.childNodes[i];\n\n return node;\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("./result.cjs");require("./binding-types.cjs");const n=require("./bindings.cjs"),r=require("./compiler.cjs");let i=require("@vielzeug/ripple");var a="html`...`: node-slot comment anchor has no parent node",o=e=>Array.isArray(e)?e:[e],s=(t,n,r)=>{let i=n.parentNode;for(e.invariant(i,a);t.fragment.firstChild;)i.insertBefore(t.fragment.firstChild,n);r.push(t.apply.bind(t))},c=(c,l)=>{let u=r.getStaticTemplate(c),d=u.element.content.cloneNode(!0),f=[],p=[],m=u.slots.map((t,n)=>{let i=l[n];if(t.kind===r.SlotKind.NODE){let n=t.commentId===void 0?void 0:u.commentPaths.get(t.commentId);return e.invariant(n,`compiled template is missing a comment path for node slot ${t.commentId}`),{comment:r.followPath(d,n),slot:t,value:i}}let a=t.elementId===void 0?void 0:u.elementPaths.get(t.elementId);return e.invariant(a,`compiled template is missing an element path for slot ${t.elementId}`),{el:r.followPath(d,a),slot:t,value:i}});for(let{comment:c,el:l,slot:u,value:d}of m){if(u.kind===r.SlotKind.NODE){let r=c;if(e.invariant(r,`compiled template produced a node slot without a comment anchor`),t.isDirectiveResult(d)){f.push({anchor:r,directive:d,type:`directive`});continue}if(t.isHtmlResult(d)){s(d,r,p),r.remove();continue}if(typeof d==`function`||(0,i.isReactive)(d)){let e=typeof d==`function`?(0,i.computed)(()=>o(d())):(0,i.computed)(()=>o(d.value));f.push({anchor:r,signal:e,type:`html`});continue}if(Array.isArray(d)){for(let i of d)if(t.isHtmlResult(i))s(i,r,p);else{let t=r.parentNode;e.invariant(t,a),t.insertBefore(document.createTextNode(n.resolveStaticText(i)),r)}r.remove();continue}r.replaceWith(document.createTextNode(n.resolveStaticText(d)));continue}if(e.invariant(l,`compiled template produced an element slot without an element`),u.kind===r.SlotKind.EVENT){let t=u.name;if(e.invariant(t,`compiled template produced an event slot without an event name`),typeof d==`function`)f.push({el:l,handler:d,name:t,type:`event`});else if((0,i.isReactive)(d)){let e=d;f.push({el:l,handler:t=>{let n=e.value;typeof n==`function`&&n(t)},name:t,type:`event`})}continue}if(u.kind===r.SlotKind.REF){d&&f.push({el:l,ref:d,type:`ref`});continue}e.invariant(u.name,`compiled template produced an attr slot without an attribute name`),f.push(n.createAttrBindingFromValue(l,u.mode??`attr`,u.name,d))}return t.createHtmlResult(d,e=>{for(let t of f)n.applyBinding(t,e);for(let t of p)t(e)})},l=(e,...t)=>c(e,t);exports.compileTemplate=c,exports.html=l;
1
+ const e=require("../errors.cjs"),t=require("./result.cjs"),n=require("./bindings.cjs"),r=require("./compiler.cjs");let i=require("@vielzeug/ripple");var a="html`...`: node-slot comment anchor has no parent node",o=e=>Array.isArray(e)?e:[e],s=(t,n,r)=>{let i=n.parentNode;for(e.invariant(i,a);t.fragment.firstChild;)i.insertBefore(t.fragment.firstChild,n);r.push(t.apply.bind(t))},c=(c,l)=>{let u=r.getStaticTemplate(c),d=u.element.content.cloneNode(!0),f=[],p=[],m=u.slots.map((t,n)=>{let i=l[n];if(t.kind===r.SlotKind.NODE){let n=t.commentId===void 0?void 0:u.commentPaths.get(t.commentId);return e.invariant(n,`compiled template is missing a comment path for node slot ${t.commentId}`),{comment:r.followPath(d,n),slot:t,value:i}}let a=t.elementId===void 0?void 0:u.elementPaths.get(t.elementId);return e.invariant(a,`compiled template is missing an element path for slot ${t.elementId}`),{el:r.followPath(d,a),slot:t,value:i}});for(let{comment:c,el:l,slot:u,value:d}of m){if(u.kind===r.SlotKind.NODE){let r=c;if(e.invariant(r,`compiled template produced a node slot without a comment anchor`),t.isDirectiveResult(d)){f.push({anchor:r,directive:d,type:`directive`});continue}if(t.isHtmlResult(d)){s(d,r,p),r.remove();continue}if(typeof d==`function`||(0,i.isReactive)(d)){let e=typeof d==`function`?(0,i.computed)(()=>o(d())):(0,i.computed)(()=>o(d.value));f.push({anchor:r,signal:e,type:`html`});continue}if(Array.isArray(d)){for(let i of d)if(t.isHtmlResult(i))s(i,r,p);else{let t=r.parentNode;e.invariant(t,a),t.insertBefore(document.createTextNode(n.resolveStaticText(i)),r)}r.remove();continue}r.replaceWith(document.createTextNode(n.resolveStaticText(d)));continue}if(e.invariant(l,`compiled template produced an element slot without an element`),u.kind===r.SlotKind.EVENT){let t=u.name;if(e.invariant(t,`compiled template produced an event slot without an event name`),typeof d==`function`)f.push({el:l,handler:d,name:t,type:`event`});else if((0,i.isReactive)(d)){let e=d;f.push({el:l,handler:t=>{let n=e.value;typeof n==`function`&&n(t)},name:t,type:`event`})}continue}if(u.kind===r.SlotKind.REF){d&&f.push({el:l,ref:d,type:`ref`});continue}e.invariant(u.name,`compiled template produced an attr slot without an attribute name`),f.push(n.createAttrBindingFromValue(l,u.mode??`attr`,u.name,d))}return t.createHtmlResult(d,e=>{for(let t of f)n.applyBinding(t,e);for(let t of p)t(e)})},l=(e,...t)=>c(e,t);exports.compileTemplate=c,exports.html=l;
2
2
  //# sourceMappingURL=instantiator.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"instantiator.cjs","names":[],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts — Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { type Binding, type HtmlBindingValue } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/** Normalize a reactive node-slot value to the HtmlBinding signal's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (\n result: CompiledHTMLResult,\n anchor: Comment,\n chainedApplies: Array<(rc: (fn: () => void) => void) => void>,\n): void => {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values — it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n const sig =\n typeof value === 'function'\n ? computed(() => toHtmlValues((value as () => unknown)()))\n : computed(() => toHtmlValues((value as Readable<unknown>).value));\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({ el, handler: value as (e: Event) => void, name, type: 'event' });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, (registerCleanup) => {\n for (const binding of bindings) applyBinding(binding, registerCleanup);\n for (const chainedApply of chainedApplies) chainedApply(registerCleanup);\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
1
+ {"version":3,"file":"instantiator.cjs","names":[],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts — Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport type { Binding, HtmlBindingValue } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/** Normalize a reactive node-slot value to the HtmlBinding signal's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (\n result: CompiledHTMLResult,\n anchor: Comment,\n chainedApplies: Array<(rc: (fn: () => void) => void) => void>,\n): void => {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values — it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n const sig =\n typeof value === 'function'\n ? computed(() => toHtmlValues((value as () => unknown)()))\n : computed(() => toHtmlValues((value as Readable<unknown>).value));\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({ el, handler: value as (e: Event) => void, name, type: 'event' });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, (registerCleanup) => {\n for (const binding of bindings) applyBinding(binding, registerCleanup);\n for (const chainedApply of chainedApplies) chainedApply(registerCleanup);\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{invariant as e}from"../errors.js";import{createHtmlResult as t,isDirectiveResult as n,isHtmlResult as r}from"./result.js";import"./binding-types.js";import{applyBinding as i,createAttrBindingFromValue as a,resolveStaticText as o}from"./bindings.js";import{SlotKind as s,followPath as c,getStaticTemplate as l}from"./compiler.js";import{computed as u,isReactive as d}from"@vielzeug/ripple";var f="html`...`: node-slot comment anchor has no parent node",p=e=>Array.isArray(e)?e:[e],m=(t,n,r)=>{let i=n.parentNode;for(e(i,f);t.fragment.firstChild;)i.insertBefore(t.fragment.firstChild,n);r.push(t.apply.bind(t))},h=(h,g)=>{let _=l(h),v=_.element.content.cloneNode(!0),y=[],b=[],x=_.slots.map((t,n)=>{let r=g[n];if(t.kind===s.NODE){let n=t.commentId===void 0?void 0:_.commentPaths.get(t.commentId);return e(n,`compiled template is missing a comment path for node slot ${t.commentId}`),{comment:c(v,n),slot:t,value:r}}let i=t.elementId===void 0?void 0:_.elementPaths.get(t.elementId);return e(i,`compiled template is missing an element path for slot ${t.elementId}`),{el:c(v,i),slot:t,value:r}});for(let{comment:t,el:i,slot:c,value:l}of x){if(c.kind===s.NODE){let i=t;if(e(i,`compiled template produced a node slot without a comment anchor`),n(l)){y.push({anchor:i,directive:l,type:`directive`});continue}if(r(l)){m(l,i,b),i.remove();continue}if(typeof l==`function`||d(l)){let e=u(typeof l==`function`?()=>p(l()):()=>p(l.value));y.push({anchor:i,signal:e,type:`html`});continue}if(Array.isArray(l)){for(let t of l)if(r(t))m(t,i,b);else{let n=i.parentNode;e(n,f),n.insertBefore(document.createTextNode(o(t)),i)}i.remove();continue}i.replaceWith(document.createTextNode(o(l)));continue}if(e(i,`compiled template produced an element slot without an element`),c.kind===s.EVENT){let t=c.name;if(e(t,`compiled template produced an event slot without an event name`),typeof l==`function`)y.push({el:i,handler:l,name:t,type:`event`});else if(d(l)){let e=l;y.push({el:i,handler:t=>{let n=e.value;typeof n==`function`&&n(t)},name:t,type:`event`})}continue}if(c.kind===s.REF){l&&y.push({el:i,ref:l,type:`ref`});continue}e(c.name,`compiled template produced an attr slot without an attribute name`),y.push(a(i,c.mode??`attr`,c.name,l))}return t(v,e=>{for(let t of y)i(t,e);for(let t of b)t(e)})},g=(e,...t)=>h(e,t);export{h as compileTemplate,g as html};
1
+ import{invariant as e}from"../errors.js";import{createHtmlResult as t,isDirectiveResult as n,isHtmlResult as r}from"./result.js";import{applyBinding as i,createAttrBindingFromValue as a,resolveStaticText as o}from"./bindings.js";import{SlotKind as s,followPath as c,getStaticTemplate as l}from"./compiler.js";import{computed as u,isReactive as d}from"@vielzeug/ripple";var f="html`...`: node-slot comment anchor has no parent node",p=e=>Array.isArray(e)?e:[e],m=(t,n,r)=>{let i=n.parentNode;for(e(i,f);t.fragment.firstChild;)i.insertBefore(t.fragment.firstChild,n);r.push(t.apply.bind(t))},h=(h,g)=>{let _=l(h),v=_.element.content.cloneNode(!0),y=[],b=[],x=_.slots.map((t,n)=>{let r=g[n];if(t.kind===s.NODE){let n=t.commentId===void 0?void 0:_.commentPaths.get(t.commentId);return e(n,`compiled template is missing a comment path for node slot ${t.commentId}`),{comment:c(v,n),slot:t,value:r}}let i=t.elementId===void 0?void 0:_.elementPaths.get(t.elementId);return e(i,`compiled template is missing an element path for slot ${t.elementId}`),{el:c(v,i),slot:t,value:r}});for(let{comment:t,el:i,slot:c,value:l}of x){if(c.kind===s.NODE){let i=t;if(e(i,`compiled template produced a node slot without a comment anchor`),n(l)){y.push({anchor:i,directive:l,type:`directive`});continue}if(r(l)){m(l,i,b),i.remove();continue}if(typeof l==`function`||d(l)){let e=u(typeof l==`function`?()=>p(l()):()=>p(l.value));y.push({anchor:i,signal:e,type:`html`});continue}if(Array.isArray(l)){for(let t of l)if(r(t))m(t,i,b);else{let n=i.parentNode;e(n,f),n.insertBefore(document.createTextNode(o(t)),i)}i.remove();continue}i.replaceWith(document.createTextNode(o(l)));continue}if(e(i,`compiled template produced an element slot without an element`),c.kind===s.EVENT){let t=c.name;if(e(t,`compiled template produced an event slot without an event name`),typeof l==`function`)y.push({el:i,handler:l,name:t,type:`event`});else if(d(l)){let e=l;y.push({el:i,handler:t=>{let n=e.value;typeof n==`function`&&n(t)},name:t,type:`event`})}continue}if(c.kind===s.REF){l&&y.push({el:i,ref:l,type:`ref`});continue}e(c.name,`compiled template produced an attr slot without an attribute name`),y.push(a(i,c.mode??`attr`,c.name,l))}return t(v,e=>{for(let t of y)i(t,e);for(let t of b)t(e)})},g=(e,...t)=>h(e,t);export{h as compileTemplate,g as html};
2
2
  //# sourceMappingURL=instantiator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"instantiator.js","names":[],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts — Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { type Binding, type HtmlBindingValue } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/** Normalize a reactive node-slot value to the HtmlBinding signal's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (\n result: CompiledHTMLResult,\n anchor: Comment,\n chainedApplies: Array<(rc: (fn: () => void) => void) => void>,\n): void => {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values — it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n const sig =\n typeof value === 'function'\n ? computed(() => toHtmlValues((value as () => unknown)()))\n : computed(() => toHtmlValues((value as Readable<unknown>).value));\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({ el, handler: value as (e: Event) => void, name, type: 'event' });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, (registerCleanup) => {\n for (const binding of bindings) applyBinding(binding, registerCleanup);\n for (const chainedApply of chainedApplies) chainedApply(registerCleanup);\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
1
+ {"version":3,"file":"instantiator.js","names":[],"sources":["../../src/template/instantiator.ts"],"sourcesContent":["/**\n * template/instantiator.ts — Template instantiation and the `html` tagged literal.\n *\n * Responsibilities:\n * - Clone a compiled static template and wire up live bindings.\n * - Expose `compileTemplate()` and `html` as the public authoring API.\n */\n\nimport { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport type { Binding, HtmlBindingValue } from './binding-types';\nimport { applyBinding, createAttrBindingFromValue, resolveStaticText } from './bindings';\nimport { followPath, getStaticTemplate, SlotKind } from './compiler';\nimport {\n type CompiledHTMLResult,\n createHtmlResult,\n type HTMLResult,\n isDirectiveResult,\n isHtmlResult,\n type Ref,\n type RefCallback,\n} from './result';\n\n// ─── Template instantiation ──────────────────────────────────────────────────\n\nconst NODE_SLOT_NO_PARENT_MSG = 'html`...`: node-slot comment anchor has no parent node';\n\n/** Normalize a reactive node-slot value to the HtmlBinding signal's array shape. */\nconst toHtmlValues = (raw: unknown): HtmlBindingValue[] =>\n Array.isArray(raw) ? (raw as HtmlBindingValue[]) : [raw as HtmlBindingValue];\n\n/**\n * Static-embed an already-created HTMLResult at a node-slot anchor: move its\n * fragment children into place and chain its apply into the outer apply phase\n * (so embedded reactive wiring starts when the host template mounts, not now).\n */\nconst embedStaticResult = (\n result: CompiledHTMLResult,\n anchor: Comment,\n chainedApplies: Array<(rc: (fn: () => void) => void) => void>,\n): void => {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n\n while (result.fragment.firstChild) parent.insertBefore(result.fragment.firstChild, anchor);\n\n chainedApplies.push(result.apply.bind(result));\n};\n\n/**\n * Instantiate a compiled template: clone the cached DOM template, navigate\n * to each binding target using pre-recorded paths, and build bindings with\n * direct node references. Returns an HTMLResult ready to mount.\n */\nexport const compileTemplate = (strings: TemplateStringsArray, values: unknown[]): HTMLResult => {\n const compiled = getStaticTemplate(strings);\n const fragment = compiled.element.content.cloneNode(true) as DocumentFragment;\n const bindings: Binding[] = [];\n // For static HTMLResult embeds: chain their apply calls\n const chainedApplies: Array<(rc: (fn: () => void) => void) => void> = [];\n\n // Phase 1: Resolve all binding targets BEFORE any DOM modifications\n type BoundSlot = { comment?: Comment; el?: HTMLElement; slot: (typeof compiled.slots)[number]; value: unknown };\n\n const boundSlots: BoundSlot[] = compiled.slots.map((slot, i) => {\n const value = values[i];\n\n if (slot.kind === SlotKind.NODE) {\n const commentPath = slot.commentId !== undefined ? compiled.commentPaths.get(slot.commentId) : undefined;\n\n invariant(commentPath, `compiled template is missing a comment path for node slot ${slot.commentId}`);\n\n return { comment: followPath(fragment, commentPath) as Comment, slot, value };\n }\n\n const elementPath = slot.elementId !== undefined ? compiled.elementPaths.get(slot.elementId) : undefined;\n\n invariant(elementPath, `compiled template is missing an element path for slot ${slot.elementId}`);\n\n return { el: followPath(fragment, elementPath) as HTMLElement, slot, value };\n });\n\n // Phase 2: Build bindings (may modify DOM for static content).\n for (const { comment, el, slot, value } of boundSlots) {\n if (slot.kind === SlotKind.NODE) {\n const anchor = comment;\n\n invariant(anchor, 'compiled template produced a node slot without a comment anchor');\n\n if (isDirectiveResult(value)) {\n bindings.push({ anchor, directive: value, type: 'directive' });\n continue;\n }\n\n if (isHtmlResult(value)) {\n // Static embed: move fragment children into place, chain apply\n embedStaticResult(value, anchor, chainedApplies);\n anchor.remove();\n continue;\n }\n\n if (typeof value === 'function' || isReactive(value)) {\n // Always use the html binding for reactive values — it handles both text\n // values and HTMLResult values, preventing silent \"[object Object]\"\n // corruption when a signal's runtime type changes from null/string to HTMLResult.\n const sig =\n typeof value === 'function'\n ? computed(() => toHtmlValues((value as () => unknown)()))\n : computed(() => toHtmlValues((value as Readable<unknown>).value));\n\n bindings.push({ anchor, signal: sig, type: 'html' });\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isHtmlResult(item)) {\n embedStaticResult(item, anchor, chainedApplies);\n } else {\n const parent = anchor.parentNode;\n\n invariant(parent, NODE_SLOT_NO_PARENT_MSG);\n parent.insertBefore(document.createTextNode(resolveStaticText(item)), anchor);\n }\n }\n anchor.remove();\n continue;\n }\n\n // Static primitive: replace with text node, no binding\n anchor.replaceWith(document.createTextNode(resolveStaticText(value)));\n continue;\n }\n\n // Element slot\n invariant(el, 'compiled template produced an element slot without an element');\n\n if (slot.kind === SlotKind.EVENT) {\n const name = slot.name;\n\n invariant(name, 'compiled template produced an event slot without an event name');\n\n if (typeof value === 'function') {\n bindings.push({ el, handler: value as (e: Event) => void, name, type: 'event' });\n } else if (isReactive(value)) {\n const signalValue = value as Readable<unknown>;\n const handler = (e: Event) => {\n const h = signalValue.value;\n\n if (typeof h === 'function') (h as (e: Event) => void)(e);\n };\n\n bindings.push({ el, handler, name, type: 'event' });\n }\n\n continue;\n }\n\n if (slot.kind === SlotKind.REF) {\n if (value) {\n bindings.push({ el, ref: value as Ref<Element> | RefCallback<Element>, type: 'ref' });\n }\n\n continue;\n }\n\n // attr / boolAttr\n invariant(slot.name, 'compiled template produced an attr slot without an attribute name');\n bindings.push(createAttrBindingFromValue(el, slot.mode ?? 'attr', slot.name, value));\n }\n\n return createHtmlResult(fragment, (registerCleanup) => {\n for (const binding of bindings) applyBinding(binding, registerCleanup);\n for (const chainedApply of chainedApplies) chainedApply(registerCleanup);\n });\n};\n\nexport const html = (strings: TemplateStringsArray, ...values: unknown[]): HTMLResult =>\n compileTemplate(strings, values);\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"result.cjs","names":[],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts — Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`) — see that module for why.\n */\n\nimport { signal, type Signal } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment, registerCleanup: (fn: () => void) => void) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function — only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\n/**\n * The output of an `html` tagged template call.\n *\n * Each `html` call produces an independent fragment — there is no shared mutable\n * state between instances, so the same template can be safely rendered multiple\n * times (e.g. inside `each()`).\n *\n * `mount()` is the entire public surface: insertion and reactive wiring in one\n * step, so the two can never get out of sync (the old public `fragment` + `apply`\n * pair made it possible to insert without wiring, or wire without inserting).\n */\nexport interface HTMLResult {\n /**\n * Insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null) and wire up all reactive effects. Returns the inserted\n * nodes so callers can remove them later.\n */\n mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];\n}\n\n/**\n * @internal The full result the template engine itself works with. `fragment` and\n * `apply` are the engine's own two-phase insertion protocol (static-embed merging in\n * the instantiator, `insertHtmlValues` in the binding layer) — not part of the\n * public API. Consumers only ever see `HTMLResult`.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(registerCleanup: (fn: () => void) => void): void;\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\nexport function createHtmlResult(\n fragment: DocumentFragment,\n applyFn: (registerCleanup: (fn: () => void) => void) => void,\n): CompiledHTMLResult {\n const mount = (parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[] => {\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn(registerCleanup);\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, fragment, mount });\n}\n"],"mappings":"wEAgBA,SAAgB,GAAiC,CAC/C,OAAA,EAAO,EAAA,OAAA,CAAiB,IAAI,CAC9B,CAUA,IAAM,EAAiB,EAAA,UAA2B,eAAe,EAMpD,EAAyB,GACpC,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnB,EAAoB,EAAe,GAqC1C,EAAkB,EAAA,UAA8B,iBAAiB,EAE1D,EAAe,EAAgB,GAE5C,SAAgB,EACd,EACA,EACoB,CAUpB,OAAO,EAAgB,MAAM,CAAE,MAAO,EAAS,WAAU,OAT1C,EAAoB,EAAqB,IAAsD,CAC5G,IAAM,EAAQ,MAAM,KAAK,EAAS,UAAU,EAK5C,OAHA,EAAO,aAAa,EAAU,CAAM,EACpC,EAAQ,CAAe,EAEhB,CACT,CAE+D,CAAC,CAClE"}
1
+ {"version":3,"file":"result.cjs","names":[],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts — Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`) — see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment, registerCleanup: (fn: () => void) => void) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function — only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\n/**\n * The output of an `html` tagged template call.\n *\n * Each `html` call produces an independent fragment — there is no shared mutable\n * state between instances, so the same template can be safely rendered multiple\n * times (e.g. inside `each()`).\n *\n * `mount()` is the entire public surface: insertion and reactive wiring in one\n * step, so the two can never get out of sync (the old public `fragment` + `apply`\n * pair made it possible to insert without wiring, or wire without inserting).\n */\nexport interface HTMLResult {\n /**\n * Insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null) and wire up all reactive effects. Returns the inserted\n * nodes so callers can remove them later.\n */\n mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];\n}\n\n/**\n * @internal The full result the template engine itself works with. `fragment` and\n * `apply` are the engine's own two-phase insertion protocol (static-embed merging in\n * the instantiator, `insertHtmlValues` in the binding layer) — not part of the\n * public API. Consumers only ever see `HTMLResult`.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(registerCleanup: (fn: () => void) => void): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\nexport function createHtmlResult(\n fragment: DocumentFragment,\n applyFn: (registerCleanup: (fn: () => void) => void) => void,\n): CompiledHTMLResult {\n const mount = (parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[] => {\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn(registerCleanup);\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, fragment, mount });\n}\n"],"mappings":"wEAgBA,SAAgB,GAAiC,CAC/C,OAAA,EAAO,EAAA,OAAA,CAAiB,IAAI,CAC9B,CAUA,IAAM,EAAiB,EAAA,UAA2B,eAAe,EAMpD,EAAyB,GACpC,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnB,EAAoB,EAAe,GAqC1C,EAAkB,EAAA,UAA8B,iBAAiB,EAE1D,EAAe,EAAgB,GAE5C,SAAgB,EACd,EACA,EACoB,CAUpB,OAAO,EAAgB,MAAM,CAAE,MAAO,EAAS,WAAU,OAT1C,EAAoB,EAAqB,IAAsD,CAC5G,IAAM,EAAQ,MAAM,KAAK,EAAS,UAAU,EAK5C,OAHA,EAAO,aAAa,EAAU,CAAM,EACpC,EAAQ,CAAe,EAEhB,CACT,CAE+D,CAAC,CAClE"}
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/template/result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAMvD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,IAAI,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAEtD,wBAAgB,GAAG,CAAC,CAAC,SAAS,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAE/C;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;AAIpE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;CAC7E,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,eAAe,CAAC,OAAO,CAAC,KAAG,eACvC,CAAC;AAElC,eAAO,MAAM,iBAAiB,8CAAoB,CAAC;AAInD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;CACnG;AAiBD,eAAO,MAAM,YAAY,iDAAqB,CAAC;AAE/C,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,GAC3D,kBAAkB,CAWpB"}
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/template/result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;AAMvD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,IAAI,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAEtD,wBAAgB,GAAG,CAAC,CAAC,SAAS,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAE/C;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;AAIpE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;CAC7E,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,eAAe,CAAC,OAAO,CAAC,KAAG,eACvC,CAAC;AAElC,eAAO,MAAM,iBAAiB,8CAAoB,CAAC;AAInD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;CACnG;AAiBD,eAAO,MAAM,YAAY,iDAAqB,CAAC;AAE/C,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,GAC3D,kBAAkB,CAWpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"result.js","names":[],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts — Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`) — see that module for why.\n */\n\nimport { signal, type Signal } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment, registerCleanup: (fn: () => void) => void) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function — only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\n/**\n * The output of an `html` tagged template call.\n *\n * Each `html` call produces an independent fragment — there is no shared mutable\n * state between instances, so the same template can be safely rendered multiple\n * times (e.g. inside `each()`).\n *\n * `mount()` is the entire public surface: insertion and reactive wiring in one\n * step, so the two can never get out of sync (the old public `fragment` + `apply`\n * pair made it possible to insert without wiring, or wire without inserting).\n */\nexport interface HTMLResult {\n /**\n * Insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null) and wire up all reactive effects. Returns the inserted\n * nodes so callers can remove them later.\n */\n mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];\n}\n\n/**\n * @internal The full result the template engine itself works with. `fragment` and\n * `apply` are the engine's own two-phase insertion protocol (static-embed merging in\n * the instantiator, `insertHtmlValues` in the binding layer) — not part of the\n * public API. Consumers only ever see `HTMLResult`.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(registerCleanup: (fn: () => void) => void): void;\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\nexport function createHtmlResult(\n fragment: DocumentFragment,\n applyFn: (registerCleanup: (fn: () => void) => void) => void,\n): CompiledHTMLResult {\n const mount = (parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[] => {\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn(registerCleanup);\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, fragment, mount });\n}\n"],"mappings":"wFAgBA,SAAgB,GAAiC,CAC/C,OAAO,EAAiB,IAAI,CAC9B,CAUA,IAAM,EAAiB,EAA2B,eAAe,EAMpD,EAAyB,GACpC,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnB,EAAoB,EAAe,GAqC1C,EAAkB,EAA8B,iBAAiB,EAE1D,EAAe,EAAgB,GAE5C,SAAgB,EACd,EACA,EACoB,CAUpB,OAAO,EAAgB,MAAM,CAAE,MAAO,EAAS,WAAU,OAT1C,EAAoB,EAAqB,IAAsD,CAC5G,IAAM,EAAQ,MAAM,KAAK,EAAS,UAAU,EAK5C,OAHA,EAAO,aAAa,EAAU,CAAM,EACpC,EAAQ,CAAe,EAEhB,CACT,CAE+D,CAAC,CAClE"}
1
+ {"version":3,"file":"result.js","names":[],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts — Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`) — see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment, registerCleanup: (fn: () => void) => void) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function — only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\n/**\n * The output of an `html` tagged template call.\n *\n * Each `html` call produces an independent fragment — there is no shared mutable\n * state between instances, so the same template can be safely rendered multiple\n * times (e.g. inside `each()`).\n *\n * `mount()` is the entire public surface: insertion and reactive wiring in one\n * step, so the two can never get out of sync (the old public `fragment` + `apply`\n * pair made it possible to insert without wiring, or wire without inserting).\n */\nexport interface HTMLResult {\n /**\n * Insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null) and wire up all reactive effects. Returns the inserted\n * nodes so callers can remove them later.\n */\n mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];\n}\n\n/**\n * @internal The full result the template engine itself works with. `fragment` and\n * `apply` are the engine's own two-phase insertion protocol (static-embed merging in\n * the instantiator, `insertHtmlValues` in the binding layer) — not part of the\n * public API. Consumers only ever see `HTMLResult`.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(registerCleanup: (fn: () => void) => void): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\nexport function createHtmlResult(\n fragment: DocumentFragment,\n applyFn: (registerCleanup: (fn: () => void) => void) => void,\n): CompiledHTMLResult {\n const mount = (parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[] => {\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn(registerCleanup);\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, fragment, mount });\n}\n"],"mappings":"wFAgBA,SAAgB,GAAiC,CAC/C,OAAO,EAAiB,IAAI,CAC9B,CAUA,IAAM,EAAiB,EAA2B,eAAe,EAMpD,EAAyB,GACpC,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnB,EAAoB,EAAe,GAqC1C,EAAkB,EAA8B,iBAAiB,EAE1D,EAAe,EAAgB,GAE5C,SAAgB,EACd,EACA,EACoB,CAUpB,OAAO,EAAgB,MAAM,CAAE,MAAO,EAAS,WAAU,OAT1C,EAAoB,EAAqB,IAAsD,CAC5G,IAAM,EAAQ,MAAM,KAAK,EAAS,UAAU,EAK5C,OAHA,EAAO,aAAa,EAAU,CAAM,EACpC,EAAQ,CAAe,EAEhB,CACT,CAE+D,CAAC,CAClE"}
@@ -7,11 +7,11 @@
7
7
  * For tree-shaking, import directly from the focused sub-modules.
8
8
  */
9
9
  export { OreTimeoutError } from '../errors';
10
- export { debugFlush, flush, type FlushOptions } from './flush';
11
10
  export { walkFlatTree } from './dom';
11
+ export { debugFlush, type FlushOptions, flush } from './flush';
12
12
  export { installFormInternalsPolyfill } from './form-internals-polyfill';
13
- export { cleanup, mock, mount, mountComponent, type Fixture, type MountOptions, type MountSetup } from './mount';
14
- export { renderHook, type HookFixture } from './render-hook';
13
+ export { cleanup, type Fixture, type MountOptions, type MountSetup, mock, mount, mountComponent } from './mount';
14
+ export { type HookFixture, renderHook } from './render-hook';
15
15
  export { resetOreForTests } from './reset';
16
16
  export type InstallOptions = {
17
17
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACrC,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AACjH,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,GAAE,cAAmB,GAAG,IAAI,CAInG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjH,OAAO,EAAE,KAAK,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,GAAE,cAAmB,GAAG,IAAI,CAInG"}
@@ -1,2 +1,2 @@
1
- const e=require("../utils/dom.cjs"),t=require("../runtime.cjs");require("../template/result.cjs");const n=require("../define.cjs"),r=require("./flush.cjs"),i=require("./render-hook.cjs"),a=require("./reset.cjs");let o=require("@vielzeug/assay");var s=[],c=`ore-test-inline-fixture`,l=`ore-test-form-inline-fixture`,u=new WeakMap,d=()=>{let e=u.get(t.getHost());if(!e)throw Error(`Inline fixture setup is unavailable.`);return e},f=(e,t)=>{customElements.get(e)||n.define(e,{formAssociated:t,setup:()=>d()({})})},p=e=>{if(!e||Object.keys(e).length===0)return c;if(Object.keys(e).length===1&&e.formAssociated===!0)return l},m=0;function h(t,n,r){e.setAttr(t,n,r)}var g=e=>e instanceof Error?e:Error(String(e)),_=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=g(e.error??e.message),e.preventDefault()},r=e=>{t=g(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function v(e,t={}){let{attrs:i={},componentOptions:a,container:c=document.body,html:d,props:g={}}=t,v,y,b;if(typeof e==`string`)v=e;else{let t=p(a);t?(f(t,t===l),b=e,v=t):(v=`trial-${++m}`,y={...a??{},setup:e})}y&&n.define(v,y);let x=document.createElement(v);b&&u.set(x,b),d&&(x.innerHTML=d),Object.keys(g).length&&Object.assign(x,g);for(let[e,t]of Object.entries(i))h(x,e,t);try{await _(async()=>{c.appendChild(x),s.push(x),await r.flush()})}catch(e){x.remove(),u.delete(x);let t=s.indexOf(x);throw t!==-1&&s.splice(t,1),e}let S=!1;function C(){if(S)return;S=!0,x.remove(),u.delete(x);let e=s.indexOf(x);e!==-1&&s.splice(e,1)}return{...(0,o.within)(x.shadowRoot??x),async act(e){await e(),await r.flush()},async attr(e,t){h(x,e,t),await r.flush()},async attrs(e){for(let[t,n]of Object.entries(e))h(x,t,n);await r.flush()},dispose:C,get disposed(){return S},element:x,flush:r.flush,get shadow(){return x.shadowRoot},[Symbol.dispose](){C()}}}async function y(e,t,r){return n.define(e,t),v(e,r)}function b(e,t=``){customElements.get(e)||customElements.define(e,class extends HTMLElement{connectedCallback(){this.innerHTML=t}})}function x(){for(let e of s)e.remove();s.length=0,i._disposeRenderHooks(),a.resetOreForTests()}exports._mountedElements=s,exports.cleanup=x,exports.mock=b,exports.mount=v,exports.mountComponent=y;
1
+ const e=require("../utils/dom.cjs"),t=require("../define.cjs"),n=require("./flush.cjs"),r=require("./render-hook.cjs"),i=require("./reset.cjs");let a=require("@vielzeug/assay");var o=[],s=0;function c(t,n,r){e.setAttr(t,n,r)}var l=e=>e instanceof Error?e:Error(String(e)),u=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=l(e.error??e.message),e.preventDefault()},r=e=>{t=l(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function d(e,r={}){let{attrs:i={},componentOptions:l,container:d=document.body,html:f,props:p={}}=r,m,h;typeof e==`string`?m=e:(m=`trial-${++s}`,h={...l??{},setup:e}),h&&t.define(m,h);let g=document.createElement(m);f&&(g.innerHTML=f),Object.keys(p).length&&Object.assign(g,p);for(let[e,t]of Object.entries(i))c(g,e,t);try{await u(async()=>{d.appendChild(g),o.push(g),await n.flush()})}catch(e){g.remove();let t=o.indexOf(g);throw t!==-1&&o.splice(t,1),e}let _=!1;function v(){if(_)return;_=!0,g.remove();let e=o.indexOf(g);e!==-1&&o.splice(e,1)}return{...(0,a.within)(g.shadowRoot??g),async act(e){await e(),await n.flush()},async attr(e,t){c(g,e,t),await n.flush()},async attrs(e){for(let[t,n]of Object.entries(e))c(g,t,n);await n.flush()},dispose:v,get disposed(){return _},element:g,flush:n.flush,get shadow(){return g.shadowRoot},[Symbol.dispose](){v()}}}async function f(e,n,r){return t.define(e,n),d(e,r)}function p(e,t=``){customElements.get(e)||customElements.define(e,class extends HTMLElement{connectedCallback(){this.innerHTML=t}})}function m(){for(let e of o)e.remove();o.length=0,r._disposeRenderHooks(),i.resetOreForTests()}exports._mountedElements=o,exports.cleanup=m,exports.mock=p,exports.mount=d,exports.mountComponent=f;
2
2
  //# sourceMappingURL=mount.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mount.cjs","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport type { Readable } from '@vielzeug/ripple';\n\nimport { type QueryScope, within } from '@vielzeug/assay';\n\nimport { type ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport { getHost } from '../runtime';\nimport { type HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { flush, type FlushOptions } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\nconst INLINE_FIXTURE_TAG = 'ore-test-inline-fixture';\nconst FORM_INLINE_FIXTURE_TAG = 'ore-test-form-inline-fixture';\n\n// Custom-element registrations are permanent. The common no-prop fixture shapes\n// therefore reuse one registered class and keep each setup function weakly owned\n// by its element instead of registering a new trial-N class for every test.\nconst inlineSetups = new WeakMap<HTMLElement, MountSetup>();\n\nconst getInlineSetup = (): MountSetup => {\n const setup = inlineSetups.get(getHost());\n\n if (!setup) throw new Error('Inline fixture setup is unavailable.');\n\n return setup;\n};\n\nconst ensureInlineFixture = (tag: string, formAssociated: boolean): void => {\n if (customElements.get(tag)) return;\n\n define(tag, {\n formAssociated,\n setup: () => getInlineSetup()({}),\n });\n};\n\nconst getReusableInlineFixtureTag = (options: MountOptions['componentOptions']): string | undefined => {\n if (!options || Object.keys(options).length === 0) return INLINE_FIXTURE_TAG;\n\n if (Object.keys(options).length === 1 && options.formAssociated === true) return FORM_INLINE_FIXTURE_TAG;\n\n return undefined;\n};\n\n// Monotonic across the whole test run — never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n let inlineSetup: MountSetup | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n const reusableTag = getReusableInlineFixtureTag(componentOptions);\n\n if (reusableTag) {\n const formAssociated = reusableTag === FORM_INLINE_FIXTURE_TAG;\n\n ensureInlineFixture(reusableTag, formAssociated);\n inlineSetup = tagOrSetup;\n tagName = reusableTag;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (inlineSetup) inlineSetups.set(element, inlineSetup);\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n inlineSetups.delete(element);\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n inlineSetups.delete(element);\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<Props extends Record<string, unknown>, T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<Props>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
1
+ {"version":3,"file":"mount.cjs","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run — never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<Props extends Record<string, unknown>, T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<Props>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
@@ -1,18 +1,16 @@
1
1
  /**
2
2
  * Component mounting utilities for test environments.
3
3
  */
4
- import type { Readable } from '@vielzeug/ripple';
5
4
  import { type QueryScope } from '@vielzeug/assay';
6
- import { type ComponentDefinition } from '../component-types';
7
- import { type HTMLResult } from '../template/result';
5
+ import type { Readable } from '@vielzeug/ripple';
6
+ import type { ComponentDefinition } from '../component-types';
7
+ import type { HTMLResult } from '../template/result';
8
8
  import { type FlushOptions } from './flush';
9
9
  /**
10
10
  * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to
11
11
  * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).
12
12
  */
13
13
  export interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {
14
- /** Delegates to `dispose()`. Enables `using` declarations. */
15
- [Symbol.dispose](): void;
16
14
  /** Run a callback then flush — the standard way to trigger and assert a reactive update */
17
15
  act(fn: () => unknown): Promise<void>;
18
16
  /** Set an attribute (boolean `false` removes it) then flush */
@@ -29,18 +27,20 @@ export interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope
29
27
  flush(options?: FlushOptions): Promise<void>;
30
28
  /** The component's shadow root (null for light-DOM components) */
31
29
  readonly shadow: ShadowRoot | null;
30
+ /** Delegates to `dispose()`. Enables `using` declarations. */
31
+ [Symbol.dispose](): void;
32
32
  }
33
33
  export interface MountOptions {
34
- /** Properties assigned directly onto the element */
35
- props?: Record<string, unknown>;
36
34
  /** HTML attributes to set on the element */
37
35
  attrs?: Record<string, string | number | boolean>;
38
- /** Inner HTML for slot content */
39
- html?: string;
40
- /** Parent container (default: document.body) */
41
- container?: HTMLElement;
42
36
  /** Extra component options when passing an inline setup function */
43
37
  componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;
38
+ /** Parent container (default: document.body) */
39
+ container?: HTMLElement;
40
+ /** Inner HTML for slot content */
41
+ html?: string;
42
+ /** Properties assigned directly onto the element */
43
+ props?: Record<string, unknown>;
44
44
  }
45
45
  type MountProps = {
46
46
  readonly [x: string]: Readable<unknown>;