@vielzeug/ore 26.11.7 → 26.11.9

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.
package/dist/slots.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./runtime.cjs");let t=require("@vielzeug/ripple");var n=`default`,r=e=>e||n,i=n=>{let i=new Map,o=new Map,s=new Map,c=e=>{let n=i.get(e);return n||(n={elements:(0,t.signal)([]),presence:(0,t.signal)(!1)},i.set(e,n)),n},l=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0},u=e=>{let t=r(e),n=o.get(t),i=[];if(n)for(let e of n)i.push(...e.assignedElements({flatten:!0}));let a=c(t);l(a.elements.value,i)||(a.elements.value=i);let s=i.length>0;a.presence.value!==s&&(a.presence.value=s)},d=e=>{if(s.has(e))return;let t=r(e.getAttribute(`name`)),n=o.get(t)??new Set;n.add(e),o.set(t,n);let i=()=>u(t);e.addEventListener(`slotchange`,i),s.set(e,()=>{e.removeEventListener(`slotchange`,i)}),u(t)},f=e=>{let t=s.get(e);if(!t)return;t(),s.delete(e);let n=r(e.getAttribute(`name`)),i=o.get(n);i&&(i.delete(e),i.size===0&&o.delete(n)),u(n)},p=()=>{n.shadowRoot?.querySelectorAll(`slot`).forEach(e=>{d(e)})},m=()=>{for(let e of o.keys())u(e)},h=null;return e.onMounted(()=>{p(),m(),!h&&n.shadowRoot&&(h=new MutationObserver(e=>{for(let t of e)for(let e of t.removedNodes)e instanceof HTMLSlotElement&&f(e);p(),s.size>0&&m()}),h.observe(n.shadowRoot,{childList:!0,subtree:!0}))}),e.onCleanup(()=>{h?.disconnect(),h=null;for(let e of s.values())e();s.clear(),o.clear(),i.clear(),a.delete(n)}),{elements:e=>c(r(e)).elements,has:e=>c(r(e)).presence}},a=new WeakMap,o=()=>{let t=e.requireSetupContext(`useSlots`),n=a.get(t.element);return n||(n=i(t.element),a.set(t.element,n)),n};exports.useSlots=o;
1
+ const e=require("./runtime.cjs");let t=require("@vielzeug/ripple");var n=`default`,r=e=>e||n,i=n=>{let i=new Map,o=new Map,s=new Map,c=e=>{let n=i.get(e);return n||(n={elements:(0,t.signal)([]),presence:(0,t.signal)(!1)},i.set(e,n)),n},l=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0},u=e=>{let t=r(e),n=o.get(t),i=[];if(n)for(let e of n)i.push(...e.assignedElements({flatten:!0}));let a=c(t);l(a.elements.value,i)||(a.elements.value=i);let s=i.length>0;a.presence.value!==s&&(a.presence.value=s)},d=e=>{if(s.has(e))return;let t=r(e.getAttribute(`name`)),n=o.get(t)??new Set;n.add(e),o.set(t,n);let i=()=>u(t);e.addEventListener(`slotchange`,i),s.set(e,()=>{e.removeEventListener(`slotchange`,i)}),u(t)},f=e=>{let t=s.get(e);if(!t)return;t(),s.delete(e);let n=r(e.getAttribute(`name`)),i=o.get(n);i&&(i.delete(e),i.size===0&&o.delete(n)),u(n)},p=()=>{n.shadowRoot?.querySelectorAll(`slot`).forEach(e=>{d(e)})},m=()=>{for(let e of o.keys())u(e)},h=e=>e instanceof HTMLSlotElement||e.nodeType===Node.ELEMENT_NODE&&e.querySelector(`slot`)!==null,g=e=>{e instanceof HTMLSlotElement?f(e):e.nodeType===Node.ELEMENT_NODE&&e.querySelectorAll(`slot`).forEach(e=>{f(e)})},_=null;return e.onMounted(()=>{p(),m(),!_&&n.shadowRoot&&(_=new MutationObserver(e=>{let t=!1;for(let n of e){for(let e of n.removedNodes)h(e)&&(t=!0,g(e));if(!t){for(let e of n.addedNodes)if(h(e)){t=!0;break}}}t&&(p(),s.size>0&&m())}),_.observe(n.shadowRoot,{childList:!0,subtree:!0}))}),e.onCleanup(()=>{_?.disconnect(),_=null;for(let e of s.values())e();s.clear(),o.clear(),i.clear(),a.delete(n)}),{elements:e=>c(r(e)).elements,has:e=>c(r(e)).presence}},a=new WeakMap,o=()=>{let t=e.requireSetupContext(`useSlots`),n=a.get(t.element);return n||(n=i(t.element),a.set(t.element,n)),n};exports.useSlots=o;
2
2
  //# sourceMappingURL=slots.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"slots.cjs","names":["signal","requireSetupContext","require_runtime","_vielzeug_ripple","SLOT_DEFAULT","normalizeSlotName","slotName","createSlots","slotSignals","slotNodesByName","slotCleanupMap","ensureSlotEntry","entry","normalizedName","areElementsEqual","prev","next","i","recomputeSlot","normalized","name","slotsForName","assigned","slotEl","hasElements","bindSlot","setForName","onChange","unbindSlot","cleanup","bindAllSlots","host","recomputeAllSlots","observer","mutation","mutations","node","slotsByElement","useSlots","ctx"],"sources":["../src/slots.ts"],"sourcesContent":["/**\n * Slot observation and reactive slot signals.\n *\n * `slots.has(name?)`: Signal<boolean>: whether a named slot has assigned elements.\n * `slots.elements(name?)`: Signal<Element[]>: assigned elements for a slot (flattened).\n */\n\nimport { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { onCleanup, onMounted, requireSetupContext } from './runtime';\n\nexport type ComponentSlots<SlotNames extends string = string> = {\n elements: (name?: SlotNames) => Readable<Element[]>;\n has: (name?: SlotNames) => Readable<boolean>;\n};\n\nconst SLOT_DEFAULT = 'default';\nconst normalizeSlotName = (slotName: string | null | undefined): string => slotName || SLOT_DEFAULT;\n\nconst createSlots = (host: HTMLElement): ComponentSlots<string> => {\n type SlotEntry = {\n elements: Signal<Element[]>;\n presence: Signal<boolean>;\n };\n\n const slotSignals = new Map<string, SlotEntry>();\n const slotNodesByName = new Map<string, Set<HTMLSlotElement>>();\n const slotCleanupMap = new Map<HTMLSlotElement, () => void>();\n\n const ensureSlotEntry = (normalizedName: string): SlotEntry => {\n let entry = slotSignals.get(normalizedName);\n\n if (!entry) {\n entry = {\n elements: signal<Element[]>([]),\n presence: signal(false),\n };\n slotSignals.set(normalizedName, entry);\n }\n\n return entry;\n };\n\n const areElementsEqual = (prev: Element[], next: Element[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let i = 0; i < prev.length; i++) {\n if (prev[i] !== next[i]) return false;\n }\n\n return true;\n };\n\n const recomputeSlot = (name: string): void => {\n const normalized = normalizeSlotName(name);\n const slotsForName = slotNodesByName.get(normalized);\n const assigned: Element[] = [];\n\n if (slotsForName) {\n for (const slotEl of slotsForName) {\n assigned.push(...slotEl.assignedElements({ flatten: true }));\n }\n }\n\n const entry = ensureSlotEntry(normalized);\n\n if (!areElementsEqual(entry.elements.value, assigned)) entry.elements.value = assigned;\n\n const hasElements = assigned.length > 0;\n\n if (entry.presence.value !== hasElements) entry.presence.value = hasElements;\n };\n\n const bindSlot = (slotEl: HTMLSlotElement): void => {\n if (slotCleanupMap.has(slotEl)) return;\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name) ?? new Set<HTMLSlotElement>();\n\n setForName.add(slotEl);\n slotNodesByName.set(name, setForName);\n\n const onChange = () => recomputeSlot(name);\n\n slotEl.addEventListener('slotchange', onChange);\n\n slotCleanupMap.set(slotEl, () => {\n slotEl.removeEventListener('slotchange', onChange);\n });\n\n recomputeSlot(name);\n };\n\n const unbindSlot = (slotEl: HTMLSlotElement): void => {\n const cleanup = slotCleanupMap.get(slotEl);\n\n if (!cleanup) return;\n\n cleanup();\n slotCleanupMap.delete(slotEl);\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name);\n\n if (setForName) {\n setForName.delete(slotEl);\n\n if (setForName.size === 0) slotNodesByName.delete(name);\n }\n\n recomputeSlot(name);\n };\n\n const bindAllSlots = (): void => {\n host.shadowRoot?.querySelectorAll('slot').forEach((slotEl) => {\n bindSlot(slotEl);\n });\n };\n\n const recomputeAllSlots = (): void => {\n for (const name of slotNodesByName.keys()) {\n recomputeSlot(name);\n }\n };\n\n // Watch for dynamically-inserted <slot> elements (e.g. inside when(), each()).\n let observer: MutationObserver | null = null;\n\n // Single init pass, run after the first render: binds slots already present\n // (pre-upgrade markup and template-rendered ones alike) and starts observation\n // for slots inserted later. The observer must stay connected for the component's\n // lifetime: it is the only way to detect a *first* <slot> appearing dynamically\n // (e.g. a when() branch that renders a slot), so it cannot be disconnected when\n // the bound-slot count drops to zero.\n const initSlots = (): undefined => {\n bindAllSlots();\n recomputeAllSlots();\n\n if (!observer && host.shadowRoot) {\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node instanceof HTMLSlotElement) unbindSlot(node);\n }\n }\n\n bindAllSlots();\n\n if (slotCleanupMap.size > 0) recomputeAllSlots();\n });\n observer.observe(host.shadowRoot, { childList: true, subtree: true });\n }\n\n return undefined;\n };\n\n onMounted(initSlots);\n\n onCleanup(() => {\n observer?.disconnect();\n observer = null;\n\n for (const cleanup of slotCleanupMap.values()) cleanup();\n\n slotCleanupMap.clear();\n slotNodesByName.clear();\n slotSignals.clear();\n\n // The element instance survives disconnect/reconnect (custom elements aren't recreated),\n // but this registry's observer/listeners are torn down above: drop the cache entry so a\n // subsequent reconnect's setup() rebuilds a live registry instead of reusing a dead one.\n slotsByElement.delete(host);\n });\n\n return {\n elements: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).elements,\n has: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).presence,\n };\n};\n\n// Keyed by the host element, not the ephemeral `RuntimeContext`: `onMounted()` callbacks each\n// run with their own freshly-created context object (see base-element.ts's\n// `_scheduleMountCallbacks`), so keying this on `RuntimeContext` would silently create a second,\n// independent registry (a second `MutationObserver`, a second signal set) every time `useSlots()`\n// was called from inside `onMounted()` rather than directly in `setup()`: a real bug the\n// \"one registry per instance\" doc comment below never actually held for that (common) case.\n/** One slot registry per component instance: reused across repeated `useSlots()` calls. */\nconst slotsByElement = new WeakMap<HTMLElement, ComponentSlots<string>>();\n\n/**\n * Returns reactive slot presence / element signals for the current component.\n * Safe to call multiple times during `setup()`: the underlying slot registry\n * (MutationObserver + `slotchange` listeners) is created once per instance.\n *\n * Pass a `SlotNames` type parameter for typed slot names:\n * ```ts\n * const slots = useSlots<'header' | 'footer'>();\n * slots.has('header'); // typed ✓\n * ```\n */\nexport const useSlots = <SlotNames extends string = string>(): ComponentSlots<SlotNames> => {\n const ctx = requireSetupContext('useSlots');\n let entry = slotsByElement.get(ctx.element);\n\n if (!entry) {\n entry = createSlots(ctx.element);\n slotsByElement.set(ctx.element, entry);\n }\n\n return entry as ComponentSlots<SlotNames>;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"slots.cjs","names":["signal","requireSetupContext","require_runtime","_vielzeug_ripple","SLOT_DEFAULT","normalizeSlotName","slotName","createSlots","slotSignals","slotNodesByName","slotCleanupMap","ensureSlotEntry","entry","normalizedName","areElementsEqual","prev","next","i","recomputeSlot","normalized","name","slotsForName","assigned","slotEl","hasElements","bindSlot","setForName","onChange","unbindSlot","cleanup","bindAllSlots","host","recomputeAllSlots","isOrContainsSlot","node","unbindSlotsIn","inner","observer","slotsTouched","mutation","mutations","slotsByElement","useSlots","ctx"],"sources":["../src/slots.ts"],"sourcesContent":["/**\n * Slot observation and reactive slot signals.\n *\n * `slots.has(name?)`: Signal<boolean>: whether a named slot has assigned elements.\n * `slots.elements(name?)`: Signal<Element[]>: assigned elements for a slot (flattened).\n */\n\nimport { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { onCleanup, onMounted, requireSetupContext } from './runtime';\n\nexport type ComponentSlots<SlotNames extends string = string> = {\n elements: (name?: SlotNames) => Readable<Element[]>;\n has: (name?: SlotNames) => Readable<boolean>;\n};\n\nconst SLOT_DEFAULT = 'default';\nconst normalizeSlotName = (slotName: string | null | undefined): string => slotName || SLOT_DEFAULT;\n\nconst createSlots = (host: HTMLElement): ComponentSlots<string> => {\n type SlotEntry = {\n elements: Signal<Element[]>;\n presence: Signal<boolean>;\n };\n\n const slotSignals = new Map<string, SlotEntry>();\n const slotNodesByName = new Map<string, Set<HTMLSlotElement>>();\n const slotCleanupMap = new Map<HTMLSlotElement, () => void>();\n\n const ensureSlotEntry = (normalizedName: string): SlotEntry => {\n let entry = slotSignals.get(normalizedName);\n\n if (!entry) {\n entry = {\n elements: signal<Element[]>([]),\n presence: signal(false),\n };\n slotSignals.set(normalizedName, entry);\n }\n\n return entry;\n };\n\n const areElementsEqual = (prev: Element[], next: Element[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let i = 0; i < prev.length; i++) {\n if (prev[i] !== next[i]) return false;\n }\n\n return true;\n };\n\n const recomputeSlot = (name: string): void => {\n const normalized = normalizeSlotName(name);\n const slotsForName = slotNodesByName.get(normalized);\n const assigned: Element[] = [];\n\n if (slotsForName) {\n for (const slotEl of slotsForName) {\n assigned.push(...slotEl.assignedElements({ flatten: true }));\n }\n }\n\n const entry = ensureSlotEntry(normalized);\n\n if (!areElementsEqual(entry.elements.value, assigned)) entry.elements.value = assigned;\n\n const hasElements = assigned.length > 0;\n\n if (entry.presence.value !== hasElements) entry.presence.value = hasElements;\n };\n\n const bindSlot = (slotEl: HTMLSlotElement): void => {\n if (slotCleanupMap.has(slotEl)) return;\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name) ?? new Set<HTMLSlotElement>();\n\n setForName.add(slotEl);\n slotNodesByName.set(name, setForName);\n\n const onChange = () => recomputeSlot(name);\n\n slotEl.addEventListener('slotchange', onChange);\n\n slotCleanupMap.set(slotEl, () => {\n slotEl.removeEventListener('slotchange', onChange);\n });\n\n recomputeSlot(name);\n };\n\n const unbindSlot = (slotEl: HTMLSlotElement): void => {\n const cleanup = slotCleanupMap.get(slotEl);\n\n if (!cleanup) return;\n\n cleanup();\n slotCleanupMap.delete(slotEl);\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name);\n\n if (setForName) {\n setForName.delete(slotEl);\n\n if (setForName.size === 0) slotNodesByName.delete(name);\n }\n\n recomputeSlot(name);\n };\n\n const bindAllSlots = (): void => {\n host.shadowRoot?.querySelectorAll('slot').forEach((slotEl) => {\n bindSlot(slotEl);\n });\n };\n\n const recomputeAllSlots = (): void => {\n for (const name of slotNodesByName.keys()) {\n recomputeSlot(name);\n }\n };\n\n const isOrContainsSlot = (node: Node): boolean =>\n node instanceof HTMLSlotElement ||\n (node.nodeType === Node.ELEMENT_NODE && (node as Element).querySelector('slot') !== null);\n\n // Detached subtrees still support `querySelector`, so a slot removed inside\n // a moved/removed wrapper is unbound from the whole removed subtree, not\n // just its top node: the old direct-children-only check leaked bound slots.\n const unbindSlotsIn = (node: Node): void => {\n if (node instanceof HTMLSlotElement) {\n unbindSlot(node);\n\n return;\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n (node as Element).querySelectorAll('slot').forEach((inner) => {\n unbindSlot(inner);\n });\n }\n };\n\n // Watch for dynamically-inserted <slot> elements (e.g. inside when(), each()).\n let observer: MutationObserver | null = null;\n\n // Single init pass, run after the first render: binds slots already present\n // (pre-upgrade markup and template-rendered ones alike) and starts observation\n // for slots inserted later. The observer must stay connected for the component's\n // lifetime: it is the only way to detect a *first* <slot> appearing dynamically\n // (e.g. a when() branch that renders a slot), so it cannot be disconnected when\n // the bound-slot count drops to zero.\n const initSlots = (): undefined => {\n bindAllSlots();\n recomputeAllSlots();\n\n if (!observer && host.shadowRoot) {\n observer = new MutationObserver((mutations) => {\n // List UIs mutate shadow DOM constantly; only mutations that actually\n // add or remove a slot (directly or inside a moved subtree) can change\n // slot bindings. Nothing slot-related: skip the full\n // `querySelectorAll('slot')` rescan and `assignedElements()` recompute.\n let slotsTouched = false;\n\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (!isOrContainsSlot(node)) continue;\n\n slotsTouched = true;\n unbindSlotsIn(node);\n }\n\n if (slotsTouched) continue;\n\n for (const node of mutation.addedNodes) {\n if (isOrContainsSlot(node)) {\n slotsTouched = true;\n break;\n }\n }\n }\n\n if (!slotsTouched) return;\n\n bindAllSlots();\n\n if (slotCleanupMap.size > 0) recomputeAllSlots();\n });\n observer.observe(host.shadowRoot, { childList: true, subtree: true });\n }\n\n return undefined;\n };\n\n onMounted(initSlots);\n\n onCleanup(() => {\n observer?.disconnect();\n observer = null;\n\n for (const cleanup of slotCleanupMap.values()) cleanup();\n\n slotCleanupMap.clear();\n slotNodesByName.clear();\n slotSignals.clear();\n\n // The element instance survives disconnect/reconnect (custom elements aren't recreated),\n // but this registry's observer/listeners are torn down above: drop the cache entry so a\n // subsequent reconnect's setup() rebuilds a live registry instead of reusing a dead one.\n slotsByElement.delete(host);\n });\n\n return {\n elements: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).elements,\n has: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).presence,\n };\n};\n\n// Keyed by the host element, not the ephemeral `RuntimeContext`: `onMounted()` callbacks each\n// run with their own freshly-created context object (see base-element.ts's\n// `_scheduleMountCallbacks`), so keying this on `RuntimeContext` would silently create a second,\n// independent registry (a second `MutationObserver`, a second signal set) every time `useSlots()`\n// was called from inside `onMounted()` rather than directly in `setup()`: a real bug the\n// \"one registry per instance\" doc comment below never actually held for that (common) case.\n/** One slot registry per component instance: reused across repeated `useSlots()` calls. */\nconst slotsByElement = new WeakMap<HTMLElement, ComponentSlots<string>>();\n\n/**\n * Returns reactive slot presence / element signals for the current component.\n * Safe to call multiple times during `setup()`: the underlying slot registry\n * (MutationObserver + `slotchange` listeners) is created once per instance.\n *\n * Pass a `SlotNames` type parameter for typed slot names:\n * ```ts\n * const slots = useSlots<'header' | 'footer'>();\n * slots.has('header'); // typed ✓\n * ```\n */\nexport const useSlots = <SlotNames extends string = string>(): ComponentSlots<SlotNames> => {\n const ctx = requireSetupContext('useSlots');\n let entry = slotsByElement.get(ctx.element);\n\n if (!entry) {\n entry = createSlots(ctx.element);\n slotsByElement.set(ctx.element, entry);\n }\n\n return entry as ComponentSlots<SlotNames>;\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,kBAAkB,CAAC;AAItE,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,IAAI;IAC9D,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AA+KF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,SAAS,MAAM,GAAG,MAAM,OAAK,cAAc,CAAC,SAAS,CAUtF,CAAC"}
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,kBAAkB,CAAC;AAItE,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,IAAI;IAC9D,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AAwNF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,SAAS,MAAM,GAAG,MAAM,OAAK,cAAc,CAAC,SAAS,CAUtF,CAAC"}
package/dist/slots.js CHANGED
@@ -1,2 +1,2 @@
1
- import{onCleanup as e,onMounted as t,requireSetupContext as n}from"./runtime.js";import{signal as r}from"@vielzeug/ripple";var i=`default`,a=e=>e||i,o=n=>{let i=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map,l=e=>{let t=i.get(e);return t||(t={elements:r([]),presence:r(!1)},i.set(e,t)),t},u=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0},d=e=>{let t=a(e),n=o.get(t),r=[];if(n)for(let e of n)r.push(...e.assignedElements({flatten:!0}));let i=l(t);u(i.elements.value,r)||(i.elements.value=r);let s=r.length>0;i.presence.value!==s&&(i.presence.value=s)},f=e=>{if(c.has(e))return;let t=a(e.getAttribute(`name`)),n=o.get(t)??/* @__PURE__ */ new Set;n.add(e),o.set(t,n);let r=()=>d(t);e.addEventListener(`slotchange`,r),c.set(e,()=>{e.removeEventListener(`slotchange`,r)}),d(t)},p=e=>{let t=c.get(e);if(!t)return;t(),c.delete(e);let n=a(e.getAttribute(`name`)),r=o.get(n);r&&(r.delete(e),r.size===0&&o.delete(n)),d(n)},m=()=>{n.shadowRoot?.querySelectorAll(`slot`).forEach(e=>{f(e)})},h=()=>{for(let e of o.keys())d(e)},g=null;return t(()=>{m(),h(),!g&&n.shadowRoot&&(g=new MutationObserver(e=>{for(let t of e)for(let e of t.removedNodes)e instanceof HTMLSlotElement&&p(e);m(),c.size>0&&h()}),g.observe(n.shadowRoot,{childList:!0,subtree:!0}))}),e(()=>{g?.disconnect(),g=null;for(let e of c.values())e();c.clear(),o.clear(),i.clear(),s.delete(n)}),{elements:e=>l(a(e)).elements,has:e=>l(a(e)).presence}},s=/* @__PURE__ */ new WeakMap,c=()=>{let e=n(`useSlots`),t=s.get(e.element);return t||(t=o(e.element),s.set(e.element,t)),t};export{c as useSlots};
1
+ import{onCleanup as e,onMounted as t,requireSetupContext as n}from"./runtime.js";import{signal as r}from"@vielzeug/ripple";var i=`default`,a=e=>e||i,o=n=>{let i=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Map,c=/* @__PURE__ */ new Map,l=e=>{let t=i.get(e);return t||(t={elements:r([]),presence:r(!1)},i.set(e,t)),t},u=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0},d=e=>{let t=a(e),n=o.get(t),r=[];if(n)for(let e of n)r.push(...e.assignedElements({flatten:!0}));let i=l(t);u(i.elements.value,r)||(i.elements.value=r);let s=r.length>0;i.presence.value!==s&&(i.presence.value=s)},f=e=>{if(c.has(e))return;let t=a(e.getAttribute(`name`)),n=o.get(t)??/* @__PURE__ */ new Set;n.add(e),o.set(t,n);let r=()=>d(t);e.addEventListener(`slotchange`,r),c.set(e,()=>{e.removeEventListener(`slotchange`,r)}),d(t)},p=e=>{let t=c.get(e);if(!t)return;t(),c.delete(e);let n=a(e.getAttribute(`name`)),r=o.get(n);r&&(r.delete(e),r.size===0&&o.delete(n)),d(n)},m=()=>{n.shadowRoot?.querySelectorAll(`slot`).forEach(e=>{f(e)})},h=()=>{for(let e of o.keys())d(e)},g=e=>e instanceof HTMLSlotElement||e.nodeType===Node.ELEMENT_NODE&&e.querySelector(`slot`)!==null,_=e=>{e instanceof HTMLSlotElement?p(e):e.nodeType===Node.ELEMENT_NODE&&e.querySelectorAll(`slot`).forEach(e=>{p(e)})},v=null;return t(()=>{m(),h(),!v&&n.shadowRoot&&(v=new MutationObserver(e=>{let t=!1;for(let n of e){for(let e of n.removedNodes)g(e)&&(t=!0,_(e));if(!t){for(let e of n.addedNodes)if(g(e)){t=!0;break}}}t&&(m(),c.size>0&&h())}),v.observe(n.shadowRoot,{childList:!0,subtree:!0}))}),e(()=>{v?.disconnect(),v=null;for(let e of c.values())e();c.clear(),o.clear(),i.clear(),s.delete(n)}),{elements:e=>l(a(e)).elements,has:e=>l(a(e)).presence}},s=/* @__PURE__ */ new WeakMap,c=()=>{let e=n(`useSlots`),t=s.get(e.element);return t||(t=o(e.element),s.set(e.element,t)),t};export{c as useSlots};
2
2
  //# sourceMappingURL=slots.js.map
package/dist/slots.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"slots.js","names":["SLOT_DEFAULT","normalizeSlotName","slotName","createSlots","slotSignals","slotNodesByName","slotCleanupMap","ensureSlotEntry","entry","normalizedName","signal","areElementsEqual","prev","next","i","recomputeSlot","normalized","name","slotsForName","assigned","slotEl","hasElements","bindSlot","setForName","onChange","unbindSlot","cleanup","bindAllSlots","host","recomputeAllSlots","observer","onMounted","mutation","mutations","node","onCleanup","slotsByElement","useSlots","ctx","requireSetupContext"],"sources":["../src/slots.ts"],"sourcesContent":["/**\n * Slot observation and reactive slot signals.\n *\n * `slots.has(name?)`: Signal<boolean>: whether a named slot has assigned elements.\n * `slots.elements(name?)`: Signal<Element[]>: assigned elements for a slot (flattened).\n */\n\nimport { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { onCleanup, onMounted, requireSetupContext } from './runtime';\n\nexport type ComponentSlots<SlotNames extends string = string> = {\n elements: (name?: SlotNames) => Readable<Element[]>;\n has: (name?: SlotNames) => Readable<boolean>;\n};\n\nconst SLOT_DEFAULT = 'default';\nconst normalizeSlotName = (slotName: string | null | undefined): string => slotName || SLOT_DEFAULT;\n\nconst createSlots = (host: HTMLElement): ComponentSlots<string> => {\n type SlotEntry = {\n elements: Signal<Element[]>;\n presence: Signal<boolean>;\n };\n\n const slotSignals = new Map<string, SlotEntry>();\n const slotNodesByName = new Map<string, Set<HTMLSlotElement>>();\n const slotCleanupMap = new Map<HTMLSlotElement, () => void>();\n\n const ensureSlotEntry = (normalizedName: string): SlotEntry => {\n let entry = slotSignals.get(normalizedName);\n\n if (!entry) {\n entry = {\n elements: signal<Element[]>([]),\n presence: signal(false),\n };\n slotSignals.set(normalizedName, entry);\n }\n\n return entry;\n };\n\n const areElementsEqual = (prev: Element[], next: Element[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let i = 0; i < prev.length; i++) {\n if (prev[i] !== next[i]) return false;\n }\n\n return true;\n };\n\n const recomputeSlot = (name: string): void => {\n const normalized = normalizeSlotName(name);\n const slotsForName = slotNodesByName.get(normalized);\n const assigned: Element[] = [];\n\n if (slotsForName) {\n for (const slotEl of slotsForName) {\n assigned.push(...slotEl.assignedElements({ flatten: true }));\n }\n }\n\n const entry = ensureSlotEntry(normalized);\n\n if (!areElementsEqual(entry.elements.value, assigned)) entry.elements.value = assigned;\n\n const hasElements = assigned.length > 0;\n\n if (entry.presence.value !== hasElements) entry.presence.value = hasElements;\n };\n\n const bindSlot = (slotEl: HTMLSlotElement): void => {\n if (slotCleanupMap.has(slotEl)) return;\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name) ?? new Set<HTMLSlotElement>();\n\n setForName.add(slotEl);\n slotNodesByName.set(name, setForName);\n\n const onChange = () => recomputeSlot(name);\n\n slotEl.addEventListener('slotchange', onChange);\n\n slotCleanupMap.set(slotEl, () => {\n slotEl.removeEventListener('slotchange', onChange);\n });\n\n recomputeSlot(name);\n };\n\n const unbindSlot = (slotEl: HTMLSlotElement): void => {\n const cleanup = slotCleanupMap.get(slotEl);\n\n if (!cleanup) return;\n\n cleanup();\n slotCleanupMap.delete(slotEl);\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name);\n\n if (setForName) {\n setForName.delete(slotEl);\n\n if (setForName.size === 0) slotNodesByName.delete(name);\n }\n\n recomputeSlot(name);\n };\n\n const bindAllSlots = (): void => {\n host.shadowRoot?.querySelectorAll('slot').forEach((slotEl) => {\n bindSlot(slotEl);\n });\n };\n\n const recomputeAllSlots = (): void => {\n for (const name of slotNodesByName.keys()) {\n recomputeSlot(name);\n }\n };\n\n // Watch for dynamically-inserted <slot> elements (e.g. inside when(), each()).\n let observer: MutationObserver | null = null;\n\n // Single init pass, run after the first render: binds slots already present\n // (pre-upgrade markup and template-rendered ones alike) and starts observation\n // for slots inserted later. The observer must stay connected for the component's\n // lifetime: it is the only way to detect a *first* <slot> appearing dynamically\n // (e.g. a when() branch that renders a slot), so it cannot be disconnected when\n // the bound-slot count drops to zero.\n const initSlots = (): undefined => {\n bindAllSlots();\n recomputeAllSlots();\n\n if (!observer && host.shadowRoot) {\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node instanceof HTMLSlotElement) unbindSlot(node);\n }\n }\n\n bindAllSlots();\n\n if (slotCleanupMap.size > 0) recomputeAllSlots();\n });\n observer.observe(host.shadowRoot, { childList: true, subtree: true });\n }\n\n return undefined;\n };\n\n onMounted(initSlots);\n\n onCleanup(() => {\n observer?.disconnect();\n observer = null;\n\n for (const cleanup of slotCleanupMap.values()) cleanup();\n\n slotCleanupMap.clear();\n slotNodesByName.clear();\n slotSignals.clear();\n\n // The element instance survives disconnect/reconnect (custom elements aren't recreated),\n // but this registry's observer/listeners are torn down above: drop the cache entry so a\n // subsequent reconnect's setup() rebuilds a live registry instead of reusing a dead one.\n slotsByElement.delete(host);\n });\n\n return {\n elements: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).elements,\n has: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).presence,\n };\n};\n\n// Keyed by the host element, not the ephemeral `RuntimeContext`: `onMounted()` callbacks each\n// run with their own freshly-created context object (see base-element.ts's\n// `_scheduleMountCallbacks`), so keying this on `RuntimeContext` would silently create a second,\n// independent registry (a second `MutationObserver`, a second signal set) every time `useSlots()`\n// was called from inside `onMounted()` rather than directly in `setup()`: a real bug the\n// \"one registry per instance\" doc comment below never actually held for that (common) case.\n/** One slot registry per component instance: reused across repeated `useSlots()` calls. */\nconst slotsByElement = new WeakMap<HTMLElement, ComponentSlots<string>>();\n\n/**\n * Returns reactive slot presence / element signals for the current component.\n * Safe to call multiple times during `setup()`: the underlying slot registry\n * (MutationObserver + `slotchange` listeners) is created once per instance.\n *\n * Pass a `SlotNames` type parameter for typed slot names:\n * ```ts\n * const slots = useSlots<'header' | 'footer'>();\n * slots.has('header'); // typed ✓\n * ```\n */\nexport const useSlots = <SlotNames extends string = string>(): ComponentSlots<SlotNames> => {\n const ctx = requireSetupContext('useSlots');\n let entry = slotsByElement.get(ctx.element);\n\n if (!entry) {\n entry = createSlots(ctx.element);\n slotsByElement.set(ctx.element, entry);\n }\n\n return entry as ComponentSlots<SlotNames>;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"slots.js","names":["SLOT_DEFAULT","normalizeSlotName","slotName","createSlots","slotSignals","slotNodesByName","slotCleanupMap","ensureSlotEntry","entry","normalizedName","signal","areElementsEqual","prev","next","i","recomputeSlot","normalized","name","slotsForName","assigned","slotEl","hasElements","bindSlot","setForName","onChange","unbindSlot","cleanup","bindAllSlots","host","recomputeAllSlots","isOrContainsSlot","node","unbindSlotsIn","inner","observer","onMounted","slotsTouched","mutation","mutations","onCleanup","slotsByElement","useSlots","ctx","requireSetupContext"],"sources":["../src/slots.ts"],"sourcesContent":["/**\n * Slot observation and reactive slot signals.\n *\n * `slots.has(name?)`: Signal<boolean>: whether a named slot has assigned elements.\n * `slots.elements(name?)`: Signal<Element[]>: assigned elements for a slot (flattened).\n */\n\nimport { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { onCleanup, onMounted, requireSetupContext } from './runtime';\n\nexport type ComponentSlots<SlotNames extends string = string> = {\n elements: (name?: SlotNames) => Readable<Element[]>;\n has: (name?: SlotNames) => Readable<boolean>;\n};\n\nconst SLOT_DEFAULT = 'default';\nconst normalizeSlotName = (slotName: string | null | undefined): string => slotName || SLOT_DEFAULT;\n\nconst createSlots = (host: HTMLElement): ComponentSlots<string> => {\n type SlotEntry = {\n elements: Signal<Element[]>;\n presence: Signal<boolean>;\n };\n\n const slotSignals = new Map<string, SlotEntry>();\n const slotNodesByName = new Map<string, Set<HTMLSlotElement>>();\n const slotCleanupMap = new Map<HTMLSlotElement, () => void>();\n\n const ensureSlotEntry = (normalizedName: string): SlotEntry => {\n let entry = slotSignals.get(normalizedName);\n\n if (!entry) {\n entry = {\n elements: signal<Element[]>([]),\n presence: signal(false),\n };\n slotSignals.set(normalizedName, entry);\n }\n\n return entry;\n };\n\n const areElementsEqual = (prev: Element[], next: Element[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let i = 0; i < prev.length; i++) {\n if (prev[i] !== next[i]) return false;\n }\n\n return true;\n };\n\n const recomputeSlot = (name: string): void => {\n const normalized = normalizeSlotName(name);\n const slotsForName = slotNodesByName.get(normalized);\n const assigned: Element[] = [];\n\n if (slotsForName) {\n for (const slotEl of slotsForName) {\n assigned.push(...slotEl.assignedElements({ flatten: true }));\n }\n }\n\n const entry = ensureSlotEntry(normalized);\n\n if (!areElementsEqual(entry.elements.value, assigned)) entry.elements.value = assigned;\n\n const hasElements = assigned.length > 0;\n\n if (entry.presence.value !== hasElements) entry.presence.value = hasElements;\n };\n\n const bindSlot = (slotEl: HTMLSlotElement): void => {\n if (slotCleanupMap.has(slotEl)) return;\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name) ?? new Set<HTMLSlotElement>();\n\n setForName.add(slotEl);\n slotNodesByName.set(name, setForName);\n\n const onChange = () => recomputeSlot(name);\n\n slotEl.addEventListener('slotchange', onChange);\n\n slotCleanupMap.set(slotEl, () => {\n slotEl.removeEventListener('slotchange', onChange);\n });\n\n recomputeSlot(name);\n };\n\n const unbindSlot = (slotEl: HTMLSlotElement): void => {\n const cleanup = slotCleanupMap.get(slotEl);\n\n if (!cleanup) return;\n\n cleanup();\n slotCleanupMap.delete(slotEl);\n\n const name = normalizeSlotName(slotEl.getAttribute('name'));\n const setForName = slotNodesByName.get(name);\n\n if (setForName) {\n setForName.delete(slotEl);\n\n if (setForName.size === 0) slotNodesByName.delete(name);\n }\n\n recomputeSlot(name);\n };\n\n const bindAllSlots = (): void => {\n host.shadowRoot?.querySelectorAll('slot').forEach((slotEl) => {\n bindSlot(slotEl);\n });\n };\n\n const recomputeAllSlots = (): void => {\n for (const name of slotNodesByName.keys()) {\n recomputeSlot(name);\n }\n };\n\n const isOrContainsSlot = (node: Node): boolean =>\n node instanceof HTMLSlotElement ||\n (node.nodeType === Node.ELEMENT_NODE && (node as Element).querySelector('slot') !== null);\n\n // Detached subtrees still support `querySelector`, so a slot removed inside\n // a moved/removed wrapper is unbound from the whole removed subtree, not\n // just its top node: the old direct-children-only check leaked bound slots.\n const unbindSlotsIn = (node: Node): void => {\n if (node instanceof HTMLSlotElement) {\n unbindSlot(node);\n\n return;\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n (node as Element).querySelectorAll('slot').forEach((inner) => {\n unbindSlot(inner);\n });\n }\n };\n\n // Watch for dynamically-inserted <slot> elements (e.g. inside when(), each()).\n let observer: MutationObserver | null = null;\n\n // Single init pass, run after the first render: binds slots already present\n // (pre-upgrade markup and template-rendered ones alike) and starts observation\n // for slots inserted later. The observer must stay connected for the component's\n // lifetime: it is the only way to detect a *first* <slot> appearing dynamically\n // (e.g. a when() branch that renders a slot), so it cannot be disconnected when\n // the bound-slot count drops to zero.\n const initSlots = (): undefined => {\n bindAllSlots();\n recomputeAllSlots();\n\n if (!observer && host.shadowRoot) {\n observer = new MutationObserver((mutations) => {\n // List UIs mutate shadow DOM constantly; only mutations that actually\n // add or remove a slot (directly or inside a moved subtree) can change\n // slot bindings. Nothing slot-related: skip the full\n // `querySelectorAll('slot')` rescan and `assignedElements()` recompute.\n let slotsTouched = false;\n\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (!isOrContainsSlot(node)) continue;\n\n slotsTouched = true;\n unbindSlotsIn(node);\n }\n\n if (slotsTouched) continue;\n\n for (const node of mutation.addedNodes) {\n if (isOrContainsSlot(node)) {\n slotsTouched = true;\n break;\n }\n }\n }\n\n if (!slotsTouched) return;\n\n bindAllSlots();\n\n if (slotCleanupMap.size > 0) recomputeAllSlots();\n });\n observer.observe(host.shadowRoot, { childList: true, subtree: true });\n }\n\n return undefined;\n };\n\n onMounted(initSlots);\n\n onCleanup(() => {\n observer?.disconnect();\n observer = null;\n\n for (const cleanup of slotCleanupMap.values()) cleanup();\n\n slotCleanupMap.clear();\n slotNodesByName.clear();\n slotSignals.clear();\n\n // The element instance survives disconnect/reconnect (custom elements aren't recreated),\n // but this registry's observer/listeners are torn down above: drop the cache entry so a\n // subsequent reconnect's setup() rebuilds a live registry instead of reusing a dead one.\n slotsByElement.delete(host);\n });\n\n return {\n elements: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).elements,\n has: (name?: string) => ensureSlotEntry(normalizeSlotName(name)).presence,\n };\n};\n\n// Keyed by the host element, not the ephemeral `RuntimeContext`: `onMounted()` callbacks each\n// run with their own freshly-created context object (see base-element.ts's\n// `_scheduleMountCallbacks`), so keying this on `RuntimeContext` would silently create a second,\n// independent registry (a second `MutationObserver`, a second signal set) every time `useSlots()`\n// was called from inside `onMounted()` rather than directly in `setup()`: a real bug the\n// \"one registry per instance\" doc comment below never actually held for that (common) case.\n/** One slot registry per component instance: reused across repeated `useSlots()` calls. */\nconst slotsByElement = new WeakMap<HTMLElement, ComponentSlots<string>>();\n\n/**\n * Returns reactive slot presence / element signals for the current component.\n * Safe to call multiple times during `setup()`: the underlying slot registry\n * (MutationObserver + `slotchange` listeners) is created once per instance.\n *\n * Pass a `SlotNames` type parameter for typed slot names:\n * ```ts\n * const slots = useSlots<'header' | 'footer'>();\n * slots.has('header'); // typed ✓\n * ```\n */\nexport const useSlots = <SlotNames extends string = string>(): ComponentSlots<SlotNames> => {\n const ctx = requireSetupContext('useSlots');\n let entry = slotsByElement.get(ctx.element);\n\n if (!entry) {\n entry = createSlots(ctx.element);\n slotsByElement.set(ctx.element, entry);\n }\n\n return entry as ComponentSlots<SlotNames>;\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require("../_dev.cjs");const e=require("../errors.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t=e.length-1;t>=0;t--)e[t]?.()},o=e=>{for(let t of e)t.remove()},s=()=>{let e=[],t=[];return{clear(){a(t),o(e),t=[],e=[]},get nodes(){return e},registerCleanup(e){t.push(e)},setNodes(t){e=t}}},c=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=/[\t\n\r]/g,d=/^[\s\p{C}]+|[\s\p{C}]+$/gu,f=e=>e.replace(u,``).replace(d,``),p=e=>l.test(f(e)),m=(e,t,n)=>{let r=t.toLowerCase();if(/^on[a-z]/i.test(t)){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r===`srcdoc`||n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);c.has(r)&&p(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},h=(t,n,r,i)=>{if(!t)return e.ORE_ERRORS.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},g=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),_=e=>i(e.startsWith(`--`)?e:g(e)),v=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},y=e=>{let t=new Set;for(let[r,i]of Object.entries(e)){if(!n(i))continue;let e=r.replace(/\s+/g,``);e&&t.add(e)}return t},b=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=s,exports.cssDeclarationName=_,exports.cssDeclarationValue=v,exports.isStructuredValue=b,exports.listen=h,exports.removeNodes=o,exports.resolveClassTokens=y,exports.resolveMaybeReactive=n,exports.runAll=a,exports.sanitizeCssToken=i,exports.setAttr=m,exports.toKebab=g;
1
+ require("../_dev.cjs");const e=require("../errors.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t=e.length-1;t>=0;t--)e[t]?.()},o=e=>{for(let t of e)t.remove()},s=()=>{let e=[],t=[];return{clear(){a(t),o(e),t=[],e=[]},get nodes(){return e},registerCleanup(e){t.push(e)},setNodes(t){e=t}}},c=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=/[\t\n\r]/g,d=/^[\s\p{C}]+|[\s\p{C}]+$/gu,f=e=>e.replace(u,``).replace(d,``),p=e=>l.test(f(e)),m=new Map,h=512,g=e=>{let t=m.get(e);if(t)return t;let n=e.toLowerCase(),r={event:/^on[a-z]/i.test(e),srcdoc:n===`srcdoc`,url:c.has(n)};return m.size>=h&&m.clear(),m.set(e,r),r},_=(e,t,n)=>{let r=g(t);if(r.event){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r.srcdoc||n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);r.url&&p(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},v=(t,n,r,i)=>{if(!t)return e.ORE_ERRORS.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},y=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),b=e=>i(e.startsWith(`--`)?e:y(e)),x=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},S=e=>{let t=new Set;for(let[r,i]of Object.entries(e)){if(!n(i))continue;let e=r.replace(/\s+/g,``);e&&t.add(e)}return t},C=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=s,exports.cssDeclarationName=b,exports.cssDeclarationValue=x,exports.isStructuredValue=C,exports.listen=v,exports.removeNodes=o,exports.resolveClassTokens=S,exports.resolveMaybeReactive=n,exports.runAll=a,exports.sanitizeCssToken=i,exports.setAttr=_,exports.toKebab=y;
2
2
  //# sourceMappingURL=dom.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom.cjs","names":["isReactive","ORE_ERRORS","require_errors","_vielzeug_ripple","resolveMaybeReactive","value","UNSAFE_CSS_CHARS","sanitizeCssToken","runAll","i","fns","removeNodes","node","nodes","createReplaceableSlot","cleanups","fn","next","URL_ATTRS","DANGEROUS_SCHEME_RE","URL_REMOVE_RE","URL_TRIM_RE","normalizeUrlForSchemeCheck","isDangerousUrl","setAttr","lowerName","name","el","val","strVal","listen","listener","handler","options","toKebab","str","c","cssDeclarationName","cssDeclarationValue","resolved","resolveClassTokens","tokens","key","record","token","isStructuredValue"],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function :\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal: the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\": a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping: this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes: read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply: it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\n/**\n * Control characters and spaces, normalized away before the scheme check to\n * mirror the WHATWG URL parser browsers use when resolving URL attributes: it\n * removes every ASCII tab/LF/CR anywhere in the input and trims C0 controls and\n * spaces from both ends. So `java\\tscript:alert(1)` executes even though the raw\n * string does not start with `javascript:`. Testing the normalized form closes\n * that gap while leaving benign URLs (e.g. one with an internal space) untouched.\n */\nconst URL_REMOVE_RE = /[\\t\\n\\r]/g;\nconst URL_TRIM_RE = /^[\\s\\p{C}]+|[\\s\\p{C}]+$/gu;\n\nconst normalizeUrlForSchemeCheck = (value: string): string => value.replace(URL_REMOVE_RE, '').replace(URL_TRIM_RE, '');\n\nconst isDangerousUrl = (value: string): boolean => DANGEROUS_SCHEME_RE.test(normalizeUrlForSchemeCheck(value));\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const lowerName = name.toLowerCase();\n\n if (/^on[a-z]/i.test(name)) {\n warn(\n `Blocked setAttribute(\"${name}\", ...): inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (lowerName === 'srcdoc') {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...): \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (URL_ATTRS.has(lowerName) && isDangerousUrl(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\n/**\n * Normalize a CSS property name for use in a declaration (camelCase → kebab-case,\n * injection characters stripped). Custom properties (`--*`) are case-sensitive, so\n * they skip the kebab conversion. Shared by `styleMap()` (template side) and\n * `bind({ style })` (host side) so both sanitize identically.\n */\nexport const cssDeclarationName = (name: string): string =>\n sanitizeCssToken(name.startsWith('--') ? name : toKebab(name));\n\n/**\n * Resolve a style value to its declaration text ('' for null/false/empty).\n * Shared by `styleMap()` and `bind({ style })`.\n */\nexport const cssDeclarationValue = (value: unknown): string => {\n const resolved = resolveMaybeReactive(value as MaybeReactive<string | number | null | undefined | false>);\n\n if (resolved == null || resolved === false) return '';\n\n return sanitizeCssToken(String(resolved));\n};\n\n/**\n * Resolve a class map to the set of active class tokens: keys with truthy\n * (possibly reactive) values, whitespace stripped from each key (spaces would\n * inject extra class tokens). Shared by `classMap()` (joins the set into a\n * class attribute) and `bind({ class })` (diffs the set against classList).\n */\nexport const resolveClassTokens = (record: Record<string, MaybeReactive<boolean>>): Set<string> => {\n const tokens = new Set<string>();\n\n for (const [key, value] of Object.entries(record)) {\n if (!resolveMaybeReactive(value)) continue;\n\n const token = key.replace(/\\s+/g, '');\n\n if (token) tokens.add(token);\n }\n\n return tokens;\n};\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"A,0FAoBA,IAAaI,EAA2B,GACtC,OAAOC,GAAU,WAAcA,EAAkB,GAAA,EAAIL,EAAAA,WAAAA,CAAWK,CAAK,EAAIA,EAAM,MAAQA,EAO5EC,EAAmB,SAEnBC,EAAoB,GAA0BF,EAAM,QAAQC,EAAkB,EAAE,EAEhFE,EAAU,GAA8B,CACnD,IAAK,IAAIC,EAAIC,EAAI,OAAS,EAAGD,GAAK,EAAG,IAAKC,EAAID,EAAE,GAAG,CACrD,EAEaE,EAAe,GAAwB,CAClD,IAAK,IAAMC,KAAQC,EACjBD,EAAoB,OAAO,CAE/B,EAuBaE,MAA+C,CAC1D,IAAID,EAAgB,CAAC,EACjBE,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACNP,EAAOO,CAAQ,EACfJ,EAAYE,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAOA,CACT,EACA,gBAAgB,EAAI,CAClBE,EAAS,KAAKC,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQC,CACV,CACF,CACF,EASMC,EAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASKC,EACJ,sGAUIC,EAAgB,YAChBC,EAAc,4BAEdC,EAA8B,GAA0BjB,EAAM,QAAQe,EAAe,EAAE,CAAC,CAAC,QAAQC,EAAa,EAAE,EAEhHE,EAAkB,GAA2BJ,EAAoB,KAAKG,EAA2BjB,CAAK,CAAC,EAEhGmB,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAMC,EAAYC,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAKA,CAAI,EAAG,CAExB,GAAyBA,EAAzB,EAAiGA,EAAK,MAAM,CAAC,EAA7G,EAEFC,EAAG,gBAAgBD,CAAI,EAEvB,MACF,CAWA,GATID,IAAc,UASdG,GAAO,MAAQA,IAAQ,GAAO,CAChCD,EAAG,gBAAgBD,CAAI,EAEvB,MACF,CAEA,IAAMG,EAASD,IAAQ,GAAO,OAAS,OAAOA,CAAG,EAE7CV,EAAU,IAAIO,CAAS,GAAKF,EAAeM,CAAM,GAEjD,GAA8CH,EAA9C,EAEFC,EAAG,gBAAgBD,CAAI,GAKzBC,EAAG,aAAaD,EAAMG,CAAM,CAC9B,EAEaC,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAACH,EAGH,OAFK1B,EAAAA,WAAW,iBAAiByB,CAAI,MAExB,CAAC,EAGhB,IAAMK,EAA0BC,EAIhC,OAFAL,EAAG,iBAAiBD,EAAMK,EAAUE,CAAO,MAE9BN,EAAG,oBAAoBD,EAAMK,EAAUE,CAAO,CAC7D,EAEaC,EAAW,GAAwBC,EAAI,QAAQ,SAAW,GAAM,IAAIC,EAAE,YAAY,GAAG,EAQrFC,EAAsB,GACjC9B,EAAiBmB,EAAK,WAAW,IAAI,EAAIA,EAAOQ,EAAQR,CAAI,CAAC,EAMlDY,EAAuB,GAA2B,CAC7D,IAAMC,EAAWnC,EAAqBC,CAAkE,EAIxG,OAFIkC,GAAY,MAAQA,IAAa,GAAc,GAE5ChC,EAAiB,OAAOgC,CAAQ,CAAC,CAC1C,EAQaC,EAAsB,GAAgE,CACjG,IAAMC,EAAS,IAAI,IAEnB,IAAK,GAAM,CAACC,EAAKrC,KAAU,OAAO,QAAQsC,CAAM,EAAG,CACjD,GAAI,CAACvC,EAAqBC,CAAK,EAAG,SAElC,IAAMuC,EAAQF,EAAI,QAAQ,OAAQ,EAAE,EAEhCE,GAAOH,EAAO,IAAIG,CAAK,CAC7B,CAEA,OAAOH,CACT,EAEaI,EAAqB,GAChC,MAAM,QAAQxC,CAAK,GAAM,OAAOA,GAAU,YAAYA,E"}
1
+ {"version":3,"file":"dom.cjs","names":["isReactive","ORE_ERRORS","require_errors","_vielzeug_ripple","resolveMaybeReactive","value","UNSAFE_CSS_CHARS","sanitizeCssToken","runAll","i","fns","removeNodes","node","nodes","createReplaceableSlot","cleanups","fn","next","URL_ATTRS","DANGEROUS_SCHEME_RE","URL_REMOVE_RE","URL_TRIM_RE","normalizeUrlForSchemeCheck","isDangerousUrl","attrWritePlans","ATTR_WRITE_PLAN_LIMIT","getAttrWritePlan","cached","name","lowerName","plan","setAttr","el","val","strVal","listen","listener","handler","options","toKebab","str","c","cssDeclarationName","cssDeclarationValue","resolved","resolveClassTokens","tokens","key","record","token","isStructuredValue"],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function :\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal: the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\": a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping: this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes: read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply: it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\n/**\n * Control characters and spaces, normalized away before the scheme check to\n * mirror the WHATWG URL parser browsers use when resolving URL attributes: it\n * removes every ASCII tab/LF/CR anywhere in the input and trims C0 controls and\n * spaces from both ends. So `java\\tscript:alert(1)` executes even though the raw\n * string does not start with `javascript:`. Testing the normalized form closes\n * that gap while leaving benign URLs (e.g. one with an internal space) untouched.\n */\nconst URL_REMOVE_RE = /[\\t\\n\\r]/g;\nconst URL_TRIM_RE = /^[\\s\\p{C}]+|[\\s\\p{C}]+$/gu;\n\nconst normalizeUrlForSchemeCheck = (value: string): string => value.replace(URL_REMOVE_RE, '').replace(URL_TRIM_RE, '');\n\nconst isDangerousUrl = (value: string): boolean => DANGEROUS_SCHEME_RE.test(normalizeUrlForSchemeCheck(value));\n\n/**\n * The name-only classification `setAttr` branches on. Attribute names repeat\n * constantly on hot update paths (a template binds `class`/`href`/... once and\n * writes them on every signal change), so the regex/toLowerCase/set-lookup\n * pass runs once per name and every write just reads the cached plan.\n */\ntype AttrWritePlan = {\n event: boolean;\n srcdoc: boolean;\n url: boolean;\n};\n\nconst attrWritePlans = new Map<string, AttrWritePlan>();\nconst ATTR_WRITE_PLAN_LIMIT = 512;\n\nconst getAttrWritePlan = (name: string): AttrWritePlan => {\n const cached = attrWritePlans.get(name);\n\n if (cached) return cached;\n\n const lowerName = name.toLowerCase();\n const plan: AttrWritePlan = {\n event: /^on[a-z]/i.test(name),\n srcdoc: lowerName === 'srcdoc',\n url: URL_ATTRS.has(lowerName),\n };\n\n if (attrWritePlans.size >= ATTR_WRITE_PLAN_LIMIT) attrWritePlans.clear();\n\n attrWritePlans.set(name, plan);\n\n return plan;\n};\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const plan = getAttrWritePlan(name);\n\n if (plan.event) {\n warn(\n `Blocked setAttribute(\"${name}\", ...): inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (plan.srcdoc) {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...): \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (plan.url && isDangerousUrl(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\n/**\n * Normalize a CSS property name for use in a declaration (camelCase → kebab-case,\n * injection characters stripped). Custom properties (`--*`) are case-sensitive, so\n * they skip the kebab conversion. Shared by `styleMap()` (template side) and\n * `bind({ style })` (host side) so both sanitize identically.\n */\nexport const cssDeclarationName = (name: string): string =>\n sanitizeCssToken(name.startsWith('--') ? name : toKebab(name));\n\n/**\n * Resolve a style value to its declaration text ('' for null/false/empty).\n * Shared by `styleMap()` and `bind({ style })`.\n */\nexport const cssDeclarationValue = (value: unknown): string => {\n const resolved = resolveMaybeReactive(value as MaybeReactive<string | number | null | undefined | false>);\n\n if (resolved == null || resolved === false) return '';\n\n return sanitizeCssToken(String(resolved));\n};\n\n/**\n * Resolve a class map to the set of active class tokens: keys with truthy\n * (possibly reactive) values, whitespace stripped from each key (spaces would\n * inject extra class tokens). Shared by `classMap()` (joins the set into a\n * class attribute) and `bind({ class })` (diffs the set against classList).\n */\nexport const resolveClassTokens = (record: Record<string, MaybeReactive<boolean>>): Set<string> => {\n const tokens = new Set<string>();\n\n for (const [key, value] of Object.entries(record)) {\n if (!resolveMaybeReactive(value)) continue;\n\n const token = key.replace(/\\s+/g, '');\n\n if (token) tokens.add(token);\n }\n\n return tokens;\n};\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK7D;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,EAAE,OAAO,aAAa,CAAC,CAAC,CAAC,KAAG,CAC4B,CAAC;AAE/F;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAW,CAAC;AAEzC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAA6C,CAAC;AAE/F,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,KAAG,IAE5C,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,IAAI,EAAE,KAAG,IAI3C,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gGAAgG;IAChG,KAAK,IAAI,IAAI,CAAC;IACd,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,eAqBxC,CAAC;AAgDF,eAAO,MAAM,OAAO,GAAI,IAAI,OAAO,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,KAAG,IAuCjE,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,IAAI,WAAW,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,MAAM,EACZ,SAAS,aAAa,EACtB,UAAU,uBAAuB,KAChC,CAAC,MAAM,IAAI,CAYb,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,MAA6D,CAAC;AAEpG;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,MACc,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,MAMpD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,KAAG,GAAG,CAAC,MAAM,CAY7F,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACW,CAAC"}
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK7D;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,EAAE,OAAO,aAAa,CAAC,CAAC,CAAC,KAAG,CAC4B,CAAC;AAE/F;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAW,CAAC;AAEzC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAA6C,CAAC;AAE/F,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,KAAG,IAE5C,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,IAAI,EAAE,KAAG,IAI3C,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gGAAgG;IAChG,KAAK,IAAI,IAAI,CAAC;IACd,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,eAqBxC,CAAC;AAkFF,eAAO,MAAM,OAAO,GAAI,IAAI,OAAO,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,KAAG,IAuCjE,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,IAAI,WAAW,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,MAAM,EACZ,SAAS,aAAa,EACtB,UAAU,uBAAuB,KAChC,CAAC,MAAM,IAAI,CAYb,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,MAA6D,CAAC;AAEpG;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,MACc,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,MAMpD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,KAAG,GAAG,CAAC,MAAM,CAY7F,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACW,CAAC"}
package/dist/utils/dom.js CHANGED
@@ -1,2 +1,2 @@
1
- import"../_dev.js";import{ORE_ERRORS as e}from"../errors.js";import{isReactive as t}from"@vielzeug/ripple";var n=e=>typeof e==`function`?e():t(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t=e.length-1;t>=0;t--)e[t]?.()},o=e=>{for(let t of e)t.remove()},s=()=>{let e=[],t=[];return{clear(){a(t),o(e),t=[],e=[]},get nodes(){return e},registerCleanup(e){t.push(e)},setNodes(t){e=t}}},c=/* @__PURE__ */ new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=/[\t\n\r]/g,d=/^[\s\p{C}]+|[\s\p{C}]+$/gu,f=e=>e.replace(u,``).replace(d,``),p=e=>l.test(f(e)),m=(e,t,n)=>{let r=t.toLowerCase();if(/^on[a-z]/i.test(t)){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r===`srcdoc`||n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);c.has(r)&&p(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},h=(t,n,r,i)=>{if(!t)return e.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},g=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),_=e=>i(e.startsWith(`--`)?e:g(e)),v=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},y=e=>{let t=/* @__PURE__ */ new Set;for(let[r,i]of Object.entries(e)){if(!n(i))continue;let e=r.replace(/\s+/g,``);e&&t.add(e)}return t},b=e=>Array.isArray(e)||typeof e==`object`&&!!e;export{r as UNSAFE_CSS_CHARS,s as createReplaceableSlot,_ as cssDeclarationName,v as cssDeclarationValue,b as isStructuredValue,h as listen,o as removeNodes,y as resolveClassTokens,n as resolveMaybeReactive,a as runAll,i as sanitizeCssToken,m as setAttr,g as toKebab};
1
+ import"../_dev.js";import{ORE_ERRORS as e}from"../errors.js";import{isReactive as t}from"@vielzeug/ripple";var n=e=>typeof e==`function`?e():t(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t=e.length-1;t>=0;t--)e[t]?.()},o=e=>{for(let t of e)t.remove()},s=()=>{let e=[],t=[];return{clear(){a(t),o(e),t=[],e=[]},get nodes(){return e},registerCleanup(e){t.push(e)},setNodes(t){e=t}}},c=/* @__PURE__ */ new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=/[\t\n\r]/g,d=/^[\s\p{C}]+|[\s\p{C}]+$/gu,f=e=>e.replace(u,``).replace(d,``),p=e=>l.test(f(e)),m=/* @__PURE__ */ new Map,h=512,g=e=>{let t=m.get(e);if(t)return t;let n=e.toLowerCase(),r={event:/^on[a-z]/i.test(e),srcdoc:n===`srcdoc`,url:c.has(n)};return m.size>=h&&m.clear(),m.set(e,r),r},_=(e,t,n)=>{let r=g(t);if(r.event){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r.srcdoc||n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);r.url&&p(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},v=(t,n,r,i)=>{if(!t)return e.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},y=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),b=e=>i(e.startsWith(`--`)?e:y(e)),x=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},S=e=>{let t=/* @__PURE__ */ new Set;for(let[r,i]of Object.entries(e)){if(!n(i))continue;let e=r.replace(/\s+/g,``);e&&t.add(e)}return t},C=e=>Array.isArray(e)||typeof e==`object`&&!!e;export{r as UNSAFE_CSS_CHARS,s as createReplaceableSlot,b as cssDeclarationName,x as cssDeclarationValue,C as isStructuredValue,v as listen,o as removeNodes,S as resolveClassTokens,n as resolveMaybeReactive,a as runAll,i as sanitizeCssToken,_ as setAttr,y as toKebab};
2
2
  //# sourceMappingURL=dom.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom.js","names":["resolveMaybeReactive","value","isReactive","UNSAFE_CSS_CHARS","sanitizeCssToken","runAll","i","fns","removeNodes","node","nodes","createReplaceableSlot","cleanups","fn","next","URL_ATTRS","DANGEROUS_SCHEME_RE","URL_REMOVE_RE","URL_TRIM_RE","normalizeUrlForSchemeCheck","isDangerousUrl","setAttr","lowerName","name","el","val","strVal","listen","ORE_ERRORS","listener","handler","options","toKebab","str","c","cssDeclarationName","cssDeclarationValue","resolved","resolveClassTokens","tokens","key","record","token","isStructuredValue"],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function :\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal: the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\": a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping: this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes: read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply: it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\n/**\n * Control characters and spaces, normalized away before the scheme check to\n * mirror the WHATWG URL parser browsers use when resolving URL attributes: it\n * removes every ASCII tab/LF/CR anywhere in the input and trims C0 controls and\n * spaces from both ends. So `java\\tscript:alert(1)` executes even though the raw\n * string does not start with `javascript:`. Testing the normalized form closes\n * that gap while leaving benign URLs (e.g. one with an internal space) untouched.\n */\nconst URL_REMOVE_RE = /[\\t\\n\\r]/g;\nconst URL_TRIM_RE = /^[\\s\\p{C}]+|[\\s\\p{C}]+$/gu;\n\nconst normalizeUrlForSchemeCheck = (value: string): string => value.replace(URL_REMOVE_RE, '').replace(URL_TRIM_RE, '');\n\nconst isDangerousUrl = (value: string): boolean => DANGEROUS_SCHEME_RE.test(normalizeUrlForSchemeCheck(value));\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const lowerName = name.toLowerCase();\n\n if (/^on[a-z]/i.test(name)) {\n warn(\n `Blocked setAttribute(\"${name}\", ...): inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (lowerName === 'srcdoc') {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...): \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (URL_ATTRS.has(lowerName) && isDangerousUrl(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\n/**\n * Normalize a CSS property name for use in a declaration (camelCase → kebab-case,\n * injection characters stripped). Custom properties (`--*`) are case-sensitive, so\n * they skip the kebab conversion. Shared by `styleMap()` (template side) and\n * `bind({ style })` (host side) so both sanitize identically.\n */\nexport const cssDeclarationName = (name: string): string =>\n sanitizeCssToken(name.startsWith('--') ? name : toKebab(name));\n\n/**\n * Resolve a style value to its declaration text ('' for null/false/empty).\n * Shared by `styleMap()` and `bind({ style })`.\n */\nexport const cssDeclarationValue = (value: unknown): string => {\n const resolved = resolveMaybeReactive(value as MaybeReactive<string | number | null | undefined | false>);\n\n if (resolved == null || resolved === false) return '';\n\n return sanitizeCssToken(String(resolved));\n};\n\n/**\n * Resolve a class map to the set of active class tokens: keys with truthy\n * (possibly reactive) values, whitespace stripped from each key (spaces would\n * inject extra class tokens). Shared by `classMap()` (joins the set into a\n * class attribute) and `bind({ class })` (diffs the set against classList).\n */\nexport const resolveClassTokens = (record: Record<string, MaybeReactive<boolean>>): Set<string> => {\n const tokens = new Set<string>();\n\n for (const [key, value] of Object.entries(record)) {\n if (!resolveMaybeReactive(value)) continue;\n\n const token = key.replace(/\\s+/g, '');\n\n if (token) tokens.add(token);\n }\n\n return tokens;\n};\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"A,2GAoBA,IAAaA,EAA2B,GACtC,OAAOC,GAAU,WAAcA,EAAkB,EAAIC,EAAWD,CAAK,EAAIA,EAAM,MAAQA,EAO5EE,EAAmB,SAEnBC,EAAoB,GAA0BH,EAAM,QAAQE,EAAkB,EAAE,EAEhFE,EAAU,GAA8B,CACnD,IAAK,IAAIC,EAAIC,EAAI,OAAS,EAAGD,GAAK,EAAG,IAAKC,EAAID,EAAE,GAAG,CACrD,EAEaE,EAAe,GAAwB,CAClD,IAAK,IAAMC,KAAQC,EACjBD,EAAoB,OAAO,CAE/B,EAuBaE,MAA+C,CAC1D,IAAID,EAAgB,CAAC,EACjBE,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACNP,EAAOO,CAAQ,EACfJ,EAAYE,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAOA,CACT,EACA,gBAAgB,EAAI,CAClBE,EAAS,KAAKC,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQC,CACV,CACF,CACF,EASMC,kBAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASKC,EACJ,sGAUIC,EAAgB,YAChBC,EAAc,4BAEdC,EAA8B,GAA0BlB,EAAM,QAAQgB,EAAe,EAAE,CAAC,CAAC,QAAQC,EAAa,EAAE,EAEhHE,EAAkB,GAA2BJ,EAAoB,KAAKG,EAA2BlB,CAAK,CAAC,EAEhGoB,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAMC,EAAYC,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAKA,CAAI,EAAG,CAExB,GAAyBA,EAAzB,EAAiGA,EAAK,MAAM,CAAC,EAA7G,EAEFC,EAAG,gBAAgBD,CAAI,EAEvB,MACF,CAWA,GATID,IAAc,UASdG,GAAO,MAAQA,IAAQ,GAAO,CAChCD,EAAG,gBAAgBD,CAAI,EAEvB,MACF,CAEA,IAAMG,EAASD,IAAQ,GAAO,OAAS,OAAOA,CAAG,EAE7CV,EAAU,IAAIO,CAAS,GAAKF,EAAeM,CAAM,GAEjD,GAA8CH,EAA9C,EAEFC,EAAG,gBAAgBD,CAAI,GAKzBC,EAAG,aAAaD,EAAMG,CAAM,CAC9B,EAEaC,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAACH,EAGH,OAFKI,EAAW,iBAAiBL,CAAI,MAExB,CAAC,EAGhB,IAAMM,EAA0BC,EAIhC,OAFAN,EAAG,iBAAiBD,EAAMM,EAAUE,CAAO,MAE9BP,EAAG,oBAAoBD,EAAMM,EAAUE,CAAO,CAC7D,EAEaC,EAAW,GAAwBC,EAAI,QAAQ,SAAW,GAAM,IAAIC,EAAE,YAAY,GAAG,EAQrFC,EAAsB,GACjC/B,EAAiBmB,EAAK,WAAW,IAAI,EAAIA,EAAOS,EAAQT,CAAI,CAAC,EAMlDa,EAAuB,GAA2B,CAC7D,IAAMC,EAAWrC,EAAqBC,CAAkE,EAIxG,OAFIoC,GAAY,MAAQA,IAAa,GAAc,GAE5CjC,EAAiB,OAAOiC,CAAQ,CAAC,CAC1C,EAQaC,EAAsB,GAAgE,CACjG,IAAMC,kBAAS,IAAI,IAEnB,IAAK,GAAM,CAACC,EAAKvC,KAAU,OAAO,QAAQwC,CAAM,EAAG,CACjD,GAAI,CAACzC,EAAqBC,CAAK,EAAG,SAElC,IAAMyC,EAAQF,EAAI,QAAQ,OAAQ,EAAE,EAEhCE,GAAOH,EAAO,IAAIG,CAAK,CAC7B,CAEA,OAAOH,CACT,EAEaI,EAAqB,GAChC,MAAM,QAAQ1C,CAAK,GAAM,OAAOA,GAAU,YAAYA,E"}
1
+ {"version":3,"file":"dom.js","names":["resolveMaybeReactive","value","isReactive","UNSAFE_CSS_CHARS","sanitizeCssToken","runAll","i","fns","removeNodes","node","nodes","createReplaceableSlot","cleanups","fn","next","URL_ATTRS","DANGEROUS_SCHEME_RE","URL_REMOVE_RE","URL_TRIM_RE","normalizeUrlForSchemeCheck","isDangerousUrl","attrWritePlans","ATTR_WRITE_PLAN_LIMIT","getAttrWritePlan","cached","name","lowerName","plan","setAttr","el","val","strVal","listen","ORE_ERRORS","listener","handler","options","toKebab","str","c","cssDeclarationName","cssDeclarationValue","resolved","resolveClassTokens","tokens","key","record","token","isStructuredValue"],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function :\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal: the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\": a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping: this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes: read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply: it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\n/**\n * Control characters and spaces, normalized away before the scheme check to\n * mirror the WHATWG URL parser browsers use when resolving URL attributes: it\n * removes every ASCII tab/LF/CR anywhere in the input and trims C0 controls and\n * spaces from both ends. So `java\\tscript:alert(1)` executes even though the raw\n * string does not start with `javascript:`. Testing the normalized form closes\n * that gap while leaving benign URLs (e.g. one with an internal space) untouched.\n */\nconst URL_REMOVE_RE = /[\\t\\n\\r]/g;\nconst URL_TRIM_RE = /^[\\s\\p{C}]+|[\\s\\p{C}]+$/gu;\n\nconst normalizeUrlForSchemeCheck = (value: string): string => value.replace(URL_REMOVE_RE, '').replace(URL_TRIM_RE, '');\n\nconst isDangerousUrl = (value: string): boolean => DANGEROUS_SCHEME_RE.test(normalizeUrlForSchemeCheck(value));\n\n/**\n * The name-only classification `setAttr` branches on. Attribute names repeat\n * constantly on hot update paths (a template binds `class`/`href`/... once and\n * writes them on every signal change), so the regex/toLowerCase/set-lookup\n * pass runs once per name and every write just reads the cached plan.\n */\ntype AttrWritePlan = {\n event: boolean;\n srcdoc: boolean;\n url: boolean;\n};\n\nconst attrWritePlans = new Map<string, AttrWritePlan>();\nconst ATTR_WRITE_PLAN_LIMIT = 512;\n\nconst getAttrWritePlan = (name: string): AttrWritePlan => {\n const cached = attrWritePlans.get(name);\n\n if (cached) return cached;\n\n const lowerName = name.toLowerCase();\n const plan: AttrWritePlan = {\n event: /^on[a-z]/i.test(name),\n srcdoc: lowerName === 'srcdoc',\n url: URL_ATTRS.has(lowerName),\n };\n\n if (attrWritePlans.size >= ATTR_WRITE_PLAN_LIMIT) attrWritePlans.clear();\n\n attrWritePlans.set(name, plan);\n\n return plan;\n};\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const plan = getAttrWritePlan(name);\n\n if (plan.event) {\n warn(\n `Blocked setAttribute(\"${name}\", ...): inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (plan.srcdoc) {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...): \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (plan.url && isDangerousUrl(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\n/**\n * Normalize a CSS property name for use in a declaration (camelCase → kebab-case,\n * injection characters stripped). Custom properties (`--*`) are case-sensitive, so\n * they skip the kebab conversion. Shared by `styleMap()` (template side) and\n * `bind({ style })` (host side) so both sanitize identically.\n */\nexport const cssDeclarationName = (name: string): string =>\n sanitizeCssToken(name.startsWith('--') ? name : toKebab(name));\n\n/**\n * Resolve a style value to its declaration text ('' for null/false/empty).\n * Shared by `styleMap()` and `bind({ style })`.\n */\nexport const cssDeclarationValue = (value: unknown): string => {\n const resolved = resolveMaybeReactive(value as MaybeReactive<string | number | null | undefined | false>);\n\n if (resolved == null || resolved === false) return '';\n\n return sanitizeCssToken(String(resolved));\n};\n\n/**\n * Resolve a class map to the set of active class tokens: keys with truthy\n * (possibly reactive) values, whitespace stripped from each key (spaces would\n * inject extra class tokens). Shared by `classMap()` (joins the set into a\n * class attribute) and `bind({ class })` (diffs the set against classList).\n */\nexport const resolveClassTokens = (record: Record<string, MaybeReactive<boolean>>): Set<string> => {\n const tokens = new Set<string>();\n\n for (const [key, value] of Object.entries(record)) {\n if (!resolveMaybeReactive(value)) continue;\n\n const token = key.replace(/\\s+/g, '');\n\n if (token) tokens.add(token);\n }\n\n return tokens;\n};\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/ore",
3
- "version": "26.11.7",
3
+ "version": "26.11.9",
4
4
  "description": "Functional web-component authoring on top of ripple",
5
5
  "repository": {
6
6
  "type": "git",
@@ -54,8 +54,8 @@
54
54
  "test:types": "tsc -p tsconfig.json --noEmit"
55
55
  },
56
56
  "peerDependencies": {
57
- "@vielzeug/assay": "^26.11.7",
58
- "@vielzeug/ripple": "^26.11.7"
57
+ "@vielzeug/assay": "^26.11.9",
58
+ "@vielzeug/ripple": "^26.11.9"
59
59
  },
60
60
  "peerDependenciesMeta": {
61
61
  "@vielzeug/assay": {
@@ -64,8 +64,8 @@
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/node": "^26.6.6",
67
- "@vielzeug/assay": "26.11.7",
68
- "@vielzeug/ripple": "26.11.7",
67
+ "@vielzeug/assay": "26.11.9",
68
+ "@vielzeug/ripple": "26.11.9",
69
69
  "axe-core": "^4.14.0",
70
70
  "jsdom": "^31.0.0",
71
71
  "typescript": "^6.0.3",