@vielzeug/ore 26.10.15 → 26.10.16
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/_dev.cjs.map +1 -1
- package/dist/_dev.js.map +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.js.map +1 -1
- package/dist/context.cjs.map +1 -1
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/define.cjs.map +1 -1
- package/dist/define.js.map +1 -1
- package/dist/directives/classMap.cjs.map +1 -1
- package/dist/directives/classMap.js.map +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/live.cjs.map +1 -1
- package/dist/directives/live.js.map +1 -1
- package/dist/directives/model.cjs.map +1 -1
- package/dist/directives/model.js.map +1 -1
- package/dist/directives/styleMap.cjs.map +1 -1
- package/dist/directives/styleMap.js.map +1 -1
- package/dist/directives/unsafe-html.cjs.map +1 -1
- package/dist/directives/unsafe-html.js.map +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/forms/field.cjs.map +1 -1
- package/dist/forms/field.js +1 -1
- package/dist/forms/field.js.map +1 -1
- package/dist/host-bind.cjs.map +1 -1
- package/dist/host-bind.js +1 -1
- package/dist/host-bind.js.map +1 -1
- package/dist/ore.cjs +1 -1
- package/dist/ore.cjs.map +1 -1
- package/dist/ore.iife.js +1 -1
- package/dist/ore.iife.js.map +1 -1
- package/dist/ore.js +1180 -2
- package/dist/ore.js.map +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/slots.cjs.map +1 -1
- package/dist/slots.js +1 -1
- package/dist/slots.js.map +1 -1
- package/dist/template/bindings.cjs +1 -1
- package/dist/template/bindings.cjs.map +1 -1
- package/dist/template/bindings.js +1 -1
- package/dist/template/bindings.js.map +1 -1
- package/dist/template/compiler.cjs.map +1 -1
- package/dist/template/compiler.js +1 -1
- package/dist/template/compiler.js.map +1 -1
- package/dist/template/instantiator.cjs +1 -1
- package/dist/template/instantiator.cjs.map +1 -1
- package/dist/template/instantiator.js +1 -1
- package/dist/template/instantiator.js.map +1 -1
- package/dist/template/result.cjs.map +1 -1
- package/dist/template/result.js.map +1 -1
- package/dist/testing/flush.cjs.map +1 -1
- package/dist/testing/flush.js.map +1 -1
- package/dist/testing/mount.cjs.map +1 -1
- package/dist/testing/mount.js.map +1 -1
- package/dist/testing/render-hook.cjs.map +1 -1
- package/dist/testing/render-hook.js +1 -1
- package/dist/testing/render-hook.js.map +1 -1
- package/dist/testing/reset.cjs.map +1 -1
- package/dist/testing/reset.js.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/utils/aria.cjs.map +1 -1
- package/dist/utils/aria.js.map +1 -1
- package/dist/utils/brand.cjs.map +1 -1
- package/dist/utils/brand.js.map +1 -1
- package/dist/utils/css.cjs.map +1 -1
- package/dist/utils/css.js +1 -1
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/dom.cjs +1 -1
- package/dist/utils/dom.cjs.map +1 -1
- package/dist/utils/dom.js +1 -1
- package/dist/utils/dom.js.map +1 -1
- package/dist/utils/emit.cjs.map +1 -1
- package/dist/utils/emit.js.map +1 -1
- package/dist/utils/id.cjs.map +1 -1
- package/dist/utils/id.js.map +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.cjs","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"iLAkEA,IAAa,EAAkC,CAAC,EAK5C,EAAuB,EAI3B,SAAS,EAAU,EAAkB,EAAc,EAAwC,CAGzF,EAAA,QAAQ,EAAS,EAAM,CAAK,CAC9B,CAEA,IAAM,EAAW,GACR,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAG3D,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAO,EAAO,EAEjD,IAAI,EAAyB,KACvB,EAAW,GAAsB,CACrC,EAAW,EAAQ,EAAM,OAAS,EAAM,OAAO,EAC/C,EAAM,eAAe,CACvB,EACM,EAAwB,GAAiC,CAC7D,EAAW,EAAQ,EAAM,MAAM,EAC/B,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAAS,CAAO,EACxC,OAAO,iBAAiB,qBAAsB,CAAoB,EAElE,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAE5B,GAAI,EAAU,MAAM,EAEpB,OAAO,CACT,QAAU,CACR,OAAO,oBAAoB,QAAS,CAAO,EAC3C,OAAO,oBAAoB,qBAAsB,CAAoB,CACvE,CACF,EA2BA,eAAsB,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAM,EAElF,EACA,EAEA,OAAO,GAAe,SACxB,EAAU,GAEV,EAAU,SAAS,EAAE,IACrB,EAAmB,CACjB,GAAI,GAAoB,CAAC,EACzB,MAAO,CACT,GAGE,GACF,EAAA,OAAO,EAAS,CAAgB,EAGlC,IAAM,EAAU,SAAS,cAAc,CAAO,EAE1C,IAAM,EAAQ,UAAY,GAE1B,OAAO,KAAK,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAO,EAAS,CAAK,EAE3D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAK,EAAG,EAAU,EAAS,EAAM,CAAK,EAEjF,GAAI,CACF,MAAM,EAAuB,SAAY,CACvC,EAAU,YAAY,CAAO,EAC7B,EAAiB,KAAK,CAAO,EAC7B,MAAM,EAAA,MAAM,CACd,CAAC,CACH,OAAS,EAAK,CACZ,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAI1C,MAFI,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,EAEpC,CACR,CAEA,IAAI,EAAa,GAEjB,SAAS,GAAU,CACjB,GAAI,EAAY,OAEhB,EAAa,GACb,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAEtC,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,IAAA,EAHY,EAAA,OAAA,CAAQ,EAAQ,YAAc,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM,EAAG,EACT,MAAM,EAAA,MAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtB,EAAU,EAAS,EAAM,CAAK,EAC9B,MAAM,EAAA,MAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,EAAU,EAAS,EAAM,CAAK,EAClF,MAAM,EAAA,MAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,UAEA,MAAA,EAAA,MAEA,IAAI,QAA4B,CAC9B,OAAO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1B,EAAA,oBAAoB,EACpB,EAAA,iBAAiB,CACnB"}
|
|
1
|
+
{"version":3,"file":"mount.cjs","names":["flush","within","require_dom","require_define","require_flush","require_render_hook","require_reset","_vielzeug_assay","_mountedElements","_componentTagCounter","applyAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","html","props","attrs","container","err","i","isDisposed","dispose","fn","record","cleanup","el"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"A,iLAkEA,IAAaQ,EAAkC,CAAC,EAK5CC,EAAuB,EAI3B,SAASC,EAAU,EAAkB,EAAc,EAAwC,CAGzFR,EAAA,QAAQS,EAASC,EAAMC,CAAK,CAC9B,CAEA,IAAMC,EAAW,GACRD,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAG3DE,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAOC,EAAO,EAEjD,IAAIC,EAAyB,KACvBC,EAAW,GAAsB,CACrC,EAAWJ,EAAQK,EAAM,OAASA,EAAM,OAAO,EAC/CA,EAAM,eAAe,CACvB,EACMC,EAAwB,GAAiC,CAC7D,EAAWN,EAAQK,EAAM,MAAM,EAC/BA,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAASD,CAAO,EACxC,OAAO,iBAAiB,qBAAsBE,CAAoB,EAElE,GAAI,CACF,IAAMC,EAAS,MAAML,EAAO,EAE5B,GAAIC,EAAU,MAAMA,EAEpB,OAAOI,CACT,QAAU,CACR,OAAO,oBAAoB,QAASH,CAAO,EAC3C,OAAO,oBAAoB,qBAAsBE,CAAoB,CACvE,CACF,EA2BA,eAAsBE,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAMC,EAElFC,EACAC,EAEA,OAAOC,GAAe,SACxB,EAAUA,GAEV,EAAU,SAAS,EAAEjB,IACrB,EAAmB,CACjB,GAAIkB,GAAoB,CAAC,EACzB,MAAOD,CACT,GAGED,GACFtB,EAAA,OAAOqB,EAASC,CAAgB,EAGlC,IAAMd,EAAU,SAAS,cAAca,CAAO,EAE1CI,IAAM,EAAQ,UAAYA,GAE1B,OAAO,KAAKC,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAOlB,EAASkB,CAAK,EAE3D,IAAK,GAAM,CAACjB,EAAMC,KAAU,OAAO,QAAQiB,CAAK,EAAGpB,EAAUC,EAASC,EAAMC,CAAK,EAEjF,GAAI,CACF,MAAME,EAAuB,SAAY,CACvCgB,EAAU,YAAYpB,CAAO,EAC7BH,EAAiB,KAAKG,CAAO,EAC7B,MAAMX,EAAAA,MAAM,CACd,CAAC,CACH,OAASgC,EAAK,CACZrB,EAAQ,OAAO,EAEf,IAAMsB,EAAIzB,EAAiB,QAAQG,CAAO,EAI1C,MAFIsB,IAAM,IAAIzB,EAAiB,OAAOyB,EAAG,CAAC,EAEpCD,CACR,CAEA,IAAIE,EAAa,GAEjB,SAASC,GAAU,CACjB,GAAID,EAAY,OAEhB,EAAa,GACbvB,EAAQ,OAAO,EAEf,IAAMsB,EAAIzB,EAAiB,QAAQG,CAAO,EAEtCsB,IAAM,IAAIzB,EAAiB,OAAOyB,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,IAAA,EAHYhC,EAAAA,OAAAA,CAAQU,EAAQ,YAAcA,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAMyB,EAAG,EACT,MAAMpC,EAAAA,MAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtBU,EAAUC,EAASC,EAAMC,CAAK,EAC9B,MAAMb,EAAAA,MAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAACY,EAAMC,KAAU,OAAO,QAAQwB,CAAM,EAAG3B,EAAUC,EAASC,EAAMC,CAAK,EAClF,MAAMb,EAAAA,MAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAOkC,CACT,EAEA,UAEA,MAAA9B,EAAA,MAEA,IAAI,QAA4B,CAC9B,OAAOO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjBwB,EAAQ,CACV,CACF,CACF,CASA,SAAgBG,GAAgB,CAC9B,IAAK,IAAMC,KAAM/B,EAAkB+B,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1BlC,EAAA,oBAAoB,EACpBC,EAAA,iBAAiB,CACnB,C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.js","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"iQAkEA,IAAa,EAAkC,CAAC,EAK5C,EAAuB,EAI3B,SAAS,EAAU,EAAkB,EAAc,EAAwC,CAGzF,EAAQ,EAAS,EAAM,CAAK,CAC9B,CAEA,IAAM,EAAW,GACR,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAG3D,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAO,EAAO,EAEjD,IAAI,EAAyB,KACvB,EAAW,GAAsB,CACrC,EAAW,EAAQ,EAAM,OAAS,EAAM,OAAO,EAC/C,EAAM,eAAe,CACvB,EACM,EAAwB,GAAiC,CAC7D,EAAW,EAAQ,EAAM,MAAM,EAC/B,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAAS,CAAO,EACxC,OAAO,iBAAiB,qBAAsB,CAAoB,EAElE,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAE5B,GAAI,EAAU,MAAM,EAEpB,OAAO,CACT,QAAU,CACR,OAAO,oBAAoB,QAAS,CAAO,EAC3C,OAAO,oBAAoB,qBAAsB,CAAoB,CACvE,CACF,EA2BA,eAAsB,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAM,EAElF,EACA,EAEA,OAAO,GAAe,SACxB,EAAU,GAEV,EAAU,SAAS,EAAE,IACrB,EAAmB,CACjB,GAAI,GAAoB,CAAC,EACzB,MAAO,CACT,GAGE,GACF,EAAO,EAAS,CAAgB,EAGlC,IAAM,EAAU,SAAS,cAAc,CAAO,EAE1C,IAAM,EAAQ,UAAY,GAE1B,OAAO,KAAK,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAO,EAAS,CAAK,EAE3D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAK,EAAG,EAAU,EAAS,EAAM,CAAK,EAEjF,GAAI,CACF,MAAM,EAAuB,SAAY,CACvC,EAAU,YAAY,CAAO,EAC7B,EAAiB,KAAK,CAAO,EAC7B,MAAM,EAAM,CACd,CAAC,CACH,OAAS,EAAK,CACZ,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAI1C,MAFI,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,EAEpC,CACR,CAEA,IAAI,EAAa,GAEjB,SAAS,GAAU,CACjB,GAAI,EAAY,OAEhB,EAAa,GACb,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAEtC,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,GAHY,EAAQ,EAAQ,YAAc,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM,EAAG,EACT,MAAM,EAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtB,EAAU,EAAS,EAAM,CAAK,EAC9B,MAAM,EAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,EAAU,EAAS,EAAM,CAAK,EAClF,MAAM,EAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,UAEA,QAEA,IAAI,QAA4B,CAC9B,OAAO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1B,EAAoB,EACpB,EAAiB,CACnB"}
|
|
1
|
+
{"version":3,"file":"mount.js","names":["_mountedElements","_componentTagCounter","applyAttr","setAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","define","html","props","attrs","container","flush","err","i","isDisposed","dispose","within","fn","record","cleanup","el","_disposeRenderHooks","resetOreForTests"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"A,iQAkEA,IAAaA,EAAkC,CAAC,EAK5CC,EAAuB,EAI3B,SAASC,EAAU,EAAkB,EAAc,EAAwC,CAGzFC,EAAQC,EAASC,EAAMC,CAAK,CAC9B,CAEA,IAAMC,EAAW,GACRD,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAG3DE,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAOC,EAAO,EAEjD,IAAIC,EAAyB,KACvBC,EAAW,GAAsB,CACrC,EAAWJ,EAAQK,EAAM,OAASA,EAAM,OAAO,EAC/CA,EAAM,eAAe,CACvB,EACMC,EAAwB,GAAiC,CAC7D,EAAWN,EAAQK,EAAM,MAAM,EAC/BA,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAASD,CAAO,EACxC,OAAO,iBAAiB,qBAAsBE,CAAoB,EAElE,GAAI,CACF,IAAMC,EAAS,MAAML,EAAO,EAE5B,GAAIC,EAAU,MAAMA,EAEpB,OAAOI,CACT,QAAU,CACR,OAAO,oBAAoB,QAASH,CAAO,EAC3C,OAAO,oBAAoB,qBAAsBE,CAAoB,CACvE,CACF,EA2BA,eAAsBE,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAMC,EAElFC,EACAC,EAEA,OAAOC,GAAe,SACxB,EAAUA,GAEV,EAAU,SAAS,EAAElB,IACrB,EAAmB,CACjB,GAAImB,GAAoB,CAAC,EACzB,MAAOD,CACT,GAGED,GACFG,EAAOJ,EAASC,CAAgB,EAGlC,IAAMd,EAAU,SAAS,cAAca,CAAO,EAE1CK,IAAM,EAAQ,UAAYA,GAE1B,OAAO,KAAKC,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAOnB,EAASmB,CAAK,EAE3D,IAAK,GAAM,CAAClB,EAAMC,KAAU,OAAO,QAAQkB,CAAK,EAAGtB,EAAUE,EAASC,EAAMC,CAAK,EAEjF,GAAI,CACF,MAAME,EAAuB,SAAY,CACvCiB,EAAU,YAAYrB,CAAO,EAC7BJ,EAAiB,KAAKI,CAAO,EAC7B,MAAMsB,EAAM,CACd,CAAC,CACH,OAASC,EAAK,CACZvB,EAAQ,OAAO,EAEf,IAAMwB,EAAI5B,EAAiB,QAAQI,CAAO,EAI1C,MAFIwB,IAAM,IAAI5B,EAAiB,OAAO4B,EAAG,CAAC,EAEpCD,CACR,CAEA,IAAIE,EAAa,GAEjB,SAASC,GAAU,CACjB,GAAID,EAAY,OAEhB,EAAa,GACbzB,EAAQ,OAAO,EAEf,IAAMwB,EAAI5B,EAAiB,QAAQI,CAAO,EAEtCwB,IAAM,IAAI5B,EAAiB,OAAO4B,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,GAHYG,EAAQ3B,EAAQ,YAAcA,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM4B,EAAG,EACT,MAAMN,EAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtBxB,EAAUE,EAASC,EAAMC,CAAK,EAC9B,MAAMoB,EAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAACrB,EAAMC,KAAU,OAAO,QAAQ2B,CAAM,EAAG/B,EAAUE,EAASC,EAAMC,CAAK,EAClF,MAAMoB,EAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAOG,CACT,EAEA,UAEA,QAEA,IAAI,QAA4B,CAC9B,OAAOzB,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB0B,EAAQ,CACV,CACF,CACF,CASA,SAAgBI,GAAgB,CAC9B,IAAK,IAAMC,KAAMnC,EAAkBmC,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1BC,EAAoB,EACpBC,EAAiB,CACnB,C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-hook.cjs","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"kJAyBA,
|
|
1
|
+
{"version":3,"file":"render-hook.cjs","names":["createScope","createRuntimeContext","createProps","OreLifecycleError","flush","require_errors","require_runtime","require_props","require_flush","_vielzeug_ripple","hookDisposers","_disposeRenderHooks","dispose","renderHook","hasPropDefs","setupOrDefs","propDefs","setup","maybeSetup","hostScope","hostEl","ctx","result","props","cb","mountCallbacks","cleanup","error","err","isDisposed"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"A,kJAyBA,IAAMU,EAAgB,IAAI,IAGbC,MAAkC,CAC7C,IAAK,IAAMC,IAAW,CAAC,GAAGF,CAAa,EAAGE,EAAQ,CACpD,EA+BA,eAAsBC,EACpB,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAAWF,EAAeC,EAAoB,IAAA,GAC9CE,EAAQH,EAAcI,EAAeH,EAErCI,GAAA,EAAYnB,EAAAA,YAAAA,CAAY,EAExBoB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAYA,CAAM,EAMhC,IAAMC,EAAMpB,EAAAA,qBAAqBmB,CAAM,EACjC,CAAE,kBAAmBC,EAEvBC,EAEJ,GAAI,CACFH,EAAU,QAAU,CAClBb,EAAA,eAAee,MAAW,CACxB,IAAME,EAAQP,EAAWd,EAAAA,YAAYkB,EAAQJ,CAAyB,EAAK,CAAC,EAE5E,EAASC,EAAMM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAMC,KAAMC,EACf,GAAI,CACFN,EAAU,QAAU,CAClBb,EAAA,eAAee,MAAW,CACxB,IAAMK,EAAUF,EAAG,EAEf,OAAOE,GAAY,YAAYpB,EAAA,UAAUoB,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAASC,EAAO,CAKd,IAAMC,EAAMD,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAEpEtB,EAAA,mBACE,IAAIF,EAAAA,kBAAkB,qCAAsC,CAC1D,MAAOyB,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACDR,CACF,CACF,CAEJ,OAASQ,EAAK,CAGZ,MAFAT,EAAU,QAAQ,EAClBC,EAAO,OAAO,EACRQ,CACR,CAEA,MAAMxB,EAAAA,MAAM,EAEZ,IAAIyB,EAAa,GAEjB,SAASjB,GAAU,CACbiB,IAEJ,EAAa,GACbV,EAAU,QAAQ,EAClBC,EAAO,OAAO,EACdV,EAAc,OAAOE,CAAO,EAC9B,CAIA,OAFAF,EAAc,IAAIE,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAOiB,CACT,EAEA,MAAM,OAAQ,CACZ,MAAMzB,EAAAA,MAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjBQ,EAAQ,CACV,CACF,CACF,C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{OreLifecycleError as e,reportRuntimeError as t}from"../errors.js";import{createRuntimeContext as n,onCleanup as r,runWithContext as i}from"../runtime.js";import{createProps as a}from"../props.js";import{flush as o}from"./flush.js";import{createScope as s}from"@vielzeug/ripple";var c
|
|
1
|
+
import{OreLifecycleError as e,reportRuntimeError as t}from"../errors.js";import{createRuntimeContext as n,onCleanup as r,runWithContext as i}from"../runtime.js";import{createProps as a}from"../props.js";import{flush as o}from"./flush.js";import{createScope as s}from"@vielzeug/ripple";var c=/* @__PURE__ */ new Set,l=()=>{for(let e of[...c])e()};async function u(l,u){let d=typeof l!=`function`,f=d?l:void 0,p=d?u:l,m=s(),h=document.createElement(`div`);document.body.appendChild(h);let g=n(h),{mountCallbacks:_}=g,v;try{m.run(()=>{i(g,()=>{let e=f?a(h,f):{};v=p(e)})});for(let n of _)try{m.run(()=>{i(g,()=>{let e=n();typeof e==`function`&&r(e)})})}catch(n){let r=n instanceof Error?n:Error(String(n));t(new e(`renderHook() mount callback failed`,{cause:r,component:`renderHook`,phase:`mounted`}),h)}}catch(e){throw m.dispose(),h.remove(),e}await o();let y=!1;function b(){y||(y=!0,m.dispose(),h.remove(),c.delete(b))}return c.add(b),{dispose:b,get disposed(){return y},async flush(){await o()},result:v,[Symbol.dispose](){b()}}}export{l as _disposeRenderHooks,u as renderHook};
|
|
2
2
|
//# sourceMappingURL=render-hook.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-hook.js","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"6RAyBA,
|
|
1
|
+
{"version":3,"file":"render-hook.js","names":["hookDisposers","_disposeRenderHooks","dispose","renderHook","hasPropDefs","setupOrDefs","propDefs","setup","maybeSetup","hostScope","createScope","hostEl","ctx","createRuntimeContext","result","runWithContext","props","createProps","cb","mountCallbacks","cleanup","onCleanup","error","err","reportRuntimeError","OreLifecycleError","flush","isDisposed"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"A,6RAyBA,IAAMA,kBAAgB,IAAI,IAGbC,MAAkC,CAC7C,IAAK,IAAMC,IAAW,CAAC,GAAGF,CAAa,EAAGE,EAAQ,CACpD,EA+BA,eAAsBC,EACpB,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAAWF,EAAeC,EAAoB,IAAA,GAC9CE,EAAQH,EAAcI,EAAeH,EAErCI,EAAYC,EAAY,EAExBC,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAYA,CAAM,EAMhC,IAAMC,EAAMC,EAAqBF,CAAM,EACjC,CAAE,kBAAmBC,EAEvBE,EAEJ,GAAI,CACFL,EAAU,QAAU,CAClBM,EAAeH,MAAW,CACxB,IAAMI,EAAQV,EAAWW,EAAYN,EAAQL,CAAyB,EAAK,CAAC,EAE5E,EAASC,EAAMS,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAME,KAAMC,EACf,GAAI,CACFV,EAAU,QAAU,CAClBM,EAAeH,MAAW,CACxB,IAAMQ,EAAUF,EAAG,EAEf,OAAOE,GAAY,YAAYC,EAAUD,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAASE,EAAO,CAKd,IAAMC,EAAMD,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAEpEE,EACE,IAAIC,EAAkB,qCAAsC,CAC1D,MAAOF,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACDZ,CACF,CACF,CAEJ,OAASY,EAAK,CAGZ,MAFAd,EAAU,QAAQ,EAClBE,EAAO,OAAO,EACRY,CACR,CAEA,MAAMG,EAAM,EAEZ,IAAIC,EAAa,GAEjB,SAASzB,GAAU,CACbyB,IAEJ,EAAa,GACblB,EAAU,QAAQ,EAClBE,EAAO,OAAO,EACdX,EAAc,OAAOE,CAAO,EAC9B,CAIA,OAFAF,EAAc,IAAIE,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAOyB,CACT,EAEA,MAAM,OAAQ,CACZ,MAAMD,EAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjBxB,EAAQ,CACV,CACF,CACF,C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reset.cjs","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules: those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()`: call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"iEAcA,
|
|
1
|
+
{"version":3,"file":"reset.cjs","names":["require_css","require_id","resetOreForTests"],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules: those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()`: call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"A,iEAcA,IAAaE,MAA+B,CAC1CF,EAAA,sBAAsB,EACtBC,EAAA,gBAAgB,CAClB,E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reset.js","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules: those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()`: call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"yGAcA,
|
|
1
|
+
{"version":3,"file":"reset.js","names":["resetOreForTests","_clearStylesheetCache","_resetIdCounter"],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules: those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()`: call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"A,yGAcA,IAAaA,MAA+B,CAC1CC,EAAsB,EACtBC,EAAgB,CAClB,E"}
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.cjs","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"kLA6BA,
|
|
1
|
+
{"version":3,"file":"testing.cjs","names":["cleanup","require_flush","require_render_hook","require_mount","install","afterEachHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,kLA6BA,SAAgBI,EAAQ,EAA+C,CACrEC,EAAcL,EAAAA,OAAO,CACvB,C"}
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"8KA6BA,
|
|
1
|
+
{"version":3,"file":"testing.js","names":["install","afterEachHook","cleanup","OreTimeoutError","flush","mount","renderHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,8KA6BA,SAAgBA,EAAQ,EAA+C,CACrEC,EAAcC,CAAO,CACvB,C"}
|
package/dist/utils/aria.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aria.cjs","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/** Host attribute key normalization. */\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAGA,
|
|
1
|
+
{"version":3,"file":"aria.cjs","names":["normalizeHostAttrKey","key"],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/** Host attribute key normalization. */\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAGA,IAAaA,EAAwB,GAC/BC,IAAQ,QAAUA,EAAI,WAAW,OAAO,EAAUA,EAG/CA,EAAI,WAAW,MAAM,EAAI,QAAQA,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAMA,E"}
|
package/dist/utils/aria.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aria.js","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/** Host attribute key normalization. */\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAGA,
|
|
1
|
+
{"version":3,"file":"aria.js","names":["normalizeHostAttrKey","key"],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/** Host attribute key normalization. */\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAGA,IAAaA,EAAwB,GAC/BC,IAAQ,QAAUA,EAAI,WAAW,OAAO,EAAUA,EAG/CA,EAAI,WAAW,MAAM,EAAI,QAAQA,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAMA,E"}
|
package/dist/utils/brand.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand.cjs","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,
|
|
1
|
+
{"version":3,"file":"brand.cjs","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
|
package/dist/utils/brand.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand.js","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,
|
|
1
|
+
{"version":3,"file":"brand.js","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
|
package/dist/utils/css.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css.cjs","names":[],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\nimport { makeBrand } from './brand';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = makeBrand<CSSResult>('ore:css-result');\n\nexport const isCssResult = cssResultBrand.is;\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n return cssResultBrand.stamp({ content: content.trim(), toString: cssResultToString });\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\nconst STYLESHEET_CACHE_LIMIT = 256;\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\n/** @internal Visible to cache-behavior tests. */\nexport const _getStylesheetCacheSize = (): number => stylesheetStringCache.size;\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) {\n // Refresh recency so styles repeatedly adopted by components survive eviction.\n stylesheetStringCache.delete(cssText);\n stylesheetStringCache.set(cssText, cached);\n\n return cached;\n }\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n // Deliberately not cached: a broken sheet served from cache would fail silently\n // for every subsequent caller with no further error.\n error('Style sheet replace failed', err);\n\n return sheet;\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n if (stylesheetStringCache.size > STYLESHEET_CACHE_LIMIT) {\n const oldestKey = stylesheetStringCache.keys().next().value;\n\n if (oldestKey !== undefined) stylesheetStringCache.delete(oldestKey);\n }\n\n return sheet;\n};\n"],"mappings":"uBAYA,
|
|
1
|
+
{"version":3,"file":"css.cjs","names":["makeBrand","cssResultBrand","isCssResult","cssResultToString","css","values","content","i","strings","v","stylesheetStringCache","STYLESHEET_CACHE_LIMIT","_clearStylesheetCache","loadStylesheet","style","cssText","cached","sheet","oldestKey"],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\nimport { makeBrand } from './brand';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = makeBrand<CSSResult>('ore:css-result');\n\nexport const isCssResult = cssResultBrand.is;\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n return cssResultBrand.stamp({ content: content.trim(), toString: cssResultToString });\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\nconst STYLESHEET_CACHE_LIMIT = 256;\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\n/** @internal Visible to cache-behavior tests. */\nexport const _getStylesheetCacheSize = (): number => stylesheetStringCache.size;\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) {\n // Refresh recency so styles repeatedly adopted by components survive eviction.\n stylesheetStringCache.delete(cssText);\n stylesheetStringCache.set(cssText, cached);\n\n return cached;\n }\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n // Deliberately not cached: a broken sheet served from cache would fail silently\n // for every subsequent caller with no further error.\n error('Style sheet replace failed', err);\n\n return sheet;\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n if (stylesheetStringCache.size > STYLESHEET_CACHE_LIMIT) {\n const oldestKey = stylesheetStringCache.keys().next().value;\n\n if (oldestKey !== undefined) stylesheetStringCache.delete(oldestKey);\n }\n\n return sheet;\n};\n"],"mappings":"A,uBAYA,IAAMC,E,qBAAiBD,CAAAA,CAAAA,UAAqB,gBAAgB,EAE/CE,EAAcD,EAAe,GAEpCE,EAAoB,UAAmC,CAC3D,OAAO,KAAK,OACd,EAEaC,GAAO,EAA+B,GAAGC,IAA0D,CAC9G,IAAIC,EAAU,GAEd,IAAK,IAAIC,EAAI,EAAGA,EAAIC,EAAQ,OAAQ,IAGlC,GAFA,GAAWA,EAAQD,GAEfA,EAAIF,EAAO,OAAQ,CACrB,IAAMI,EAAIJ,EAAOE,GAEjB,GAAWL,EAAYO,CAAC,EAAIA,EAAE,QAAU,OAAOA,CAAC,CAClD,CAGF,OAAOR,EAAe,MAAM,CAAE,QAASK,EAAQ,KAAK,EAAG,SAAUH,CAAkB,CAAC,CACtF,EAEMO,EAAwB,IAAI,IAC5BC,EAAyB,IAGlBC,MAAoC,CAC/CF,EAAsB,MAAM,CAC9B,EAKaG,EAAkB,GAA6D,CAC1F,GAAIC,aAAiB,cAAe,OAAOA,EAE3C,IAAMC,EAAU,OAAOD,GAAU,SAAWA,EAAQA,EAAM,QACpDE,EAASN,EAAsB,IAAIK,CAAO,EAEhD,GAAIC,EAKF,OAHAN,EAAsB,OAAOK,CAAO,EACpCL,EAAsB,IAAIK,EAASC,CAAM,EAElCA,EAGT,IAAMC,EAAQ,IAAI,cAElB,GAAI,CACFA,EAAM,YAAYF,CAAO,CAC3B,MAAc,CAKZ,OAAOE,CACT,CAIA,GAFAP,EAAsB,IAAIK,EAASE,CAAK,EAEpCP,EAAsB,KAAOC,EAAwB,CACvD,IAAMO,EAAYR,EAAsB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAElDQ,IAAc,IAAA,IAAWR,EAAsB,OAAOQ,CAAS,CACrE,CAEA,OAAOD,CACT,E"}
|
package/dist/utils/css.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import"../_dev.js";import{makeBrand as e}from"./brand.js";var t=e(`ore:css-result`),n=t.is,r=function(){return this.content},i=(e,...i)=>{let a=``;for(let t=0;t<e.length;t++)if(a+=e[t],t<i.length){let e=i[t];a+=n(e)?e.content:String(e)}return t.stamp({content:a.trim(),toString:r})},a
|
|
1
|
+
import"../_dev.js";import{makeBrand as e}from"./brand.js";var t=e(`ore:css-result`),n=t.is,r=function(){return this.content},i=(e,...i)=>{let a=``;for(let t=0;t<e.length;t++)if(a+=e[t],t<i.length){let e=i[t];a+=n(e)?e.content:String(e)}return t.stamp({content:a.trim(),toString:r})},a=/* @__PURE__ */ new Map,o=256,s=()=>{a.clear()},c=e=>{if(e instanceof CSSStyleSheet)return e;let t=typeof e==`string`?e:e.content,n=a.get(t);if(n)return a.delete(t),a.set(t,n),n;let r=new CSSStyleSheet;try{r.replaceSync(t)}catch{return r}if(a.set(t,r),a.size>o){let e=a.keys().next().value;e!==void 0&&a.delete(e)}return r};export{s as _clearStylesheetCache,i as css,n as isCssResult,c as loadStylesheet};
|
|
2
2
|
//# sourceMappingURL=css.js.map
|
package/dist/utils/css.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css.js","names":[],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\nimport { makeBrand } from './brand';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = makeBrand<CSSResult>('ore:css-result');\n\nexport const isCssResult = cssResultBrand.is;\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n return cssResultBrand.stamp({ content: content.trim(), toString: cssResultToString });\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\nconst STYLESHEET_CACHE_LIMIT = 256;\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\n/** @internal Visible to cache-behavior tests. */\nexport const _getStylesheetCacheSize = (): number => stylesheetStringCache.size;\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) {\n // Refresh recency so styles repeatedly adopted by components survive eviction.\n stylesheetStringCache.delete(cssText);\n stylesheetStringCache.set(cssText, cached);\n\n return cached;\n }\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n // Deliberately not cached: a broken sheet served from cache would fail silently\n // for every subsequent caller with no further error.\n error('Style sheet replace failed', err);\n\n return sheet;\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n if (stylesheetStringCache.size > STYLESHEET_CACHE_LIMIT) {\n const oldestKey = stylesheetStringCache.keys().next().value;\n\n if (oldestKey !== undefined) stylesheetStringCache.delete(oldestKey);\n }\n\n return sheet;\n};\n"],"mappings":"0DAYA,
|
|
1
|
+
{"version":3,"file":"css.js","names":["cssResultBrand","makeBrand","isCssResult","cssResultToString","css","values","content","i","strings","v","stylesheetStringCache","STYLESHEET_CACHE_LIMIT","_clearStylesheetCache","loadStylesheet","style","cssText","cached","sheet","oldestKey"],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\nimport { makeBrand } from './brand';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = makeBrand<CSSResult>('ore:css-result');\n\nexport const isCssResult = cssResultBrand.is;\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n return cssResultBrand.stamp({ content: content.trim(), toString: cssResultToString });\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\nconst STYLESHEET_CACHE_LIMIT = 256;\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\n/** @internal Visible to cache-behavior tests. */\nexport const _getStylesheetCacheSize = (): number => stylesheetStringCache.size;\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) {\n // Refresh recency so styles repeatedly adopted by components survive eviction.\n stylesheetStringCache.delete(cssText);\n stylesheetStringCache.set(cssText, cached);\n\n return cached;\n }\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n // Deliberately not cached: a broken sheet served from cache would fail silently\n // for every subsequent caller with no further error.\n error('Style sheet replace failed', err);\n\n return sheet;\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n if (stylesheetStringCache.size > STYLESHEET_CACHE_LIMIT) {\n const oldestKey = stylesheetStringCache.keys().next().value;\n\n if (oldestKey !== undefined) stylesheetStringCache.delete(oldestKey);\n }\n\n return sheet;\n};\n"],"mappings":"A,0DAYA,IAAMA,EAAiBC,EAAqB,gBAAgB,EAE/CC,EAAcF,EAAe,GAEpCG,EAAoB,UAAmC,CAC3D,OAAO,KAAK,OACd,EAEaC,GAAO,EAA+B,GAAGC,IAA0D,CAC9G,IAAIC,EAAU,GAEd,IAAK,IAAIC,EAAI,EAAGA,EAAIC,EAAQ,OAAQ,IAGlC,GAFA,GAAWA,EAAQD,GAEfA,EAAIF,EAAO,OAAQ,CACrB,IAAMI,EAAIJ,EAAOE,GAEjB,GAAWL,EAAYO,CAAC,EAAIA,EAAE,QAAU,OAAOA,CAAC,CAClD,CAGF,OAAOT,EAAe,MAAM,CAAE,QAASM,EAAQ,KAAK,EAAG,SAAUH,CAAkB,CAAC,CACtF,EAEMO,kBAAwB,IAAI,IAC5BC,EAAyB,IAGlBC,MAAoC,CAC/CF,EAAsB,MAAM,CAC9B,EAKaG,EAAkB,GAA6D,CAC1F,GAAIC,aAAiB,cAAe,OAAOA,EAE3C,IAAMC,EAAU,OAAOD,GAAU,SAAWA,EAAQA,EAAM,QACpDE,EAASN,EAAsB,IAAIK,CAAO,EAEhD,GAAIC,EAKF,OAHAN,EAAsB,OAAOK,CAAO,EACpCL,EAAsB,IAAIK,EAASC,CAAM,EAElCA,EAGT,IAAMC,EAAQ,IAAI,cAElB,GAAI,CACFA,EAAM,YAAYF,CAAO,CAC3B,MAAc,CAKZ,OAAOE,CACT,CAIA,GAFAP,EAAsB,IAAIK,EAASE,CAAK,EAEpCP,EAAsB,KAAOC,EAAwB,CACvD,IAAMO,EAAYR,EAAsB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAElDQ,IAAc,IAAA,IAAWR,EAAsB,OAAOQ,CAAS,CACrE,CAEA,OAAOD,CACT,E"}
|
package/dist/utils/dom.cjs
CHANGED
|
@@ -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=(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
|
|
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=(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)&&l.test(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},d=(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)},f=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),p=e=>i(e.startsWith(`--`)?e:f(e)),m=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},h=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},g=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=s,exports.cssDeclarationName=p,exports.cssDeclarationValue=m,exports.isStructuredValue=g,exports.listen=d,exports.removeNodes=o,exports.resolveClassTokens=h,exports.resolveMaybeReactive=n,exports.runAll=a,exports.sanitizeCssToken=i,exports.setAttr=u,exports.toKebab=f;
|
|
2
2
|
//# sourceMappingURL=dom.cjs.map
|
package/dist/utils/dom.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.cjs","names":[],"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\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) && DANGEROUS_SCHEME_RE.test(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":"0FAoBA,
|
|
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","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\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) && DANGEROUS_SCHEME_RE.test(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,sGAEWC,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,EAE7CN,EAAU,IAAIG,CAAS,GAAKF,EAAoB,KAAKM,CAAM,GAE3D,GAA8CH,EAA9C,EAEFC,EAAG,gBAAgBD,CAAI,GAKzBC,EAAG,aAAaD,EAAMG,CAAM,CAC9B,EAEaC,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAACH,EAGH,OAFKtB,EAAAA,WAAW,iBAAiBqB,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,GACjC1B,EAAiBe,EAAK,WAAW,IAAI,EAAIA,EAAOQ,EAAQR,CAAI,CAAC,EAMlDY,EAAuB,GAA2B,CAC7D,IAAMC,EAAW/B,EAAqBC,CAAkE,EAIxG,OAFI8B,GAAY,MAAQA,IAAa,GAAc,GAE5C5B,EAAiB,OAAO4B,CAAQ,CAAC,CAC1C,EAQaC,EAAsB,GAAgE,CACjG,IAAMC,EAAS,IAAI,IAEnB,IAAK,GAAM,CAACC,EAAKjC,KAAU,OAAO,QAAQkC,CAAM,EAAG,CACjD,GAAI,CAACnC,EAAqBC,CAAK,EAAG,SAElC,IAAMmC,EAAQF,EAAI,QAAQ,OAAQ,EAAE,EAEhCE,GAAOH,EAAO,IAAIG,CAAK,CAC7B,CAEA,OAAOH,CACT,EAEaI,EAAqB,GAChC,MAAM,QAAQpC,CAAK,GAAM,OAAOA,GAAU,YAAYA,E"}
|
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
|
|
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=(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)&&l.test(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},d=(t,n,r,i)=>{if(!t)return e.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},f=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),p=e=>i(e.startsWith(`--`)?e:f(e)),m=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},h=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},g=e=>Array.isArray(e)||typeof e==`object`&&!!e;export{r as UNSAFE_CSS_CHARS,s as createReplaceableSlot,p as cssDeclarationName,m as cssDeclarationValue,g as isStructuredValue,d as listen,o as removeNodes,h as resolveClassTokens,n as resolveMaybeReactive,a as runAll,i as sanitizeCssToken,u as setAttr,f as toKebab};
|
|
2
2
|
//# sourceMappingURL=dom.js.map
|
package/dist/utils/dom.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.js","names":[],"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\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) && DANGEROUS_SCHEME_RE.test(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":"2GAoBA,
|
|
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","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\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) && DANGEROUS_SCHEME_RE.test(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,sGAEWC,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,EAE7CN,EAAU,IAAIG,CAAS,GAAKF,EAAoB,KAAKM,CAAM,GAE3D,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,GACjC3B,EAAiBe,EAAK,WAAW,IAAI,EAAIA,EAAOS,EAAQT,CAAI,CAAC,EAMlDa,EAAuB,GAA2B,CAC7D,IAAMC,EAAWjC,EAAqBC,CAAkE,EAIxG,OAFIgC,GAAY,MAAQA,IAAa,GAAc,GAE5C7B,EAAiB,OAAO6B,CAAQ,CAAC,CAC1C,EAQaC,EAAsB,GAAgE,CACjG,IAAMC,kBAAS,IAAI,IAEnB,IAAK,GAAM,CAACC,EAAKnC,KAAU,OAAO,QAAQoC,CAAM,EAAG,CACjD,GAAI,CAACrC,EAAqBC,CAAK,EAAG,SAElC,IAAMqC,EAAQF,EAAI,QAAQ,OAAQ,EAAE,EAEhCE,GAAOH,EAAO,IAAIG,CAAK,CAC7B,CAEA,OAAOH,CACT,EAEaI,EAAqB,GAChC,MAAM,QAAQtC,CAAK,GAAM,OAAOA,GAAU,YAAYA,E"}
|
package/dist/utils/emit.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit.cjs","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site: an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()`: the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result: `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true`: the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"kCAqBA,
|
|
1
|
+
{"version":3,"file":"emit.cjs","names":["getHost","require_runtime","DEFAULT_FIRE_OPTIONS","useEmit","host","rest","customEventInit","event"],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site: an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()`: the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result: `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true`: the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"A,kCAqBA,IAAME,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAK,EAgClEC,MAAsF,CACjG,IAAMC,EAAOJ,EAAAA,QAAQ,EAErB,QAAS,EAAgB,GAAGK,IAAoB,CAC9C,IAAMC,EAAkBD,EAAK,OAAS,EAAI,CAAE,GAAGH,EAAsB,OAAQG,EAAK,EAAG,EAAIH,EAEzF,OAAOE,EAAK,cAAc,IAAI,YAAqB,OAAOG,CAAK,EAAGD,CAAe,CAAC,CACpF,EACF,E"}
|
package/dist/utils/emit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit.js","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site: an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()`: the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result: `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true`: the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"wCAqBA,
|
|
1
|
+
{"version":3,"file":"emit.js","names":["DEFAULT_FIRE_OPTIONS","useEmit","host","getHost","rest","customEventInit","event"],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site: an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()`: the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result: `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true`: the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"A,wCAqBA,IAAMA,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAK,EAgClEC,MAAsF,CACjG,IAAMC,EAAOC,EAAQ,EAErB,QAAS,EAAgB,GAAGC,IAAoB,CAC9C,IAAMC,EAAkBD,EAAK,OAAS,EAAI,CAAE,GAAGJ,EAAsB,OAAQI,EAAK,EAAG,EAAIJ,EAEzF,OAAOE,EAAK,cAAc,IAAI,YAAqB,OAAOI,CAAK,EAAGD,CAAe,CAAC,CACpF,EACF,E"}
|
package/dist/utils/id.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.cjs","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["/** Unique ID generation for component and ARIA relationships. */\n\nlet counter = 0;\nconst tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Reset by the testing cleanup path. */\nexport const _resetIdCounter = (): void => {\n counter = 0;\n};\n\n/** Generate a collision-resistant ID with an optional semantic prefix. */\nexport const createId = (prefix = 'id'): string => `${prefix}-${tag}${++counter}`;\n"],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"id.cjs","names":["counter","tag","_resetIdCounter","createId","prefix"],"sources":["../../src/utils/id.ts"],"sourcesContent":["/** Unique ID generation for component and ARIA relationships. */\n\nlet counter = 0;\nconst tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Reset by the testing cleanup path. */\nexport const _resetIdCounter = (): void => {\n counter = 0;\n};\n\n/** Generate a collision-resistant ID with an optional semantic prefix. */\nexport const createId = (prefix = 'id'): string => `${prefix}-${tag}${++counter}`;\n"],"mappings":"AAEA,IAAIA,EAAU,EACRC,EAAM,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAG,CAAC,EAGpCC,MAA8B,CACzC,EAAU,CACZ,EAGaC,GAAY,EAAS,OAAiB,GAAGC,EAAO,GAAGH,IAAM,EAAED,I"}
|
package/dist/utils/id.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.js","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["/** Unique ID generation for component and ARIA relationships. */\n\nlet counter = 0;\nconst tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Reset by the testing cleanup path. */\nexport const _resetIdCounter = (): void => {\n counter = 0;\n};\n\n/** Generate a collision-resistant ID with an optional semantic prefix. */\nexport const createId = (prefix = 'id'): string => `${prefix}-${tag}${++counter}`;\n"],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"id.js","names":["counter","tag","_resetIdCounter","createId","prefix"],"sources":["../../src/utils/id.ts"],"sourcesContent":["/** Unique ID generation for component and ARIA relationships. */\n\nlet counter = 0;\nconst tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Reset by the testing cleanup path. */\nexport const _resetIdCounter = (): void => {\n counter = 0;\n};\n\n/** Generate a collision-resistant ID with an optional semantic prefix. */\nexport const createId = (prefix = 'id'): string => `${prefix}-${tag}${++counter}`;\n"],"mappings":"AAEA,IAAIA,EAAU,EACRC,EAAM,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAG,CAAC,EAGpCC,MAA8B,CACzC,EAAU,CACZ,EAGaC,GAAY,EAAS,OAAiB,GAAGC,EAAO,GAAGH,IAAM,EAAED,I"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/ore",
|
|
3
|
-
"version": "26.10.
|
|
3
|
+
"version": "26.10.16",
|
|
4
4
|
"description": "Functional web-component authoring on top of ripple",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"test:types": "tsc -p tsconfig.json --noEmit"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@vielzeug/assay": "^26.10.
|
|
57
|
-
"@vielzeug/ripple": "^26.10.
|
|
56
|
+
"@vielzeug/assay": "^26.10.16",
|
|
57
|
+
"@vielzeug/ripple": "^26.10.16"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|
|
60
60
|
"@vielzeug/assay": {
|
|
@@ -62,13 +62,13 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@types/node": "^26.6.
|
|
66
|
-
"@vielzeug/assay": "26.10.
|
|
67
|
-
"@vielzeug/ripple": "26.10.
|
|
68
|
-
"axe-core": "^4.
|
|
69
|
-
"jsdom": "^30.1.
|
|
65
|
+
"@types/node": "^26.6.5",
|
|
66
|
+
"@vielzeug/assay": "26.10.16",
|
|
67
|
+
"@vielzeug/ripple": "26.10.16",
|
|
68
|
+
"axe-core": "^4.14.0",
|
|
69
|
+
"jsdom": "^30.1.3",
|
|
70
70
|
"typescript": "^6.0.3",
|
|
71
|
-
"vite": "^8.3.
|
|
72
|
-
"vitest": "^5.0.
|
|
71
|
+
"vite": "^8.3.4",
|
|
72
|
+
"vitest": "^5.0.5"
|
|
73
73
|
}
|
|
74
74
|
}
|