@vielzeug/ore 1.2.2 → 1.3.0
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/aria.cjs +1 -1
- package/dist/aria.cjs.map +1 -1
- package/dist/aria.d.ts +9 -8
- package/dist/aria.d.ts.map +1 -1
- package/dist/aria.js +1 -1
- package/dist/aria.js.map +1 -1
- package/dist/base-element.cjs +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.d.ts.map +1 -1
- package/dist/base-element.js +1 -1
- package/dist/base-element.js.map +1 -1
- package/dist/directives/classMap.cjs +1 -1
- package/dist/directives/classMap.cjs.map +1 -1
- package/dist/directives/classMap.d.ts.map +1 -1
- package/dist/directives/classMap.js +1 -1
- package/dist/directives/classMap.js.map +1 -1
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +8 -1
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/index.d.ts +34 -5
- package/dist/directives/index.d.ts.map +1 -1
- package/dist/directives/raw.cjs +1 -1
- package/dist/directives/raw.cjs.map +1 -1
- package/dist/directives/raw.js +1 -1
- package/dist/directives/raw.js.map +1 -1
- package/dist/directives/styleMap.cjs +1 -1
- package/dist/directives/styleMap.cjs.map +1 -1
- package/dist/directives/styleMap.d.ts.map +1 -1
- package/dist/directives/styleMap.js +1 -1
- package/dist/directives/styleMap.js.map +1 -1
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/directives.cjs +1 -1
- package/dist/directives.js +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +16 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/host-bind.cjs +1 -1
- package/dist/host-bind.cjs.map +1 -1
- package/dist/host-bind.d.ts +6 -0
- package/dist/host-bind.d.ts.map +1 -1
- package/dist/host-bind.js +1 -1
- package/dist/host-bind.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/ore.cjs +2 -2
- package/dist/ore.cjs.map +1 -1
- package/dist/ore.iife.js +2 -2
- package/dist/ore.iife.js.map +1 -1
- package/dist/ore.js +2 -2
- package/dist/ore.js.map +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts +12 -0
- package/dist/props.d.ts.map +1 -1
- package/dist/props.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/slots.cjs +1 -1
- package/dist/slots.cjs.map +1 -1
- package/dist/slots.d.ts.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.d.ts.map +1 -1
- package/dist/template/bindings.js +1 -1
- package/dist/template/bindings.js.map +1 -1
- package/dist/template/instantiator.cjs.map +1 -1
- package/dist/template/instantiator.d.ts.map +1 -1
- package/dist/template/instantiator.js.map +1 -1
- package/dist/testing/flush.cjs +1 -1
- package/dist/testing/flush.cjs.map +1 -1
- package/dist/testing/flush.d.ts +30 -26
- package/dist/testing/flush.d.ts.map +1 -1
- package/dist/testing/flush.js +1 -1
- package/dist/testing/flush.js.map +1 -1
- package/dist/testing/index.d.ts +30 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/interactions.cjs +1 -1
- package/dist/testing/interactions.cjs.map +1 -1
- package/dist/testing/interactions.d.ts.map +1 -1
- package/dist/testing/interactions.js +1 -1
- package/dist/testing/interactions.js.map +1 -1
- package/dist/testing/mount.cjs +1 -1
- package/dist/testing/mount.cjs.map +1 -1
- package/dist/testing/mount.d.ts +8 -14
- package/dist/testing/mount.d.ts.map +1 -1
- package/dist/testing/mount.js +1 -1
- package/dist/testing/mount.js.map +1 -1
- package/dist/testing/render-hook.cjs +1 -1
- package/dist/testing/render-hook.js +1 -1
- package/dist/testing.cjs +2 -1
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.js +2 -1
- package/dist/testing.js.map +1 -0
- package/dist/utils/aria.cjs.map +1 -1
- package/dist/utils/aria.d.ts +1 -1
- package/dist/utils/aria.js.map +1 -1
- package/dist/utils/dom.cjs +1 -1
- package/dist/utils/dom.cjs.map +1 -1
- package/dist/utils/dom.d.ts +36 -0
- package/dist/utils/dom.d.ts.map +1 -1
- package/dist/utils/dom.js +1 -1
- package/dist/utils/dom.js.map +1 -1
- package/dist/utils/id.cjs +1 -1
- package/dist/utils/id.cjs.map +1 -1
- package/dist/utils/id.d.ts +11 -2
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +1 -1
- package/dist/utils/id.js.map +1 -1
- package/package.json +4 -9
- package/dist/devtools.cjs +0 -2
- package/dist/devtools.cjs.map +0 -1
- package/dist/devtools.d.ts +0 -32
- package/dist/devtools.d.ts.map +0 -1
- package/dist/devtools.js +0 -2
- package/dist/devtools.js.map +0 -1
- package/dist/testing/events.cjs +0 -2
- package/dist/testing/events.cjs.map +0 -1
- package/dist/testing/events.d.ts +0 -37
- package/dist/testing/events.d.ts.map +0 -1
- package/dist/testing/events.js +0 -2
- package/dist/testing/events.js.map +0 -1
- package/dist/testing/query.cjs +0 -2
- package/dist/testing/query.cjs.map +0 -1
- package/dist/testing/query.d.ts +0 -24
- package/dist/testing/query.d.ts.map +0 -1
- package/dist/testing/query.js +0 -2
- package/dist/testing/query.js.map +0 -1
- package/dist/testing/testing.cjs +0 -2
- package/dist/testing/testing.cjs.map +0 -1
- package/dist/testing/testing.d.ts +0 -32
- package/dist/testing/testing.d.ts.map +0 -1
- package/dist/testing/testing.js +0 -2
- package/dist/testing/testing.js.map +0 -1
- package/dist/testing/wait.cjs +0 -2
- package/dist/testing/wait.cjs.map +0 -1
- package/dist/testing/wait.d.ts +0 -35
- package/dist/testing/wait.d.ts.map +0 -1
- package/dist/testing/wait.js +0 -2
- package/dist/testing/wait.js.map +0 -1
package/dist/testing/mount.d.ts
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
* Component mounting utilities for test environments.
|
|
3
3
|
*/
|
|
4
4
|
import type { Readable } from '@vielzeug/ripple';
|
|
5
|
+
import { type QueryScope } from '@vielzeug/assay';
|
|
5
6
|
import { type ComponentDefinition } from '../component-types';
|
|
6
7
|
import { type HTMLResult } from '../types/bindings';
|
|
7
8
|
import { type FlushOptions } from './flush';
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* A mounted component ready for assertions. Every inherited `QueryScope` method (`query`,
|
|
11
|
+
* `queryAll`, `queryByText`, `queryAllByText`, `queryByTestId`, `queryAllByTestId`) is scoped
|
|
12
|
+
* to `element.shadowRoot` — falling back to `element` itself for light-DOM components
|
|
13
|
+
* (`shadow: false` / no shadow root).
|
|
14
|
+
*/
|
|
15
|
+
export interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {
|
|
10
16
|
/** Delegates to `dispose()`. Enables `using` declarations. */
|
|
11
17
|
[Symbol.dispose](): void;
|
|
12
18
|
/** Run a callback then flush — the standard way to trigger and assert a reactive update */
|
|
@@ -23,18 +29,6 @@ export interface Fixture<T extends HTMLElement = HTMLElement> {
|
|
|
23
29
|
element: T;
|
|
24
30
|
/** Wait for all reactive updates and animation frames */
|
|
25
31
|
flush(options?: FlushOptions): Promise<void>;
|
|
26
|
-
/** Query a single element within shadow root */
|
|
27
|
-
query<E extends Element = Element>(selector: string): E | null;
|
|
28
|
-
/** Query all elements within shadow root */
|
|
29
|
-
queryAll<E extends Element = Element>(selector: string): E[];
|
|
30
|
-
/** Query all elements by their `data-testid` attribute */
|
|
31
|
-
queryAllByTestId<E extends Element = Element>(testId: string): E[];
|
|
32
|
-
/** Query all elements whose trimmed text content matches */
|
|
33
|
-
queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];
|
|
34
|
-
/** Query a single element by its `data-testid` attribute */
|
|
35
|
-
queryByTestId<E extends Element = Element>(testId: string): E | null;
|
|
36
|
-
/** Query the first element whose trimmed text content matches */
|
|
37
|
-
queryByText<E extends Element = Element>(text: string, selector?: string): E | null;
|
|
38
32
|
/** The component's shadow root (null for light-DOM components) */
|
|
39
33
|
readonly shadow: ShadowRoot | null;
|
|
40
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"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"}
|
package/dist/testing/mount.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{_resetLiveSignals as e}from"../directives/live.js";import"../types/bindings.js";import{_resetRawSanitizer as t}from"../directives/raw.js";import{_clearStylesheetCache as n}from"../utils/css.js";import{define as r}from"../define.js";import{_resetIdCounter as i}from"../utils/id.js";import{flush as a}from"./flush.js";import{query as o,queryAll as s,queryAllByTestId as c,queryAllByText as l,queryByTestId as u,queryByText as d}from"@vielzeug/assay";var f=[],p=0;function m(e,t,n){n===!1?e.removeAttribute(t):e.setAttribute(t,n===!0?``:String(n))}var h=e=>e instanceof Error?e:Error(String(e)),g=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=h(e.error??e.message),e.preventDefault()},r=e=>{t=h(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function _(e,t={}){let{attrs:n={},componentOptions:i,container:h=document.body,html:_,props:v={}}=t,y,b;typeof e==`string`?y=e:(y=`trial-${++p}-${Math.random().toString(36).slice(2,7)}`,b={...i??{},setup:e}),b&&r(y,b);let x=document.createElement(y);_&&(x.innerHTML=_),Object.keys(v).length&&Object.assign(x,v);for(let[e,t]of Object.entries(n))m(x,e,t);try{await g(async()=>{h.appendChild(x),f.push(x),await a()})}catch(e){x.remove();let t=f.indexOf(x);throw t!==-1&&f.splice(t,1),e}let S=!1;function C(){if(S)return;S=!0,x.remove();let e=f.indexOf(x);e!==-1&&f.splice(e,1)}return{async act(e){await e(),await a()},async attr(e,t){m(x,e,t),await a()},async attrs(e){for(let[t,n]of Object.entries(e))m(x,t,n);await a()},dispose:C,get disposed(){return S},element:x,flush:a,query(e){return o(x.shadowRoot??x,e)},queryAll(e){return s(x.shadowRoot??x,e)},queryAllByTestId(e){return c(x.shadowRoot??x,e)},queryAllByText(e,t=`*`){return l(x.shadowRoot??x,e,t)},queryByTestId(e){return u(x.shadowRoot??x,e)},queryByText(e,t=`*`){return d(x.shadowRoot??x,e,t)},get shadow(){return x.shadowRoot},[Symbol.dispose](){C()}}}async function v(e,t,n){return r(e,t),_(e,n)}function y(e,t=``){customElements.get(e)||customElements.define(e,class extends HTMLElement{connectedCallback(){this.innerHTML=t}})}function b(){for(let e of f)e.remove();f.length=0,t(),e(),n(),i()}export{p as _componentTagCounter,f as _mountedElements,b as cleanup,y as mock,_ as mount,v as mountComponent};
|
|
2
2
|
//# sourceMappingURL=mount.js.map
|
|
@@ -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 { Readable } from '@vielzeug/ripple';\n\nimport { type ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport { _resetLiveSignals } from '../directives/live';\nimport { _resetRawSanitizer } from '../directives/raw';\nimport { type HTMLResult } from '../types/bindings';\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\nimport { flush, type FlushOptions } from './flush';\nimport { queryAllByText, queryByText, type QueryScope } from './query';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface Fixture<T extends HTMLElement = HTMLElement> {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** Query a single element within shadow root */\n query<E extends Element = Element>(selector: string): E | null;\n /** Query all elements within shadow root */\n queryAll<E extends Element = Element>(selector: string): E[];\n /** Query all elements by their `data-testid` attribute */\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n /** Query all elements whose trimmed text content matches */\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n /** Query a single element by its `data-testid` attribute */\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n /** Query the first element whose trimmed text content matches */\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<any>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null | Promise<HTMLResult | null>;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\nexport let _componentTagCounter = 0;\n\n/**\n * Resets global test counters used for deterministic IDs/markers.\n * @internal\n */\nexport const _resetCounters = (): void => {\n _resetIdCounter();\n _componentTagCounter = 0;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n if (value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? '' : String(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<any> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}-${Math.random().toString(36).slice(2, 7)}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup as ComponentDefinition<any>['setup'],\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 return {\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 query<E extends Element = Element>(selector: string): E | null {\n return (element.shadowRoot ?? element).querySelector<E>(selector);\n },\n\n queryAll<E extends Element = Element>(selector: string): E[] {\n return Array.from((element.shadowRoot ?? element).querySelectorAll<E>(selector));\n },\n\n queryAllByTestId<E extends Element = Element>(testId: string): E[] {\n return Array.from((element.shadowRoot ?? element).querySelectorAll<E>(`[data-testid=\"${testId}\"]`));\n },\n\n queryAllByText<E extends Element = Element>(text: string, selector = '*'): E[] {\n return queryAllByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n queryByTestId<E extends Element = Element>(testId: string): E | null {\n return (element.shadowRoot ?? element).querySelector<E>(`[data-testid=\"${testId}\"]`);\n },\n\n queryByText<E extends Element = Element>(text: string, selector = '*'): E | null {\n return queryByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<any>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _resetRawSanitizer();\n _resetLiveSignals();\n _clearStylesheetCache();\n _resetIdCounter();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mount.js","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport type { Readable } from '@vielzeug/ripple';\n\nimport {\n query,\n queryAll,\n queryAllByTestId,\n queryAllByText,\n queryByTestId,\n queryByText,\n type QueryScope,\n} from '@vielzeug/assay';\n\nimport { type ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport { _resetLiveSignals } from '../directives/live';\nimport { _resetRawSanitizer } from '../directives/raw';\nimport { type HTMLResult } from '../types/bindings';\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\nimport { flush, type FlushOptions } from './flush';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture's query/queryAll/queryBy*/queryAllBy* methods are exactly QueryScope's surface —\n// extending it (rather than redeclaring all six signatures a second time) means a future\n// addition to one is automatically part of the other, with the type system enforcing it.\n/**\n * A mounted component ready for assertions. Every inherited `QueryScope` method (`query`,\n * `queryAll`, `queryByText`, `queryAllByText`, `queryByTestId`, `queryAllByTestId`) is scoped\n * to `element.shadowRoot` — falling back to `element` itself for light-DOM components\n * (`shadow: false` / no shadow root).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<any>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null | Promise<HTMLResult | null>;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\nexport let _componentTagCounter = 0;\n\n/**\n * Resets global test counters used for deterministic IDs/markers.\n * @internal\n */\nexport const _resetCounters = (): void => {\n _resetIdCounter();\n _componentTagCounter = 0;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n if (value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? '' : String(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<any> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}-${Math.random().toString(36).slice(2, 7)}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup as ComponentDefinition<any>['setup'],\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 return {\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 query<E extends Element = Element>(selector: string): E | null {\n return query<E>((element.shadowRoot ?? element) as Element, selector);\n },\n\n queryAll<E extends Element = Element>(selector: string): E[] {\n return queryAll<E>((element.shadowRoot ?? element) as Element, selector);\n },\n\n queryAllByTestId<E extends Element = Element>(testId: string): E[] {\n return queryAllByTestId<E>((element.shadowRoot ?? element) as Element, testId);\n },\n\n queryAllByText<E extends Element = Element>(text: string, selector = '*'): E[] {\n return queryAllByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n queryByTestId<E extends Element = Element>(testId: string): E | null {\n return queryByTestId<E>((element.shadowRoot ?? element) as Element, testId);\n },\n\n queryByText<E extends Element = Element>(text: string, selector = '*'): E | null {\n return queryByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<any>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _resetRawSanitizer();\n _resetLiveSignals();\n _clearStylesheetCache();\n _resetIdCounter();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"ucA+EA,IAAa,EAAkC,CAAC,EACrC,EAAuB,EAalC,SAAS,EAAU,EAAkB,EAAc,EAAwC,CACrF,IAAU,GAAO,EAAQ,gBAAgB,CAAI,EAC5C,EAAQ,aAAa,EAAM,IAAU,GAAO,GAAK,OAAO,CAAK,CAAC,CACrE,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,EAAqB,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAG,CAAC,IAClF,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,CAEA,MAAO,CACL,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,MAAmC,EAA4B,CAC7D,OAAO,EAAU,EAAQ,YAAc,EAAqB,CAAQ,CACtE,EAEA,SAAsC,EAAuB,CAC3D,OAAO,EAAa,EAAQ,YAAc,EAAqB,CAAQ,CACzE,EAEA,iBAA8C,EAAqB,CACjE,OAAO,EAAqB,EAAQ,YAAc,EAAqB,CAAM,CAC/E,EAEA,eAA4C,EAAc,EAAW,IAAU,CAC7E,OAAO,EAAmB,EAAQ,YAAc,EAAqB,EAAM,CAAQ,CACrF,EAEA,cAA2C,EAA0B,CACnE,OAAO,EAAkB,EAAQ,YAAc,EAAqB,CAAM,CAC5E,EAEA,YAAyC,EAAc,EAAW,IAAe,CAC/E,OAAO,EAAgB,EAAQ,YAAc,EAAqB,EAAM,CAAQ,CAClF,EAEA,IAAI,QAA4B,CAC9B,OAAO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF,CAcA,eAAsB,EACpB,EACA,EACA,EACqB,CAGrB,OAFA,EAAO,EAAK,CAAU,EAEf,EAAS,EAAK,CAAO,CAC9B,CAQA,SAAgB,EAAK,EAAiB,EAAW,GAAU,CACpD,eAAe,IAAI,CAAO,GAC7B,eAAe,OACb,EACA,cAAc,WAAY,CACxB,mBAAoB,CAClB,KAAK,UAAY,CACnB,CACF,CACF,CAEJ,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1B,EAAmB,EACnB,EAAkB,EAClB,EAAsB,EACtB,EAAgB,CAClB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../runtime.cjs"),n=require("../props.cjs"),r=require("./flush.cjs");let i=require("@vielzeug/ripple");async function a(a,o){let s=typeof a!=`function`,c=s?a:void 0,l=s?o:a,u=(0,i.scope)(),d=document.createElement(`div`);document.body.appendChild(d);let f=[],p={element:d,formResetCallbacks:[],mountCallbacks:f},m;try{u.run(()=>{t.runWithContext(p,()=>{let e=c?n.createProps(d,c):{};m=l(e)})});for(let n of f)try{u.run(()=>{t.runWithContext(p,()=>{let e=n();typeof e==`function`&&(0,i.onCleanup)(e)})})}catch(t){e.reportRuntimeError(new e.OreLifecycleError(`renderHook() mount callback failed`,{cause:t instanceof Error?t:Error(String(t)),component:`renderHook`,phase:`mounted`}),d)}}catch(e){throw u.dispose(),d.remove(),e}await r.flush();let h=!1;function g(){h||(h=!0,u.dispose(),d.remove())}return{dispose:g,get disposed(){return h},async flush(){await r.flush()},result:m,[Symbol.dispose](){g()}}}exports.renderHook=a;
|
|
2
2
|
//# sourceMappingURL=render-hook.cjs.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{OreLifecycleError as e,reportRuntimeError as t}from"../errors.js";import{runWithContext as n}from"../runtime.js";import{createProps as r}from"../props.js";import{flush as i}from"./flush.js";import{onCleanup as a,scope as o}from"@vielzeug/ripple";async function s(s,c){let l=typeof s!=`function`,u=l?s:void 0,d=l?c:s,f=o(),p=document.createElement(`div`);document.body.appendChild(p);let m=[],h={element:p,formResetCallbacks:[],mountCallbacks:m},g;try{f.run(()=>{n(h,()=>{let e=u?r(p,u):{};g=d(e)})});for(let r of m)try{f.run(()=>{n(h,()=>{let e=r();typeof e==`function`&&a(e)})})}catch(n){t(new e(`renderHook() mount callback failed`,{cause:n instanceof Error?n:Error(String(n)),component:`renderHook`,phase:`mounted`}),p)}}catch(e){throw f.dispose(),p.remove(),e}await i();let _=!1;function v(){_||(_=!0,f.dispose(),p.remove())}return{dispose:v,get disposed(){return _},async flush(){await i()},result:g,[Symbol.dispose](){v()}}}export{s as renderHook};
|
|
2
2
|
//# sourceMappingURL=render-hook.js.map
|
package/dist/testing.cjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./testing/dom.cjs"),n=require("./testing/form-internals-polyfill.cjs"),r=require("./testing/flush.cjs"),i=require("./testing/mount.cjs"),a=require("./testing/interactions.cjs"),o=require("./testing/render-hook.cjs");let s=require("@vielzeug/assay");function c(e){e(i.cleanup),n.installFormInternalsPolyfill()}exports.OreTimeoutError=e.OreTimeoutError,exports.cleanup=i.cleanup,Object.defineProperty(exports,"createPointerEvent",{enumerable:!0,get:function(){return s.createPointerEvent}}),exports.debugFlush=r.debugFlush,Object.defineProperty(exports,"fire",{enumerable:!0,get:function(){return s.fire}}),exports.flush=r.flush,exports.install=c,exports.installFormInternalsPolyfill=n.installFormInternalsPolyfill,exports.mock=i.mock,exports.mount=i.mount,exports.mountComponent=i.mountComponent,exports.renderHook=o.renderHook,exports.user=a.user,Object.defineProperty(exports,"waitFor",{enumerable:!0,get:function(){return s.waitFor}}),Object.defineProperty(exports,"waitForEvent",{enumerable:!0,get:function(){return s.waitForEvent}}),exports.walkFlatTree=t.walkFlatTree,Object.defineProperty(exports,"within",{enumerable:!0,get:function(){return s.within}});
|
|
2
|
+
//# sourceMappingURL=testing.cjs.map
|
|
@@ -0,0 +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 barrel re-exports the full testing API for convenience.\n * For tree-shaking, import directly from the focused sub-modules.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\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// waitFor()/waitForEvent() throw @vielzeug/assay's own AssayTimeoutError instead — re-exported\n// below alongside them, since it's a generic DOM-testing concern, not an ore-specific one.\nexport { OreTimeoutError } from '../errors';\n\n// Generic DOM-testing primitives with no ore-specific logic — re-exported from @vielzeug/assay\n// (used by other @vielzeug/* UI packages too) rather than duplicated here.\nexport {\n fire,\n createPointerEvent,\n within,\n waitFor,\n waitForEvent,\n type QueryScope,\n type WaitOptions,\n} from '@vielzeug/assay';\n\nexport { debugFlush, flush, type FlushOptions } from './flush';\nexport { walkFlatTree } from './dom';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { user } from './interactions';\nexport { cleanup, mock, mount, mountComponent, type Fixture, type MountOptions, type MountSetup } from './mount';\nexport { renderHook, type HookFixture } from './render-hook';\n\n/**\n * Register auto-cleanup after each test, and polyfill the jsdom/happy-dom gaps in\n * `ElementInternals`/`FormData`/`<form>.reset()` that `useField()` needs (see\n * `installFormInternalsPolyfill` — safe to call even in a suite with no form-associated\n * components; every patch it installs is a guarded no-op when its target is missing).\n * 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 installFormInternalsPolyfill();\n}\n"],"mappings":"uWAmDA,SAAgB,EAAQ,EAA+C,CACrE,EAAc,EAAA,OAAO,EACrB,EAAA,6BAA6B,CAC/B"}
|
package/dist/testing.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{OreTimeoutError as e}from"./errors.js";import{walkFlatTree as t}from"./testing/dom.js";import{installFormInternalsPolyfill as n}from"./testing/form-internals-polyfill.js";import{debugFlush as r,flush as i}from"./testing/flush.js";import{cleanup as a,mock as o,mount as s,mountComponent as c}from"./testing/mount.js";import{user as l}from"./testing/interactions.js";import{renderHook as u}from"./testing/render-hook.js";import{createPointerEvent as d,fire as f,waitFor as p,waitForEvent as m,within as h}from"@vielzeug/assay";function g(e){e(a),n()}export{e as OreTimeoutError,a as cleanup,d as createPointerEvent,r as debugFlush,f as fire,i as flush,g as install,n as installFormInternalsPolyfill,o as mock,s as mount,c as mountComponent,u as renderHook,l as user,p as waitFor,m as waitForEvent,t as walkFlatTree,h as within};
|
|
2
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +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 barrel re-exports the full testing API for convenience.\n * For tree-shaking, import directly from the focused sub-modules.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\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// waitFor()/waitForEvent() throw @vielzeug/assay's own AssayTimeoutError instead — re-exported\n// below alongside them, since it's a generic DOM-testing concern, not an ore-specific one.\nexport { OreTimeoutError } from '../errors';\n\n// Generic DOM-testing primitives with no ore-specific logic — re-exported from @vielzeug/assay\n// (used by other @vielzeug/* UI packages too) rather than duplicated here.\nexport {\n fire,\n createPointerEvent,\n within,\n waitFor,\n waitForEvent,\n type QueryScope,\n type WaitOptions,\n} from '@vielzeug/assay';\n\nexport { debugFlush, flush, type FlushOptions } from './flush';\nexport { walkFlatTree } from './dom';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { user } from './interactions';\nexport { cleanup, mock, mount, mountComponent, type Fixture, type MountOptions, type MountSetup } from './mount';\nexport { renderHook, type HookFixture } from './render-hook';\n\n/**\n * Register auto-cleanup after each test, and polyfill the jsdom/happy-dom gaps in\n * `ElementInternals`/`FormData`/`<form>.reset()` that `useField()` needs (see\n * `installFormInternalsPolyfill` — safe to call even in a suite with no form-associated\n * components; every patch it installs is a guarded no-op when its target is missing).\n * 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 installFormInternalsPolyfill();\n}\n"],"mappings":"ohBAmDA,SAAgB,EAAQ,EAA+C,CACrE,EAAc,CAAO,EACrB,EAA6B,CAC/B"}
|
package/dist/utils/aria.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aria.cjs","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `
|
|
1
|
+
{"version":3,"file":"aria.cjs","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `aria()` and `bind({ aria: ... })`, where every config key is an ARIA attribute.\n *\n * - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.\n * Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.\n */\n\n/** Normalise an aria config key to a full `aria-*` attribute name. */\nexport const normalizeAriaKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', 'expanded' → 'aria-expanded'\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : `aria-${key}`;\n};\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":"AAaA,IAAa,EAAoB,GAC3B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM,QAAQ,IAIpE,EAAwB,GAC/B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM"}
|
package/dist/utils/aria.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Two flavours are needed across the codebase:
|
|
5
5
|
*
|
|
6
6
|
* - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.
|
|
7
|
-
* Used by `
|
|
7
|
+
* Used by `aria()` and `bind({ aria: ... })`, where every config key is an ARIA attribute.
|
|
8
8
|
*
|
|
9
9
|
* - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.
|
|
10
10
|
* Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.
|
package/dist/utils/aria.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aria.js","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `
|
|
1
|
+
{"version":3,"file":"aria.js","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `aria()` and `bind({ aria: ... })`, where every config key is an ARIA attribute.\n *\n * - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.\n * Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.\n */\n\n/** Normalise an aria config key to a full `aria-*` attribute name. */\nexport const normalizeAriaKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', 'expanded' → 'aria-expanded'\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : `aria-${key}`;\n};\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":"AAaA,IAAa,EAAoB,GAC3B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM,QAAQ,IAIpE,EAAwB,GAC/B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM"}
|
package/dist/utils/dom.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../_dev.cjs");var t=e=>{for(let t=e.length-1;t>=0;t--)e[t]()},
|
|
1
|
+
const e=require("../_dev.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t=e.length-1;t>=0;t--)e[t]()},o=e=>{for(let t of e)t.remove()},s=()=>{let e=[],t=[];return{clear(){a(t),o(e),t=[],e=[]},get nodes(){return e},registerCleanup(e){t.push(e)},setNodes(t){e=t}}},c=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=(t,n,r)=>{let i=n.toLowerCase();if(/^on[a-z]/i.test(n)){e.warn(`Blocked setAttribute("${n}", ...) — inline event handler attributes are not supported. Use @${n.slice(2)} binding syntax instead.`),t.removeAttribute(n);return}if(i===`srcdoc`){e.warn(`Blocked setAttribute("srcdoc", ...) — "srcdoc" holds raw HTML, not a URL, and is not supported via attribute binding. Use raw() with a registered sanitizer if you need to inject trusted HTML.`),t.removeAttribute(n);return}if(r==null||r===!1){t.removeAttribute(n);return}let a=r===!0?`true`:String(r);if(c.has(i)&&l.test(a)){e.warn(`Blocked dangerous URL scheme in attribute "${n}". Only safe URLs are permitted in URL-accepting attributes.`),t.removeAttribute(n);return}t.setAttribute(n,a)},d=(e,t,n,r)=>{if(!e)return()=>{};let i=n;return e.addEventListener(t,i,r),()=>e.removeEventListener(t,i,r)},f=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),p=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=s,exports.isStructuredValue=p,exports.listen=d,exports.removeNodes=o,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 { warn } from '../_dev';\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 * 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. Use raw() with a registered sanitizer if you need to inject trusted HTML.`,\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) return () => {};\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\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"
|
|
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';\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive signal — the\n * same three-way branch `classMap`/`styleMap`/`bind()`'s class-map binding each used to\n * hand-implement separately (with one silently diverging: only the `bind()` copy coerced with\n * `Boolean(...)`). One shared implementation here is the single source of truth.\n */\nexport const resolveMaybeReactive = <T>(value: T | Readable<T> | (() => 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()`, `raw()`, `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. Use raw() with a registered sanitizer if you need to inject trusted HTML.`,\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) return () => {};\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\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"iEAcA,IAAa,EAA2B,GACtC,OAAO,GAAU,WAAc,EAAkB,GAAA,EAAA,EAAA,WAAA,CAAe,CAAK,EAAI,EAAM,MAAQ,EAO5E,EAAmB,SAEnB,EAAoB,GAA0B,EAAM,QAAQ,EAAkB,EAAE,EAEhF,EAAU,GAA8B,CACnD,IAAK,IAAI,EAAI,EAAI,OAAS,EAAG,GAAK,EAAG,IAAK,EAAI,EAAE,CAAE,CACpD,EAEa,EAAe,GAAwB,CAClD,IAAK,IAAM,KAAQ,EACjB,EAAoB,OAAO,CAE/B,EAuBa,MAA+C,CAC1D,IAAI,EAAgB,CAAC,EACjB,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACN,EAAO,CAAQ,EACf,EAAY,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAO,CACT,EACA,gBAAgB,EAAI,CAClB,EAAS,KAAK,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQ,CACV,CACF,CACF,EASM,EAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASK,EACJ,sGAEW,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAM,EAAY,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAK,CAAI,EAAG,CAC1B,EAAA,KACE,yBAAyB,EAAK,oEAAoE,EAAK,MAAM,CAAC,EAAE,yBAClH,EACA,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,IAAc,SAAU,CAC1B,EAAA,KACE,iMACF,EACA,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,GAAO,MAAQ,IAAQ,GAAO,CAChC,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,IAAM,EAAS,IAAQ,GAAO,OAAS,OAAO,CAAG,EAEjD,GAAI,EAAU,IAAI,CAAS,GAAK,EAAoB,KAAK,CAAM,EAAG,CAChE,EAAA,KACE,8CAA8C,EAAK,6DACrD,EACA,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,EAAG,aAAa,EAAM,CAAM,CAC9B,EAEa,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAAC,EAAI,UAAa,CAAC,EAEvB,IAAM,EAA0B,EAIhC,OAFA,EAAG,iBAAiB,EAAM,EAAU,CAAO,MAE9B,EAAG,oBAAoB,EAAM,EAAU,CAAO,CAC7D,EAEa,EAAW,GAAwB,EAAI,QAAQ,SAAW,GAAM,IAAI,EAAE,YAAY,GAAG,EAErF,EAAqB,GAChC,MAAM,QAAQ,CAAK,GAAM,OAAO,GAAU,YAAY"}
|
package/dist/utils/dom.d.ts
CHANGED
|
@@ -1,8 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Low-level DOM utilities used throughout the runtime and binding layers.
|
|
3
3
|
*/
|
|
4
|
+
import { type Readable } from '@vielzeug/ripple';
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a value that may be a plain value, a getter function, or a reactive signal — the
|
|
7
|
+
* same three-way branch `classMap`/`styleMap`/`bind()`'s class-map binding each used to
|
|
8
|
+
* hand-implement separately (with one silently diverging: only the `bind()` copy coerced with
|
|
9
|
+
* `Boolean(...)`). One shared implementation here is the single source of truth.
|
|
10
|
+
*/
|
|
11
|
+
export declare const resolveMaybeReactive: <T>(value: T | Readable<T> | (() => T)) => T;
|
|
12
|
+
/**
|
|
13
|
+
* Characters that can break out of a CSS declaration in an inline style value or property name.
|
|
14
|
+
* Semicolons end the current declaration; braces are meaningful in stylesheet rules but not
|
|
15
|
+
* inline style values, and signal an injection attempt there.
|
|
16
|
+
*/
|
|
17
|
+
export declare const UNSAFE_CSS_CHARS: RegExp;
|
|
18
|
+
export declare const sanitizeCssToken: (value: string) => string;
|
|
4
19
|
export declare const runAll: (fns: (() => void)[]) => void;
|
|
5
20
|
export declare const removeNodes: (nodes: Node[]) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Tracks "whatever is currently rendered in one spot" — a list of live DOM nodes plus the
|
|
23
|
+
* cleanup functions that were registered while mounting them. `clear()` tears both down and
|
|
24
|
+
* resets to empty, ready for the next render.
|
|
25
|
+
*
|
|
26
|
+
* Every directive/binding that swaps its rendered content when a reactive source changes
|
|
27
|
+
* (`when()`, `raw()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this
|
|
28
|
+
* bookkeeping — this is the one shared implementation instead of four independently-maintained
|
|
29
|
+
* `currentNodes`/`currentCleanups` variable pairs.
|
|
30
|
+
*/
|
|
31
|
+
export type ReplaceableSlot = {
|
|
32
|
+
/** Tears down every registered cleanup and removes every tracked node, then resets to empty. */
|
|
33
|
+
clear(): void;
|
|
34
|
+
/** Currently tracked nodes — read after mounting to know what's live. */
|
|
35
|
+
readonly nodes: Node[];
|
|
36
|
+
/** Pass as the `registerCleanup` callback to whatever mounts the next render. */
|
|
37
|
+
registerCleanup(fn: () => void): void;
|
|
38
|
+
/** Replace the tracked node list (call once mounting the next render is complete). */
|
|
39
|
+
setNodes(nodes: Node[]): void;
|
|
40
|
+
};
|
|
41
|
+
export declare const createReplaceableSlot: () => ReplaceableSlot;
|
|
6
42
|
export declare const setAttr: (el: Element, name: string, val: unknown) => void;
|
|
7
43
|
export declare const listen: (el: EventTarget | null | undefined, name: string, handler: EventListener, options?: AddEventListenerOptions) => (() => void);
|
|
8
44
|
export declare const toKebab: (str: string) => string;
|
package/dist/utils/dom.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI7D;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,KAAG,CACiB,CAAC;AAE/F;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAW,CAAC;AAEzC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAA6C,CAAC;AAE/F,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,KAAG,IAE5C,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,IAAI,EAAE,KAAG,IAI3C,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gGAAgG;IAChG,KAAK,IAAI,IAAI,CAAC;IACd,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,eAqBxC,CAAC;AAiCF,eAAO,MAAM,OAAO,GAAI,IAAI,OAAO,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,KAAG,IAuCjE,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,IAAI,WAAW,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,MAAM,EACZ,SAAS,aAAa,EACtB,UAAU,uBAAuB,KAChC,CAAC,MAAM,IAAI,CAQb,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,MAA6D,CAAC;AAEpG,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACW,CAAC"}
|
package/dist/utils/dom.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{warn as e}from"../_dev.js";var t=e=>{for(let t=e.length-1;t>=0;t--)e[t]()},
|
|
1
|
+
import{warn as e}from"../_dev.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=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=(t,n,r)=>{let i=n.toLowerCase();if(/^on[a-z]/i.test(n)){e(`Blocked setAttribute("${n}", ...) — inline event handler attributes are not supported. Use @${n.slice(2)} binding syntax instead.`),t.removeAttribute(n);return}if(i===`srcdoc`){e(`Blocked setAttribute("srcdoc", ...) — "srcdoc" holds raw HTML, not a URL, and is not supported via attribute binding. Use raw() with a registered sanitizer if you need to inject trusted HTML.`),t.removeAttribute(n);return}if(r==null||r===!1){t.removeAttribute(n);return}let a=r===!0?`true`:String(r);if(c.has(i)&&l.test(a)){e(`Blocked dangerous URL scheme in attribute "${n}". Only safe URLs are permitted in URL-accepting attributes.`),t.removeAttribute(n);return}t.setAttribute(n,a)},d=(e,t,n,r)=>{if(!e)return()=>{};let i=n;return e.addEventListener(t,i,r),()=>e.removeEventListener(t,i,r)},f=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),p=e=>Array.isArray(e)||typeof e==`object`&&!!e;export{r as UNSAFE_CSS_CHARS,s as createReplaceableSlot,p as isStructuredValue,d as listen,o as removeNodes,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 { warn } from '../_dev';\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 * 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. Use raw() with a registered sanitizer if you need to inject trusted HTML.`,\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) return () => {};\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\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"
|
|
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';\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive signal — the\n * same three-way branch `classMap`/`styleMap`/`bind()`'s class-map binding each used to\n * hand-implement separately (with one silently diverging: only the `bind()` copy coerced with\n * `Boolean(...)`). One shared implementation here is the single source of truth.\n */\nexport const resolveMaybeReactive = <T>(value: T | Readable<T> | (() => 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()`, `raw()`, `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. Use raw() with a registered sanitizer if you need to inject trusted HTML.`,\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) return () => {};\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\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"gFAcA,IAAa,EAA2B,GACtC,OAAO,GAAU,WAAc,EAAkB,EAAI,EAAW,CAAK,EAAI,EAAM,MAAQ,EAO5E,EAAmB,SAEnB,EAAoB,GAA0B,EAAM,QAAQ,EAAkB,EAAE,EAEhF,EAAU,GAA8B,CACnD,IAAK,IAAI,EAAI,EAAI,OAAS,EAAG,GAAK,EAAG,IAAK,EAAI,EAAE,CAAE,CACpD,EAEa,EAAe,GAAwB,CAClD,IAAK,IAAM,KAAQ,EACjB,EAAoB,OAAO,CAE/B,EAuBa,MAA+C,CAC1D,IAAI,EAAgB,CAAC,EACjB,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACN,EAAO,CAAQ,EACf,EAAY,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAO,CACT,EACA,gBAAgB,EAAI,CAClB,EAAS,KAAK,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQ,CACV,CACF,CACF,EASM,EAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASK,EACJ,sGAEW,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAM,EAAY,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAK,CAAI,EAAG,CAC1B,EACE,yBAAyB,EAAK,oEAAoE,EAAK,MAAM,CAAC,EAAE,yBAClH,EACA,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,IAAc,SAAU,CAC1B,EACE,iMACF,EACA,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,GAAO,MAAQ,IAAQ,GAAO,CAChC,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,IAAM,EAAS,IAAQ,GAAO,OAAS,OAAO,CAAG,EAEjD,GAAI,EAAU,IAAI,CAAS,GAAK,EAAoB,KAAK,CAAM,EAAG,CAChE,EACE,8CAA8C,EAAK,6DACrD,EACA,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,EAAG,aAAa,EAAM,CAAM,CAC9B,EAEa,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAAC,EAAI,UAAa,CAAC,EAEvB,IAAM,EAA0B,EAIhC,OAFA,EAAG,iBAAiB,EAAM,EAAU,CAAO,MAE9B,EAAG,oBAAoB,EAAM,EAAU,CAAO,CAC7D,EAEa,EAAW,GAAwB,EAAI,QAAQ,SAAW,GAAM,IAAI,EAAE,YAAY,GAAG,EAErF,EAAqB,GAChC,MAAM,QAAQ,CAAK,GAAM,OAAO,GAAU,YAAY"}
|
package/dist/utils/id.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=0,t=0,n=Math.random().toString(36).slice(2,6),r=()=>{e=0,t=0},i=(t=`id`)=>`${t}-${++e}`,a=(e=`id`)=>`${e}-${n}${++t}`,o=()=>{t=0};exports._resetIdCounter=r,exports.createId=i,exports.createStableId=a,exports.
|
|
1
|
+
var e=0,t=0,n=Math.random().toString(36).slice(2,6),r=()=>{e=0,t=0},i=(t=`id`)=>`${t}-${++e}`,a=(e=`id`)=>`${e}-${n}${++t}`,o=()=>{t=0};exports._resetIdCounter=r,exports.createId=i,exports.createStableId=a,exports.resetStableIdCounter=o;
|
|
2
2
|
//# sourceMappingURL=id.cjs.map
|
package/dist/utils/id.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.cjs","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["/**\n * Unique ID generation for runtime use (component IDs, label associations, etc.).\n * Template binding no longer uses this for marker IDs — binding targets are resolved\n * via path navigation on cloned template nodes.\n */\n\nlet _idCounter = 0;\nlet _stableCounter = 0;\nconst _tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Resets both ID counters. Called by testing cleanup(). */\nexport const _resetIdCounter = (): void => {\n _idCounter = 0;\n _stableCounter = 0;\n};\n\nexport const createId = (prefix = 'id'): string => `${prefix}-${++_idCounter}`;\n\n/**\n * Generates a stable, unique ID with an optional semantic prefix.\n * Includes a short random tag to prevent collisions across multiple app instances.\n * Format: `${prefix}-${tag}${counter}` — e.g. `field-a3k21`.\n */\nexport const createStableId = (prefix = 'id'): string => `${prefix}-${_tag}${++_stableCounter}`;\n\n/**\n * Resets the
|
|
1
|
+
{"version":3,"file":"id.cjs","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["/**\n * Unique ID generation for runtime use (component IDs, label associations, etc.).\n * Template binding no longer uses this for marker IDs — binding targets are resolved\n * via path navigation on cloned template nodes.\n */\n\nlet _idCounter = 0;\nlet _stableCounter = 0;\nconst _tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Resets both ID counters. Called by testing cleanup(). */\nexport const _resetIdCounter = (): void => {\n _idCounter = 0;\n _stableCounter = 0;\n};\n\n/**\n * Generates a monotonically-increasing, unique-within-this-process ID with an optional prefix.\n * No public reset — this is for uniqueness, not cross-test determinism; use `createStableId()`\n * (and `resetStableIdCounter()`) when a test needs IDs to restart from the same value each run.\n */\nexport const createId = (prefix = 'id'): string => `${prefix}-${++_idCounter}`;\n\n/**\n * Generates a stable, unique ID with an optional semantic prefix.\n * Includes a short random tag to prevent collisions across multiple app instances.\n * Format: `${prefix}-${tag}${counter}` — e.g. `field-a3k21`.\n */\nexport const createStableId = (prefix = 'id'): string => `${prefix}-${_tag}${++_stableCounter}`;\n\n/**\n * Resets the `createStableId()` counter to 0. Use in test `beforeEach` hooks when you need\n * deterministic IDs across test runs.\n *\n * Named to match `createStableId()` precisely — `createId()` has no public reset (it's meant\n * for non-deterministic, monotonically-unique IDs; nothing in the package relies on it\n * restarting from a fixed value across tests).\n */\nexport const resetStableIdCounter = (): void => {\n _stableCounter = 0;\n};\n"],"mappings":"AAMA,IAAI,EAAa,EACb,EAAiB,EACf,EAAO,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAG,CAAC,EAGrC,MAA8B,CACzC,EAAa,EACb,EAAiB,CACnB,EAOa,GAAY,EAAS,OAAiB,GAAG,EAAO,GAAG,EAAE,IAOrD,GAAkB,EAAS,OAAiB,GAAG,EAAO,GAAG,IAAO,EAAE,IAUlE,MAAmC,CAC9C,EAAiB,CACnB"}
|
package/dist/utils/id.d.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* Template binding no longer uses this for marker IDs — binding targets are resolved
|
|
4
4
|
* via path navigation on cloned template nodes.
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* Generates a monotonically-increasing, unique-within-this-process ID with an optional prefix.
|
|
8
|
+
* No public reset — this is for uniqueness, not cross-test determinism; use `createStableId()`
|
|
9
|
+
* (and `resetStableIdCounter()`) when a test needs IDs to restart from the same value each run.
|
|
10
|
+
*/
|
|
6
11
|
export declare const createId: (prefix?: string) => string;
|
|
7
12
|
/**
|
|
8
13
|
* Generates a stable, unique ID with an optional semantic prefix.
|
|
@@ -11,8 +16,12 @@ export declare const createId: (prefix?: string) => string;
|
|
|
11
16
|
*/
|
|
12
17
|
export declare const createStableId: (prefix?: string) => string;
|
|
13
18
|
/**
|
|
14
|
-
* Resets the
|
|
19
|
+
* Resets the `createStableId()` counter to 0. Use in test `beforeEach` hooks when you need
|
|
15
20
|
* deterministic IDs across test runs.
|
|
21
|
+
*
|
|
22
|
+
* Named to match `createStableId()` precisely — `createId()` has no public reset (it's meant
|
|
23
|
+
* for non-deterministic, monotonically-unique IDs; nothing in the package relies on it
|
|
24
|
+
* restarting from a fixed value across tests).
|
|
16
25
|
*/
|
|
17
|
-
export declare const
|
|
26
|
+
export declare const resetStableIdCounter: () => void;
|
|
18
27
|
//# sourceMappingURL=id.d.ts.map
|
package/dist/utils/id.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,eAAO,MAAM,QAAQ,GAAI,eAAa,KAAG,MAAqC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,eAAa,KAAG,MAAgD,CAAC;AAEhG
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,eAAa,KAAG,MAAqC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,eAAa,KAAG,MAAgD,CAAC;AAEhG;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,QAAO,IAEvC,CAAC"}
|
package/dist/utils/id.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=0,t=0,n=Math.random().toString(36).slice(2,6),r=()=>{e=0,t=0},i=(t=`id`)=>`${t}-${++e}`,a=(e=`id`)=>`${e}-${n}${++t}`,o=()=>{t=0};export{r as _resetIdCounter,i as createId,a as createStableId,o as
|
|
1
|
+
var e=0,t=0,n=Math.random().toString(36).slice(2,6),r=()=>{e=0,t=0},i=(t=`id`)=>`${t}-${++e}`,a=(e=`id`)=>`${e}-${n}${++t}`,o=()=>{t=0};export{r as _resetIdCounter,i as createId,a as createStableId,o as resetStableIdCounter};
|
|
2
2
|
//# sourceMappingURL=id.js.map
|
package/dist/utils/id.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.js","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["/**\n * Unique ID generation for runtime use (component IDs, label associations, etc.).\n * Template binding no longer uses this for marker IDs — binding targets are resolved\n * via path navigation on cloned template nodes.\n */\n\nlet _idCounter = 0;\nlet _stableCounter = 0;\nconst _tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Resets both ID counters. Called by testing cleanup(). */\nexport const _resetIdCounter = (): void => {\n _idCounter = 0;\n _stableCounter = 0;\n};\n\nexport const createId = (prefix = 'id'): string => `${prefix}-${++_idCounter}`;\n\n/**\n * Generates a stable, unique ID with an optional semantic prefix.\n * Includes a short random tag to prevent collisions across multiple app instances.\n * Format: `${prefix}-${tag}${counter}` — e.g. `field-a3k21`.\n */\nexport const createStableId = (prefix = 'id'): string => `${prefix}-${_tag}${++_stableCounter}`;\n\n/**\n * Resets the
|
|
1
|
+
{"version":3,"file":"id.js","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["/**\n * Unique ID generation for runtime use (component IDs, label associations, etc.).\n * Template binding no longer uses this for marker IDs — binding targets are resolved\n * via path navigation on cloned template nodes.\n */\n\nlet _idCounter = 0;\nlet _stableCounter = 0;\nconst _tag = Math.random().toString(36).slice(2, 6);\n\n/** @internal Resets both ID counters. Called by testing cleanup(). */\nexport const _resetIdCounter = (): void => {\n _idCounter = 0;\n _stableCounter = 0;\n};\n\n/**\n * Generates a monotonically-increasing, unique-within-this-process ID with an optional prefix.\n * No public reset — this is for uniqueness, not cross-test determinism; use `createStableId()`\n * (and `resetStableIdCounter()`) when a test needs IDs to restart from the same value each run.\n */\nexport const createId = (prefix = 'id'): string => `${prefix}-${++_idCounter}`;\n\n/**\n * Generates a stable, unique ID with an optional semantic prefix.\n * Includes a short random tag to prevent collisions across multiple app instances.\n * Format: `${prefix}-${tag}${counter}` — e.g. `field-a3k21`.\n */\nexport const createStableId = (prefix = 'id'): string => `${prefix}-${_tag}${++_stableCounter}`;\n\n/**\n * Resets the `createStableId()` counter to 0. Use in test `beforeEach` hooks when you need\n * deterministic IDs across test runs.\n *\n * Named to match `createStableId()` precisely — `createId()` has no public reset (it's meant\n * for non-deterministic, monotonically-unique IDs; nothing in the package relies on it\n * restarting from a fixed value across tests).\n */\nexport const resetStableIdCounter = (): void => {\n _stableCounter = 0;\n};\n"],"mappings":"AAMA,IAAI,EAAa,EACb,EAAiB,EACf,EAAO,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAG,CAAC,EAGrC,MAA8B,CACzC,EAAa,EACb,EAAiB,CACnB,EAOa,GAAY,EAAS,OAAiB,GAAG,EAAO,GAAG,EAAE,IAOrD,GAAkB,EAAS,OAAiB,GAAG,EAAO,GAAG,IAAO,EAAE,IAUlE,MAAmC,CAC9C,EAAiB,CACnB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/ore",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Custom element authoring primitives — reactive templates, signals, slots, and automatic lifecycle management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -16,12 +16,6 @@
|
|
|
16
16
|
"import": "./dist/index.js",
|
|
17
17
|
"require": "./dist/index.cjs"
|
|
18
18
|
},
|
|
19
|
-
"./devtools": {
|
|
20
|
-
"source": "./src/devtools.ts",
|
|
21
|
-
"types": "./dist/devtools.d.ts",
|
|
22
|
-
"import": "./dist/devtools.js",
|
|
23
|
-
"require": "./dist/devtools.cjs"
|
|
24
|
-
},
|
|
25
19
|
"./directives": {
|
|
26
20
|
"source": "./src/directives/index.ts",
|
|
27
21
|
"types": "./dist/directives/index.d.ts",
|
|
@@ -62,10 +56,11 @@
|
|
|
62
56
|
"registry": "https://registry.npmjs.org/"
|
|
63
57
|
},
|
|
64
58
|
"engines": {
|
|
65
|
-
"node": ">=
|
|
59
|
+
"node": ">=22"
|
|
66
60
|
},
|
|
67
61
|
"dependencies": {
|
|
68
|
-
"@vielzeug/
|
|
62
|
+
"@vielzeug/assay": "1.0.0",
|
|
63
|
+
"@vielzeug/ripple": "1.3.0"
|
|
69
64
|
},
|
|
70
65
|
"devDependencies": {
|
|
71
66
|
"@types/node": "^26.1.0",
|
package/dist/devtools.cjs
DELETED
package/dist/devtools.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/ore — debug utilities for component update visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugFlush } from '@vielzeug/ore/devtools';\n * ```\n */\n\nimport type { FlushOptions } from './testing/flush';\n\nimport { flush } from './testing/flush';\n\n/**\n * Flushes pending reactive updates with debug logging pre-wired to `console.debug`.\n *\n * Equivalent to `flush({ ...options, logger: console.debug })` but imported from a\n * dedicated sub-path so the `console.debug` reference is tree-shaken from production\n * bundles when this sub-path is not imported.\n *\n * Logs each turn and timing phase to `console.debug` so you can diagnose\n * unexpected update order or timing issues.\n *\n * @example\n * ```ts\n * import { debugFlush } from '@vielzeug/ore/devtools';\n *\n * // in a test\n * await debugFlush();\n * // [flush] starting with maxTurns=5\n * // [flush] microtask turn 1/5\n * // ...\n * ```\n */\nexport async function debugFlush(options?: Omit<FlushOptions, 'logger'>): Promise<void> {\n return flush({ ...options, logger: console.debug });\n}\n"],"mappings":"0GAkCA,eAAsB,EAAW,EAAuD,CACtF,OAAO,EAAA,MAAM,CAAE,GAAG,EAAS,OAAQ,QAAQ,KAAM,CAAC,CACpD"}
|
package/dist/devtools.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @vielzeug/ore — debug utilities for component update visualisation.
|
|
3
|
-
*
|
|
4
|
-
* Import from the dedicated sub-path so it is tree-shaken from production bundles:
|
|
5
|
-
* ```ts
|
|
6
|
-
* import { debugFlush } from '@vielzeug/ore/devtools';
|
|
7
|
-
* ```
|
|
8
|
-
*/
|
|
9
|
-
import type { FlushOptions } from './testing/flush';
|
|
10
|
-
/**
|
|
11
|
-
* Flushes pending reactive updates with debug logging pre-wired to `console.debug`.
|
|
12
|
-
*
|
|
13
|
-
* Equivalent to `flush({ ...options, logger: console.debug })` but imported from a
|
|
14
|
-
* dedicated sub-path so the `console.debug` reference is tree-shaken from production
|
|
15
|
-
* bundles when this sub-path is not imported.
|
|
16
|
-
*
|
|
17
|
-
* Logs each turn and timing phase to `console.debug` so you can diagnose
|
|
18
|
-
* unexpected update order or timing issues.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```ts
|
|
22
|
-
* import { debugFlush } from '@vielzeug/ore/devtools';
|
|
23
|
-
*
|
|
24
|
-
* // in a test
|
|
25
|
-
* await debugFlush();
|
|
26
|
-
* // [flush] starting with maxTurns=5
|
|
27
|
-
* // [flush] microtask turn 1/5
|
|
28
|
-
* // ...
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare function debugFlush(options?: Omit<FlushOptions, 'logger'>): Promise<void>;
|
|
32
|
-
//# sourceMappingURL=devtools.d.ts.map
|
package/dist/devtools.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtF"}
|
package/dist/devtools.js
DELETED
package/dist/devtools.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/ore — debug utilities for component update visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugFlush } from '@vielzeug/ore/devtools';\n * ```\n */\n\nimport type { FlushOptions } from './testing/flush';\n\nimport { flush } from './testing/flush';\n\n/**\n * Flushes pending reactive updates with debug logging pre-wired to `console.debug`.\n *\n * Equivalent to `flush({ ...options, logger: console.debug })` but imported from a\n * dedicated sub-path so the `console.debug` reference is tree-shaken from production\n * bundles when this sub-path is not imported.\n *\n * Logs each turn and timing phase to `console.debug` so you can diagnose\n * unexpected update order or timing issues.\n *\n * @example\n * ```ts\n * import { debugFlush } from '@vielzeug/ore/devtools';\n *\n * // in a test\n * await debugFlush();\n * // [flush] starting with maxTurns=5\n * // [flush] microtask turn 1/5\n * // ...\n * ```\n */\nexport async function debugFlush(options?: Omit<FlushOptions, 'logger'>): Promise<void> {\n return flush({ ...options, logger: console.debug });\n}\n"],"mappings":"2CAkCA,eAAsB,EAAW,EAAuD,CACtF,OAAO,EAAM,CAAE,GAAG,EAAS,OAAQ,QAAQ,KAAM,CAAC,CACpD"}
|
package/dist/testing/events.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var e=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):new MouseEvent(e,t),t={blur:(e,t)=>e.dispatchEvent(new FocusEvent(`blur`,{bubbles:!0,...t})),change:(e,t)=>e.dispatchEvent(new Event(`change`,{bubbles:!0,...t})),click:(e,t)=>e.dispatchEvent(new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),custom(e,t,n){e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n}))},event(e,t){e.dispatchEvent(t)},focus:(e,t,n)=>e.dispatchEvent(new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,t)=>e.dispatchEvent(new Event(`input`,{bubbles:!0,...t})),keyboard:(e,t,n)=>e.dispatchEvent(new KeyboardEvent(t,{bubbles:!0,cancelable:!0,...n})),keyDown:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),keyUp:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),mouse:(e,t,n)=>e.dispatchEvent(new MouseEvent(t,{bubbles:!0,cancelable:!0,...n})),pointerDown:(t,n)=>t.dispatchEvent(e(`pointerdown`,{bubbles:!0,cancelable:!0,...n})),pointerEnter:(t,n)=>t.dispatchEvent(e(`pointerenter`,{bubbles:!1,...n})),pointerLeave:(t,n)=>t.dispatchEvent(e(`pointerleave`,{bubbles:!1,...n})),pointerUp:(t,n)=>t.dispatchEvent(e(`pointerup`,{bubbles:!0,cancelable:!0,...n})),submit:(e,t)=>e.dispatchEvent(new Event(`submit`,{bubbles:!0,cancelable:!0,...t})),touch(e,t,n){typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{bubbles:!0,cancelable:!0,...n})):e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,...n}))}};exports.createPointerEvent=e,exports.fire=t;
|
|
2
|
-
//# sourceMappingURL=events.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"events.cjs","names":[],"sources":["../../src/testing/events.ts"],"sourcesContent":["/**\n * Synchronous DOM event dispatchers for test environments.\n */\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport const createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n return new MouseEvent(type, init);\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Fire low-level DOM events synchronously.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', { detail: 42 });\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */\n custom(el: Element, name: string, opts?: CustomEventInit): void {\n el.dispatchEvent(new CustomEvent(name, { bubbles: true, cancelable: true, composed: false, ...opts }));\n },\n /** Dispatch any pre-built Event instance directly. */\n event(el: Element, event: Event): void {\n el.dispatchEvent(event);\n },\n focus: (el: Element, _name?: string, opts?: FocusEventInit) =>\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n /** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */\n keyboard: (el: Element, type: string, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a MouseEvent with a given type (click, mousedown, etc.). */\n mouse: (el: Element, type: string, opts?: MouseEventInit) =>\n el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */\n touch(el: Element, type: string, opts?: EventInit): void {\n if (typeof TouchEvent !== 'undefined') {\n el.dispatchEvent(new TouchEvent(type, { bubbles: true, cancelable: true, ...opts }));\n } else {\n el.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, ...opts }));\n }\n },\n} as const;\n"],"mappings":"AAMA,IAAa,GAAsB,EAAc,EAAyB,CAAC,IACrE,OAAO,aAAiB,IACnB,IAAI,aAAa,EAAM,CAAI,EAG7B,IAAI,WAAW,EAAM,CAAI,EAarB,EAAO,CAClB,MAAO,EAAa,IAA0B,EAAG,cAAc,IAAI,WAAW,OAAQ,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACjH,QAAS,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAC3G,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAExF,OAAO,EAAa,EAAc,EAA8B,CAC9D,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,GAAO,GAAG,CAAK,CAAC,CAAC,CACvG,EAEA,MAAM,EAAa,EAAoB,CACrC,EAAG,cAAc,CAAK,CACxB,EACA,OAAQ,EAAa,EAAgB,IACnC,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACtE,OAAQ,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAEzG,UAAW,EAAa,EAAc,IACpC,EAAG,cAAc,IAAI,cAAc,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACxF,SAAU,EAAa,IACrB,EAAG,cAAc,IAAI,cAAc,UAAW,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAC7F,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,cAAc,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAE3F,OAAQ,EAAa,EAAc,IACjC,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACrF,aAAc,EAAa,IACzB,EAAG,cAAc,EAAmB,cAAe,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAClG,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,WAAY,EAAa,IACvB,EAAG,cAAc,EAAmB,YAAa,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAChG,QAAS,EAAa,IACpB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEpF,MAAM,EAAa,EAAc,EAAwB,CACnD,OAAO,WAAe,IACxB,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEnF,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,CAExF,CACF"}
|