@vielzeug/ore 1.0.4 → 1.1.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.
Files changed (150) hide show
  1. package/README.md +36 -3
  2. package/dist/_dev.cjs +1 -1
  3. package/dist/_dev.cjs.map +1 -1
  4. package/dist/_dev.js +1 -1
  5. package/dist/_dev.js.map +1 -1
  6. package/dist/aria.cjs +1 -1
  7. package/dist/aria.cjs.map +1 -1
  8. package/dist/aria.d.ts +16 -8
  9. package/dist/aria.d.ts.map +1 -1
  10. package/dist/aria.js +1 -1
  11. package/dist/aria.js.map +1 -1
  12. package/dist/base-element.cjs +1 -1
  13. package/dist/base-element.cjs.map +1 -1
  14. package/dist/base-element.d.ts +30 -1
  15. package/dist/base-element.d.ts.map +1 -1
  16. package/dist/base-element.js +1 -1
  17. package/dist/base-element.js.map +1 -1
  18. package/dist/component-types.d.ts +13 -76
  19. package/dist/component-types.d.ts.map +1 -1
  20. package/dist/context.cjs +1 -1
  21. package/dist/context.cjs.map +1 -1
  22. package/dist/context.d.ts +11 -2
  23. package/dist/context.d.ts.map +1 -1
  24. package/dist/context.js +1 -1
  25. package/dist/context.js.map +1 -1
  26. package/dist/define.cjs.map +1 -1
  27. package/dist/define.d.ts +13 -12
  28. package/dist/define.d.ts.map +1 -1
  29. package/dist/define.js.map +1 -1
  30. package/dist/directives/each.cjs +1 -1
  31. package/dist/directives/each.cjs.map +1 -1
  32. package/dist/directives/each.d.ts +6 -0
  33. package/dist/directives/each.d.ts.map +1 -1
  34. package/dist/directives/each.js +1 -1
  35. package/dist/directives/each.js.map +1 -1
  36. package/dist/directives/raw.cjs +1 -1
  37. package/dist/directives/raw.cjs.map +1 -1
  38. package/dist/directives/raw.d.ts.map +1 -1
  39. package/dist/directives/raw.js +1 -1
  40. package/dist/directives/raw.js.map +1 -1
  41. package/dist/directives/when.cjs +1 -1
  42. package/dist/directives/when.cjs.map +1 -1
  43. package/dist/directives/when.d.ts.map +1 -1
  44. package/dist/directives/when.js +1 -1
  45. package/dist/directives/when.js.map +1 -1
  46. package/dist/errors.cjs +2 -2
  47. package/dist/errors.cjs.map +1 -1
  48. package/dist/errors.d.ts +21 -0
  49. package/dist/errors.d.ts.map +1 -1
  50. package/dist/errors.js +2 -2
  51. package/dist/errors.js.map +1 -1
  52. package/dist/forms/context.cjs +2 -0
  53. package/dist/forms/context.cjs.map +1 -0
  54. package/dist/{form-context.d.ts → forms/context.d.ts} +5 -5
  55. package/dist/forms/context.d.ts.map +1 -0
  56. package/dist/forms/context.js +2 -0
  57. package/dist/forms/context.js.map +1 -0
  58. package/dist/forms/field.cjs +2 -0
  59. package/dist/forms/field.cjs.map +1 -0
  60. package/dist/{form-field.d.ts → forms/field.d.ts} +2 -2
  61. package/dist/forms/field.d.ts.map +1 -0
  62. package/dist/forms/field.js +2 -0
  63. package/dist/forms/field.js.map +1 -0
  64. package/dist/forms/index.d.ts +11 -0
  65. package/dist/forms/index.d.ts.map +1 -0
  66. package/dist/forms.cjs +1 -0
  67. package/dist/forms.js +1 -0
  68. package/dist/host-bind.cjs +1 -1
  69. package/dist/host-bind.cjs.map +1 -1
  70. package/dist/host-bind.d.ts +8 -2
  71. package/dist/host-bind.d.ts.map +1 -1
  72. package/dist/host-bind.js +1 -1
  73. package/dist/host-bind.js.map +1 -1
  74. package/dist/index.cjs +1 -1
  75. package/dist/index.d.ts +8 -8
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +1 -1
  78. package/dist/ore.cjs +2 -2
  79. package/dist/ore.cjs.map +1 -1
  80. package/dist/ore.iife.js +2 -2
  81. package/dist/ore.iife.js.map +1 -1
  82. package/dist/ore.js +2 -2
  83. package/dist/ore.js.map +1 -1
  84. package/dist/props.cjs +1 -1
  85. package/dist/props.cjs.map +1 -1
  86. package/dist/props.d.ts.map +1 -1
  87. package/dist/props.js +1 -1
  88. package/dist/props.js.map +1 -1
  89. package/dist/runtime.cjs +1 -1
  90. package/dist/runtime.cjs.map +1 -1
  91. package/dist/runtime.d.ts +23 -1
  92. package/dist/runtime.d.ts.map +1 -1
  93. package/dist/runtime.js +1 -1
  94. package/dist/runtime.js.map +1 -1
  95. package/dist/slots.cjs +1 -1
  96. package/dist/slots.cjs.map +1 -1
  97. package/dist/slots.d.ts +12 -1
  98. package/dist/slots.d.ts.map +1 -1
  99. package/dist/slots.js +1 -1
  100. package/dist/slots.js.map +1 -1
  101. package/dist/template/bindings.cjs +1 -1
  102. package/dist/template/bindings.cjs.map +1 -1
  103. package/dist/template/bindings.d.ts.map +1 -1
  104. package/dist/template/bindings.js +1 -1
  105. package/dist/template/bindings.js.map +1 -1
  106. package/dist/template/compiler.cjs +1 -1
  107. package/dist/template/compiler.cjs.map +1 -1
  108. package/dist/template/compiler.d.ts +11 -1
  109. package/dist/template/compiler.d.ts.map +1 -1
  110. package/dist/template/compiler.js +1 -1
  111. package/dist/template/compiler.js.map +1 -1
  112. package/dist/template/instantiator.cjs +1 -1
  113. package/dist/template/instantiator.cjs.map +1 -1
  114. package/dist/template/instantiator.d.ts +0 -5
  115. package/dist/template/instantiator.d.ts.map +1 -1
  116. package/dist/template/instantiator.js +1 -1
  117. package/dist/template/instantiator.js.map +1 -1
  118. package/dist/testing/mount.cjs.map +1 -1
  119. package/dist/testing/mount.d.ts +4 -4
  120. package/dist/testing/mount.d.ts.map +1 -1
  121. package/dist/testing/mount.js.map +1 -1
  122. package/dist/testing/render-hook.cjs +1 -1
  123. package/dist/testing/render-hook.cjs.map +1 -1
  124. package/dist/testing/render-hook.d.ts +6 -6
  125. package/dist/testing/render-hook.d.ts.map +1 -1
  126. package/dist/testing/render-hook.js +1 -1
  127. package/dist/testing/render-hook.js.map +1 -1
  128. package/dist/utils/emit.cjs +1 -1
  129. package/dist/utils/emit.cjs.map +1 -1
  130. package/dist/utils/emit.d.ts +18 -2
  131. package/dist/utils/emit.d.ts.map +1 -1
  132. package/dist/utils/emit.js +1 -1
  133. package/dist/utils/emit.js.map +1 -1
  134. package/package.json +14 -4
  135. package/dist/context-bag.cjs +0 -2
  136. package/dist/context-bag.cjs.map +0 -1
  137. package/dist/context-bag.d.ts +0 -12
  138. package/dist/context-bag.d.ts.map +0 -1
  139. package/dist/context-bag.js +0 -2
  140. package/dist/context-bag.js.map +0 -1
  141. package/dist/form-context.cjs +0 -2
  142. package/dist/form-context.cjs.map +0 -1
  143. package/dist/form-context.d.ts.map +0 -1
  144. package/dist/form-context.js +0 -2
  145. package/dist/form-context.js.map +0 -1
  146. package/dist/form-field.cjs +0 -2
  147. package/dist/form-field.cjs.map +0 -1
  148. package/dist/form-field.d.ts.map +0 -1
  149. package/dist/form-field.js +0 -2
  150. package/dist/form-field.js.map +0 -1
@@ -2,7 +2,7 @@
2
2
  * Component mounting utilities for test environments.
3
3
  */
4
4
  import type { Readable } from '@vielzeug/ripple';
5
- import { type ComponentDefinition, type SetupContextBag } from '../component-types';
5
+ import { type ComponentDefinition } from '../component-types';
6
6
  import { type HTMLResult } from '../types/bindings';
7
7
  import { type FlushOptions } from './flush';
8
8
  import { type QueryScope } from './query';
@@ -48,13 +48,13 @@ export interface MountOptions {
48
48
  /** Parent container (default: document.body) */
49
49
  container?: HTMLElement;
50
50
  /** Extra component options when passing an inline setup function */
51
- componentOptions?: Omit<ComponentDefinition<any, any>, 'setup'>;
51
+ componentOptions?: Omit<ComponentDefinition<any>, 'setup'>;
52
52
  }
53
53
  type MountProps = {
54
54
  readonly [x: string]: Readable<unknown>;
55
55
  };
56
56
  export type MountSetup = {
57
- bivarianceHack: (props: MountProps, ctx: SetupContextBag<any>) => HTMLResult | null | Promise<HTMLResult | null>;
57
+ bivarianceHack: (props: MountProps) => HTMLResult | null | Promise<HTMLResult | null>;
58
58
  }['bivarianceHack'];
59
59
  export declare const _mountedElements: HTMLElement[];
60
60
  export declare let _componentTagCounter: number;
@@ -87,7 +87,7 @@ export declare function mount<T extends HTMLElement = HTMLElement>(tagOrSetup: M
87
87
  * setup: (props) => html`<div>${props.count}</div>`,
88
88
  * });
89
89
  */
90
- export declare function mountComponent<T extends HTMLElement = HTMLElement>(tag: string, definition: ComponentDefinition<any, any>, options?: MountOptions): Promise<Fixture<T>>;
90
+ export declare function mountComponent<T extends HTMLElement = HTMLElement>(tag: string, definition: ComponentDefinition<any>, options?: MountOptions): Promise<Fixture<T>>;
91
91
  /**
92
92
  * Register a stub custom element (no-op if already defined).
93
93
  *
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"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"}
@@ -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, type SetupContextBag } 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, any>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying setup context specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps, ctx: SetupContextBag<any>) => 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, 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, 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 await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\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, 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 { 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 await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\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,2 +1,2 @@
1
- const e=require("./flush.cjs"),t=require("../runtime.cjs"),n=require("../context-bag.cjs"),r=require("../props.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,mountCallbacks:f},m;try{u.run(()=>{t.runWithContext(p,()=>{let e=n.createContextBag(d),t=c?r.createProps(d,c):{};m=l(t,e)})});for(let e of f)try{u.run(()=>{t.runWithContext(p,()=>{let t=e();typeof t==`function`&&(0,i.onCleanup)(t)})})}catch{}}catch(e){throw u.dispose(),d.remove(),e}await e.flush();let h=!1;function g(){h||(h=!0,u.dispose(),d.remove())}return{dispose:g,get disposed(){return h},async flush(){await e.flush()},result:m,[Symbol.dispose](){g()}}}exports.renderHook=a;
1
+ const e=require("./flush.cjs"),t=require("../runtime.cjs"),n=require("../props.cjs");let r=require("@vielzeug/ripple");async function i(i,a){let o=typeof i!=`function`,s=o?i:void 0,c=o?a:i,l=(0,r.scope)(),u=document.createElement(`div`);document.body.appendChild(u);let d=[],f={element:u,mountCallbacks:d},p;try{l.run(()=>{t.runWithContext(f,()=>{let e=s?n.createProps(u,s):{};p=c(e)})});for(let e of d)try{l.run(()=>{t.runWithContext(f,()=>{let t=e();typeof t==`function`&&(0,r.onCleanup)(t)})})}catch{}}catch(e){throw l.dispose(),u.remove(),e}await e.flush();let m=!1;function h(){m||(m=!0,l.dispose(),u.remove())}return{dispose:h,get disposed(){return m},async flush(){await e.flush()},result:p,[Symbol.dispose](){h()}}}exports.renderHook=i;
2
2
  //# sourceMappingURL=render-hook.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.cjs","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { onCleanup as _onCleanup, scope as _scope } from '@vielzeug/ripple';\n\nimport type { SetupContextBag } from '../component-types';\n\nimport { createContextBag } from '../context-bag';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n}\n\n/**\n * Run a setup function in a component-like context and return the result.\n * The callback receives the same `SetupContextBag` as a real `setup()` function.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook((_props, ctx) => {\n * const count = signal(0);\n * ctx.onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(\n setup: (_props: Record<never, never>, ctx: SetupContextBag) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>, ctx: SetupContextBag) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>, ctx: SetupContextBag) => T) | D,\n maybeSetup?: (props: InferProps<D>, ctx: SetupContextBag) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>, ctx: SetupContextBag) => T);\n\n const hostScope = _scope();\n // Minimal host element so getCurrentElement() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n const mountCallbacks: Array<() => void | (() => void)> = [];\n const ctx = { element: hostEl, mountCallbacks };\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const contextBag = createContextBag(hostEl);\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>, contextBag);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') _onCleanup(cleanup);\n });\n });\n } catch {\n /* test will surface the error via result */\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"uJAyDA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,GAAA,EAAA,EAAA,MAAA,CAAmB,EAEnB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAEhC,IAAM,EAAmD,CAAC,EACpD,EAAM,CAAE,QAAS,EAAQ,gBAAe,EAE1C,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAa,EAAA,iBAAiB,CAAM,EACpC,EAAQ,EAAW,EAAA,YAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,EAAwB,CAAU,CACnD,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,aAAY,EAAA,EAAA,UAAA,CAAW,CAAO,CACvD,CAAC,CACH,CAAC,CACH,MAAQ,CAER,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAA,MAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EAChB,CAEA,MAAO,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAA,MAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
1
+ {"version":3,"file":"render-hook.cjs","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { onCleanup as _onCleanup, scope as _scope } from '@vielzeug/ripple';\n\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n}\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = _scope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n const mountCallbacks: Array<() => void | (() => void)> = [];\n const ctx = { element: hostEl, mountCallbacks };\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') _onCleanup(cleanup);\n });\n });\n } catch {\n /* test will surface the error via result */\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"uHAqDA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,GAAA,EAAA,EAAA,MAAA,CAAmB,EAEnB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAEhC,IAAM,EAAmD,CAAC,EACpD,EAAM,CAAE,QAAS,EAAQ,gBAAe,EAE1C,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAQ,EAAW,EAAA,YAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,aAAY,EAAA,EAAA,UAAA,CAAW,CAAO,CACvD,CAAC,CACH,CAAC,CACH,MAAQ,CAER,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAA,MAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EAChB,CAEA,MAAO,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAA,MAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
@@ -2,7 +2,6 @@
2
2
  * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation
3
3
  * without rendering a full component template.
4
4
  */
5
- import type { SetupContextBag } from '../component-types';
6
5
  import { type InferProps, type PropInputDefs } from '../props';
7
6
  export interface HookFixture<T> {
8
7
  /** Delegates to `dispose()`. Enables `using` declarations. */
@@ -18,13 +17,14 @@ export interface HookFixture<T> {
18
17
  }
19
18
  /**
20
19
  * Run a setup function in a component-like context and return the result.
21
- * The callback receives the same `SetupContextBag` as a real `setup()` function.
20
+ * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real
21
+ * `setup()`, since they resolve the same implicit current-component context.
22
22
  *
23
23
  * @example No props
24
24
  * ```ts
25
- * const { result, flush } = await renderHook((_props, ctx) => {
25
+ * const { result, flush } = await renderHook(() => {
26
26
  * const count = signal(0);
27
- * ctx.onMounted(() => { count.value = 1; });
27
+ * onMounted(() => { count.value = 1; });
28
28
  * return count;
29
29
  * });
30
30
  * expect(result.value).toBe(1);
@@ -39,6 +39,6 @@ export interface HookFixture<T> {
39
39
  * expect(result.value).toBe('hello');
40
40
  * ```
41
41
  */
42
- export declare function renderHook<T>(setup: (_props: Record<never, never>, ctx: SetupContextBag) => T): Promise<HookFixture<T>>;
43
- export declare function renderHook<D extends PropInputDefs, T>(propDefs: D, setup: (props: InferProps<D>, ctx: SetupContextBag) => T): Promise<HookFixture<T>>;
42
+ export declare function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;
43
+ export declare function renderHook<D extends PropInputDefs, T>(propDefs: D, setup: (props: InferProps<D>) => T): Promise<HookFixture<T>>;
44
44
  //# sourceMappingURL=render-hook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.d.ts","sourceRoot":"","sources":["../../src/testing/render-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAI5E,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,qDAAqD;IACrD,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qCAAqC;IACrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+CAA+C;IAC/C,MAAM,EAAE,CAAC,CAAC;CACX;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,eAAe,KAAK,CAAC,GAC/D,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3B,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EAAE,CAAC,EACzD,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,KAAK,CAAC,GACvD,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"render-hook.d.ts","sourceRoot":"","sources":["../../src/testing/render-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAI5E,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,qDAAqD;IACrD,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qCAAqC;IACrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+CAA+C;IAC/C,MAAM,EAAE,CAAC,CAAC;CACX;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACzG,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EAAE,CAAC,EACzD,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -1,2 +1,2 @@
1
- import{flush as e}from"./flush.js";import{runWithContext as t}from"../runtime.js";import{createContextBag as n}from"../context-bag.js";import{createProps as r}from"../props.js";import{onCleanup as i,scope as a}from"@vielzeug/ripple";async function o(o,s){let c=typeof o!=`function`,l=c?o:void 0,u=c?s:o,d=a(),f=document.createElement(`div`);document.body.appendChild(f);let p=[],m={element:f,mountCallbacks:p},h;try{d.run(()=>{t(m,()=>{let e=n(f),t=l?r(f,l):{};h=u(t,e)})});for(let e of p)try{d.run(()=>{t(m,()=>{let t=e();typeof t==`function`&&i(t)})})}catch{}}catch(e){throw d.dispose(),f.remove(),e}await e();let g=!1;function _(){g||(g=!0,d.dispose(),f.remove())}return{dispose:_,get disposed(){return g},async flush(){await e()},result:h,[Symbol.dispose](){_()}}}export{o as renderHook};
1
+ import{flush as e}from"./flush.js";import{runWithContext as t}from"../runtime.js";import{createProps as n}from"../props.js";import{onCleanup as r,scope as i}from"@vielzeug/ripple";async function a(a,o){let s=typeof a!=`function`,c=s?a:void 0,l=s?o:a,u=i(),d=document.createElement(`div`);document.body.appendChild(d);let f=[],p={element:d,mountCallbacks:f},m;try{u.run(()=>{t(p,()=>{let e=c?n(d,c):{};m=l(e)})});for(let e of f)try{u.run(()=>{t(p,()=>{let t=e();typeof t==`function`&&r(t)})})}catch{}}catch(e){throw u.dispose(),d.remove(),e}await e();let h=!1;function g(){h||(h=!0,u.dispose(),d.remove())}return{dispose:g,get disposed(){return h},async flush(){await e()},result:m,[Symbol.dispose](){g()}}}export{a as renderHook};
2
2
  //# sourceMappingURL=render-hook.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.js","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { onCleanup as _onCleanup, scope as _scope } from '@vielzeug/ripple';\n\nimport type { SetupContextBag } from '../component-types';\n\nimport { createContextBag } from '../context-bag';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n}\n\n/**\n * Run a setup function in a component-like context and return the result.\n * The callback receives the same `SetupContextBag` as a real `setup()` function.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook((_props, ctx) => {\n * const count = signal(0);\n * ctx.onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(\n setup: (_props: Record<never, never>, ctx: SetupContextBag) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>, ctx: SetupContextBag) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>, ctx: SetupContextBag) => T) | D,\n maybeSetup?: (props: InferProps<D>, ctx: SetupContextBag) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>, ctx: SetupContextBag) => T);\n\n const hostScope = _scope();\n // Minimal host element so getCurrentElement() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n const mountCallbacks: Array<() => void | (() => void)> = [];\n const ctx = { element: hostEl, mountCallbacks };\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const contextBag = createContextBag(hostEl);\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>, contextBag);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') _onCleanup(cleanup);\n });\n });\n } catch {\n /* test will surface the error via result */\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"yOAyDA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,EAAY,EAAO,EAEnB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAEhC,IAAM,EAAmD,CAAC,EACpD,EAAM,CAAE,QAAS,EAAQ,gBAAe,EAE1C,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAa,EAAiB,CAAM,EACpC,EAAQ,EAAW,EAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,EAAwB,CAAU,CACnD,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,YAAY,EAAW,CAAO,CACvD,CAAC,CACH,CAAC,CACH,MAAQ,CAER,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EAChB,CAEA,MAAO,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
1
+ {"version":3,"file":"render-hook.js","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { onCleanup as _onCleanup, scope as _scope } from '@vielzeug/ripple';\n\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n}\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = _scope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n const mountCallbacks: Array<() => void | (() => void)> = [];\n const ctx = { element: hostEl, mountCallbacks };\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') _onCleanup(cleanup);\n });\n });\n } catch {\n /* test will surface the error via result */\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"oLAqDA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,EAAY,EAAO,EAEnB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAEhC,IAAM,EAAmD,CAAC,EACpD,EAAM,CAAE,QAAS,EAAQ,gBAAe,EAE1C,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAQ,EAAW,EAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,YAAY,EAAW,CAAO,CACvD,CAAC,CACH,CAAC,CACH,MAAQ,CAER,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EAChB,CAEA,MAAO,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
@@ -1,2 +1,2 @@
1
- var e={bubbles:!0,cancelable:!0,composed:!1},t=t=>((n,...r)=>{let i=r.length>0?{...e,detail:r[0]}:e;t.dispatchEvent(new CustomEvent(String(n),i))});exports.createEmitFn=t;
1
+ const e=require("../runtime.cjs");var t={bubbles:!0,cancelable:!0,composed:!1},n=()=>{let n=e.getHost();return((e,...r)=>{let i=r.length>0?{...t,detail:r[0]}:t;n.dispatchEvent(new CustomEvent(String(e),i))})};exports.useEmit=n;
2
2
  //# sourceMappingURL=emit.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"emit.cjs","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission factory for components.\n */\n\ntype NoDetail = void | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\ntype StrictEmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): void;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): void;\n};\n\ntype LooseEmitFn = (event: string, detail?: unknown) => void;\n\nexport type EmitFn<T extends Record<string, unknown>> = StrictEmitFn<T> & LooseEmitFn;\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: false };\n\nexport const createEmitFn = <T extends Record<string, unknown>>(el: HTMLElement): EmitFn<T> => {\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n el.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"AAkBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAM,EAEnE,EAAmD,KACrD,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,EAAG,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CAC3E"}
1
+ {"version":3,"file":"emit.cjs","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = void | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\ntype StrictEmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): void;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): void;\n};\n\ntype LooseEmitFn = (event: string, detail?: unknown) => void;\n\nexport type EmitFn<T extends Record<string, unknown>> = StrictEmitFn<T> & LooseEmitFn;\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: false };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()` — the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n * emit('change', { value: 'ok' });\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"kCAoBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAM,EAkBnE,MAAsF,CACjG,IAAM,EAAO,EAAA,QAAQ,EAErB,QAAS,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,EAAK,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CAC7E,EACF"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Type-safe custom event emission factory for components.
2
+ * Type-safe custom event emission for components.
3
3
  */
4
4
  type NoDetail = void | undefined | never;
5
5
  type KeysWithoutDetail<T extends Record<string, unknown>> = {
@@ -11,6 +11,22 @@ type StrictEmitFn<T extends Record<string, unknown>> = {
11
11
  };
12
12
  type LooseEmitFn = (event: string, detail?: unknown) => void;
13
13
  export type EmitFn<T extends Record<string, unknown>> = StrictEmitFn<T> & LooseEmitFn;
14
- export declare const createEmitFn: <T extends Record<string, unknown>>(el: HTMLElement) => EmitFn<T>;
14
+ /**
15
+ * Returns a typed `emit()` function bound to the current component's host element.
16
+ * Call once during `setup()` — the `Emits` type parameter maps event names to
17
+ * their `detail` payload type.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * type Events = { close: undefined; change: { value: string } };
22
+ *
23
+ * setup(props) {
24
+ * const emit = useEmit<Events>();
25
+ * emit('close');
26
+ * emit('change', { value: 'ok' });
27
+ * }
28
+ * ```
29
+ */
30
+ export declare const useEmit: <T extends Record<string, unknown> = Record<string, never>>() => EmitFn<T>;
15
31
  export {};
16
32
  //# sourceMappingURL=emit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/utils/emit.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC;AACzC,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;KACzD,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK;CACtD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACrD,CAAC,CAAC,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACjD,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAClF,CAAC;AAEF,KAAK,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAE7D,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC;AAItF,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,WAAW,KAAG,MAAM,CAAC,CAAC,CAMzF,CAAC"}
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/utils/emit.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,KAAK,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC;AACzC,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;KACzD,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK;CACtD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACrD,CAAC,CAAC,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACjD,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAClF,CAAC;AAEF,KAAK,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAE7D,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC;AAItF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,OAAK,MAAM,CAAC,CAAC,CAQ7F,CAAC"}
@@ -1,2 +1,2 @@
1
- var e={bubbles:!0,cancelable:!0,composed:!1},t=t=>((n,...r)=>{let i=r.length>0?{...e,detail:r[0]}:e;t.dispatchEvent(new CustomEvent(String(n),i))});export{t as createEmitFn};
1
+ import{getHost as e}from"../runtime.js";var t={bubbles:!0,cancelable:!0,composed:!1},n=()=>{let n=e();return((e,...r)=>{let i=r.length>0?{...t,detail:r[0]}:t;n.dispatchEvent(new CustomEvent(String(e),i))})};export{n as useEmit};
2
2
  //# sourceMappingURL=emit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"emit.js","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission factory for components.\n */\n\ntype NoDetail = void | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\ntype StrictEmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): void;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): void;\n};\n\ntype LooseEmitFn = (event: string, detail?: unknown) => void;\n\nexport type EmitFn<T extends Record<string, unknown>> = StrictEmitFn<T> & LooseEmitFn;\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: false };\n\nexport const createEmitFn = <T extends Record<string, unknown>>(el: HTMLElement): EmitFn<T> => {\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n el.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"AAkBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAM,EAEnE,EAAmD,KACrD,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,EAAG,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CAC3E"}
1
+ {"version":3,"file":"emit.js","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = void | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\ntype StrictEmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): void;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): void;\n};\n\ntype LooseEmitFn = (event: string, detail?: unknown) => void;\n\nexport type EmitFn<T extends Record<string, unknown>> = StrictEmitFn<T> & LooseEmitFn;\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: false };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()` — the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n * emit('change', { value: 'ok' });\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"wCAoBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAM,EAkBnE,MAAsF,CACjG,IAAM,EAAO,EAAQ,EAErB,QAAS,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,EAAK,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CAC7E,EACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/ore",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Custom element authoring primitives — reactive templates, signals, slots, and automatic lifecycle management",
5
5
  "type": "module",
6
6
  "files": [
@@ -28,6 +28,12 @@
28
28
  "import": "./dist/directives.js",
29
29
  "require": "./dist/directives.cjs"
30
30
  },
31
+ "./forms": {
32
+ "source": "./src/forms/index.ts",
33
+ "types": "./dist/forms/index.d.ts",
34
+ "import": "./dist/forms.js",
35
+ "require": "./dist/forms.cjs"
36
+ },
31
37
  "./observers": {
32
38
  "source": "./src/observers/index.ts",
33
39
  "types": "./dist/observers/index.d.ts",
@@ -44,8 +50,8 @@
44
50
  "scripts": {
45
51
  "build": "vite build && pnpm run build:bundle && pnpm run build:types",
46
52
  "build:types": "tsc -p tsconfig.declarations.json",
47
- "fix": "eslint --fix src",
48
- "lint": "eslint src",
53
+ "fix": "eslint --fix src vitest.setup.ts",
54
+ "lint": "eslint src vitest.setup.ts",
49
55
  "prepublishOnly": "pnpm run build",
50
56
  "preview": "vite preview",
51
57
  "test": "vitest",
@@ -55,14 +61,18 @@
55
61
  "access": "public",
56
62
  "registry": "https://registry.npmjs.org/"
57
63
  },
64
+ "engines": {
65
+ "node": ">=18"
66
+ },
58
67
  "dependencies": {
59
68
  "@vielzeug/ripple": "workspace:*"
60
69
  },
61
70
  "devDependencies": {
62
71
  "@types/node": "^26.1.0",
72
+ "axe-core": "^4.12.1",
63
73
  "jsdom": "^29.1.1",
64
74
  "typescript": "~6.0.3",
65
75
  "vite": "^8.1.3",
66
- "vitest": "^4.1.9"
76
+ "vitest": "^4.1.10"
67
77
  }
68
78
  }
@@ -1,2 +0,0 @@
1
- const e=require("./runtime.cjs"),t=require("./aria.cjs"),n=require("./context.cjs"),r=require("./host-bind.cjs"),i=require("./slots.cjs"),a=require("./utils/emit.cjs");var o=o=>{let s=i.createSlots(o),c=r.createBind(o);return{aria:t.createAriaFn(),bind:c,el:o,emit:a.createEmitFn(o),inject:n.inject,onCleanup:e.onCleanup,onElement:e.onElement,onEvent:e.onEvent,onMounted:e.onMounted,provide:(e,t)=>n.provideOnElement(o,e,t),slots:s,watch:e.effect}};exports.createContextBag=o;
2
- //# sourceMappingURL=context-bag.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-bag.cjs","names":[],"sources":["../src/context-bag.ts"],"sourcesContent":["/**\n * Factory for the SetupContextBag — shared between BaseElement and renderHook.\n * Keeps the real component lifecycle and the test harness in sync automatically.\n */\n\nimport type { SetupContextBag } from './component-types';\nimport type { InjectionKey } from './context';\n\nimport { createAriaFn } from './aria';\nimport { inject, provideOnElement } from './context';\nimport { createBind } from './host-bind';\nimport { effect, onCleanup, onElement, onEvent, onMounted, type OnMountedCallback } from './runtime';\nimport { type ComponentSlots, createSlots } from './slots';\nimport { createEmitFn } from './utils/emit';\n\nexport type ContextBagInit = {\n el: HTMLElement;\n mountCallbacks: OnMountedCallback[];\n};\n\nexport const createContextBag = (el: HTMLElement): SetupContextBag<Record<string, never>, string> => {\n const slots = createSlots(el) as ComponentSlots<string>;\n\n const bind = createBind(el);\n\n return {\n aria: createAriaFn(),\n bind,\n el,\n emit: createEmitFn(el),\n inject,\n onCleanup,\n onElement,\n onEvent,\n onMounted,\n provide: <T>(key: InjectionKey<T>, value: T) => provideOnElement(el, key, value),\n slots,\n watch: effect,\n };\n};\n"],"mappings":"wKAoBA,IAAa,EAAoB,GAAoE,CACnG,IAAM,EAAQ,EAAA,YAAY,CAAE,EAEtB,EAAO,EAAA,WAAW,CAAE,EAE1B,MAAO,CACL,KAAM,EAAA,aAAa,EACnB,OACA,KACA,KAAM,EAAA,aAAa,CAAE,EACrB,OAAA,EAAA,OACA,UAAA,EAAA,UACA,UAAA,EAAA,UACA,QAAA,EAAA,QACA,UAAA,EAAA,UACA,SAAa,EAAsB,IAAa,EAAA,iBAAiB,EAAI,EAAK,CAAK,EAC/E,QACA,MAAO,EAAA,MACT,CACF"}
@@ -1,12 +0,0 @@
1
- /**
2
- * Factory for the SetupContextBag — shared between BaseElement and renderHook.
3
- * Keeps the real component lifecycle and the test harness in sync automatically.
4
- */
5
- import type { SetupContextBag } from './component-types';
6
- import { type OnMountedCallback } from './runtime';
7
- export type ContextBagInit = {
8
- el: HTMLElement;
9
- mountCallbacks: OnMountedCallback[];
10
- };
11
- export declare const createContextBag: (el: HTMLElement) => SetupContextBag<Record<string, never>, string>;
12
- //# sourceMappingURL=context-bag.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-bag.d.ts","sourceRoot":"","sources":["../src/context-bag.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMzD,OAAO,EAAoD,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAIrG,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,WAAW,CAAC;IAChB,cAAc,EAAE,iBAAiB,EAAE,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,IAAI,WAAW,KAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAmB/F,CAAC"}
@@ -1,2 +0,0 @@
1
- import{effect as e,onCleanup as t,onElement as n,onEvent as r,onMounted as i}from"./runtime.js";import{createAriaFn as a}from"./aria.js";import{inject as o,provideOnElement as s}from"./context.js";import{createBind as c}from"./host-bind.js";import{createSlots as l}from"./slots.js";import{createEmitFn as u}from"./utils/emit.js";var d=d=>{let f=l(d),p=c(d);return{aria:a(),bind:p,el:d,emit:u(d),inject:o,onCleanup:t,onElement:n,onEvent:r,onMounted:i,provide:(e,t)=>s(d,e,t),slots:f,watch:e}};export{d as createContextBag};
2
- //# sourceMappingURL=context-bag.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-bag.js","names":[],"sources":["../src/context-bag.ts"],"sourcesContent":["/**\n * Factory for the SetupContextBag — shared between BaseElement and renderHook.\n * Keeps the real component lifecycle and the test harness in sync automatically.\n */\n\nimport type { SetupContextBag } from './component-types';\nimport type { InjectionKey } from './context';\n\nimport { createAriaFn } from './aria';\nimport { inject, provideOnElement } from './context';\nimport { createBind } from './host-bind';\nimport { effect, onCleanup, onElement, onEvent, onMounted, type OnMountedCallback } from './runtime';\nimport { type ComponentSlots, createSlots } from './slots';\nimport { createEmitFn } from './utils/emit';\n\nexport type ContextBagInit = {\n el: HTMLElement;\n mountCallbacks: OnMountedCallback[];\n};\n\nexport const createContextBag = (el: HTMLElement): SetupContextBag<Record<string, never>, string> => {\n const slots = createSlots(el) as ComponentSlots<string>;\n\n const bind = createBind(el);\n\n return {\n aria: createAriaFn(),\n bind,\n el,\n emit: createEmitFn(el),\n inject,\n onCleanup,\n onElement,\n onEvent,\n onMounted,\n provide: <T>(key: InjectionKey<T>, value: T) => provideOnElement(el, key, value),\n slots,\n watch: effect,\n };\n};\n"],"mappings":"yUAoBA,IAAa,EAAoB,GAAoE,CACnG,IAAM,EAAQ,EAAY,CAAE,EAEtB,EAAO,EAAW,CAAE,EAE1B,MAAO,CACL,KAAM,EAAa,EACnB,OACA,KACA,KAAM,EAAa,CAAE,EACrB,SACA,YACA,YACA,UACA,YACA,SAAa,EAAsB,IAAa,EAAiB,EAAI,EAAK,CAAK,EAC/E,QACA,MAAO,CACT,CACF"}
@@ -1,2 +0,0 @@
1
- const e=require("./context.cjs");let t=require("@vielzeug/ripple");var n=e.createContext(`ore:form-context`);function r(e={}){let n=(0,t.signal)([]),r=(0,t.signal)(!1),i=(0,t.signal)(!1),a=(0,t.signal)(null);return{clearStatus:()=>{i.value=!1,a.value=null,e.onReset?.()},dirty:i,error:a,markDirty:()=>{i.value=!0},registerField:e=>(n.value=[...n.value,e],()=>{n.value=n.value.filter(t=>t!==e)}),submit:async t=>{if(t?.preventDefault(),!r.value){r.value=!0,a.value=null;try{await e.onSubmit?.(t),i.value=!1}catch(e){a.value=e}finally{r.value=!1}}},submitting:r,valid:(0,t.computed)(()=>n.value.every(e=>e.value))}}exports.FORM_CONTEXT_KEY=n,exports.createFormContext=r;
2
- //# sourceMappingURL=form-context.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-context.cjs","names":[],"sources":["../src/form-context.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport { createContext } from './context';\n\n/**\n * Context shape injected by child field components.\n * Contains only what a field needs — not submit/error/status APIs.\n */\nexport type FormFieldContext = {\n /** Whether any field has been touched (interacted with). Set via markDirty(). */\n readonly dirty: Readable<boolean>;\n markDirty(): void;\n /**\n * Register a field's validity signal with the form context.\n * Returns a cleanup function.\n */\n registerField(validity: Readable<boolean>): () => void;\n};\n\n/**\n * Full form controller returned by `createFormContext`.\n * Extends `FormFieldContext` with submission and status APIs.\n */\nexport type FormController = FormFieldContext & {\n /**\n * Clear `dirty` and `error` status signals and invoke `onReset` if provided.\n * Note: this does not reset individual field values — pass an `onReset` callback\n * to handle value restoration.\n */\n clearStatus(): void;\n /** The last submit error, or null if the last submit succeeded. */\n readonly error: Readable<unknown>;\n /** Submit the form programmatically. */\n submit(e?: Event): Promise<void>;\n /** Whether the form is currently submitting. */\n readonly submitting: Readable<boolean>;\n /** Whether all fields in the form are valid. */\n readonly valid: Readable<boolean>;\n};\n\nexport const FORM_CONTEXT_KEY = createContext<FormFieldContext>('ore:form-context');\n\n/**\n * Create a `FormController` for coordinating form state across child field components.\n * Call `ctx.provide(FORM_CONTEXT_KEY, form)` to make it available to descendants.\n *\n * @example\n * ```ts\n * define('my-form', {\n * setup(_props, ctx) {\n * const form = createFormContext({ onSubmit: async (e) => { ... } });\n * ctx.provide(FORM_CONTEXT_KEY, form);\n * return html`<form @submit=${form.submit}><slot></slot></form>`;\n * }\n * });\n * ```\n */\nexport function createFormContext(\n options: {\n onReset?: () => void;\n onSubmit?: (e?: Event) => void | Promise<void>;\n } = {},\n): FormController {\n const fieldValiditySignals = signal<Array<Readable<boolean>>>([]);\n const submitting = signal(false);\n const dirty = signal(false);\n const error = signal<unknown>(null);\n\n // Note: [].every() is vacuously true, so valid is true when no fields are registered.\n // This is intentional — a form with no fields is considered valid (nothing to invalidate it).\n const valid = computed(() => fieldValiditySignals.value.every((s) => s.value));\n\n const submit = async (e?: Event): Promise<void> => {\n e?.preventDefault();\n\n if (submitting.value) return;\n\n submitting.value = true;\n error.value = null;\n\n try {\n await options.onSubmit?.(e);\n dirty.value = false;\n } catch (err) {\n error.value = err;\n } finally {\n submitting.value = false;\n }\n };\n\n const clearStatus = (): void => {\n dirty.value = false;\n error.value = null;\n options.onReset?.();\n };\n\n const markDirty = (): void => {\n dirty.value = true;\n };\n\n const registerField = (validity: Readable<boolean>): (() => void) => {\n fieldValiditySignals.value = [...fieldValiditySignals.value, validity];\n\n return () => {\n fieldValiditySignals.value = fieldValiditySignals.value.filter((s) => s !== validity);\n };\n };\n\n return { clearStatus, dirty, error, markDirty, registerField, submit, submitting, valid };\n}\n"],"mappings":"mEAwCA,IAAa,EAAmB,EAAA,cAAgC,kBAAkB,EAiBlF,SAAgB,EACd,EAGI,CAAC,EACW,CAChB,IAAM,GAAA,EAAA,EAAA,OAAA,CAAwD,CAAC,CAAC,EAC1D,GAAA,EAAA,EAAA,OAAA,CAAoB,EAAK,EACzB,GAAA,EAAA,EAAA,OAAA,CAAe,EAAK,EACpB,GAAA,EAAA,EAAA,OAAA,CAAwB,IAAI,EA0ClC,MAAO,CAAE,gBAlBuB,CAC9B,EAAM,MAAQ,GACd,EAAM,MAAQ,KACd,EAAQ,UAAU,CACpB,EAcsB,QAAO,QAAO,cAZN,CAC5B,EAAM,MAAQ,EAChB,EAU+C,cARxB,IACrB,EAAqB,MAAQ,CAAC,GAAG,EAAqB,MAAO,CAAQ,MAExD,CACX,EAAqB,MAAQ,EAAqB,MAAM,OAAQ,GAAM,IAAM,CAAQ,CACtF,GAG4D,YApCxC,IAA6B,CACjD,MAAG,eAAe,EAEd,GAAW,MAGf,CADA,EAAW,MAAQ,GACnB,EAAM,MAAQ,KAEd,GAAI,CACF,MAAM,EAAQ,WAAW,CAAC,EAC1B,EAAM,MAAQ,EAChB,OAAS,EAAK,CACZ,EAAM,MAAQ,CAChB,QAAU,CACR,EAAW,MAAQ,EACrB,CATc,CAUhB,EAoBsE,aAAY,OAtC5E,EAAA,EAAA,SAAA,KAAuB,EAAqB,MAAM,MAAO,GAAM,EAAE,KAAK,CAsCM,CAAM,CAC1F"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-context.d.ts","sourceRoot":"","sources":["../src/form-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAU,MAAM,kBAAkB,CAAC;AAInE;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClC,SAAS,IAAI,IAAI,CAAC;IAClB;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC;CACxD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG;IAC9C;;;;OAIG;IACH,WAAW,IAAI,IAAI,CAAC;IACpB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClC,wCAAwC;IACxC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,gDAAgD;IAChD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;CACnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,oDAAsD,CAAC;AAEpF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C,GACL,cAAc,CA+ChB"}
@@ -1,2 +0,0 @@
1
- import{createContext as e}from"./context.js";import{computed as t,signal as n}from"@vielzeug/ripple";var r=e(`ore:form-context`);function i(e={}){let r=n([]),i=n(!1),a=n(!1),o=n(null);return{clearStatus:()=>{a.value=!1,o.value=null,e.onReset?.()},dirty:a,error:o,markDirty:()=>{a.value=!0},registerField:e=>(r.value=[...r.value,e],()=>{r.value=r.value.filter(t=>t!==e)}),submit:async t=>{if(t?.preventDefault(),!i.value){i.value=!0,o.value=null;try{await e.onSubmit?.(t),a.value=!1}catch(e){o.value=e}finally{i.value=!1}}},submitting:i,valid:t(()=>r.value.every(e=>e.value))}}export{r as FORM_CONTEXT_KEY,i as createFormContext};
2
- //# sourceMappingURL=form-context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-context.js","names":[],"sources":["../src/form-context.ts"],"sourcesContent":["import { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport { createContext } from './context';\n\n/**\n * Context shape injected by child field components.\n * Contains only what a field needs — not submit/error/status APIs.\n */\nexport type FormFieldContext = {\n /** Whether any field has been touched (interacted with). Set via markDirty(). */\n readonly dirty: Readable<boolean>;\n markDirty(): void;\n /**\n * Register a field's validity signal with the form context.\n * Returns a cleanup function.\n */\n registerField(validity: Readable<boolean>): () => void;\n};\n\n/**\n * Full form controller returned by `createFormContext`.\n * Extends `FormFieldContext` with submission and status APIs.\n */\nexport type FormController = FormFieldContext & {\n /**\n * Clear `dirty` and `error` status signals and invoke `onReset` if provided.\n * Note: this does not reset individual field values — pass an `onReset` callback\n * to handle value restoration.\n */\n clearStatus(): void;\n /** The last submit error, or null if the last submit succeeded. */\n readonly error: Readable<unknown>;\n /** Submit the form programmatically. */\n submit(e?: Event): Promise<void>;\n /** Whether the form is currently submitting. */\n readonly submitting: Readable<boolean>;\n /** Whether all fields in the form are valid. */\n readonly valid: Readable<boolean>;\n};\n\nexport const FORM_CONTEXT_KEY = createContext<FormFieldContext>('ore:form-context');\n\n/**\n * Create a `FormController` for coordinating form state across child field components.\n * Call `ctx.provide(FORM_CONTEXT_KEY, form)` to make it available to descendants.\n *\n * @example\n * ```ts\n * define('my-form', {\n * setup(_props, ctx) {\n * const form = createFormContext({ onSubmit: async (e) => { ... } });\n * ctx.provide(FORM_CONTEXT_KEY, form);\n * return html`<form @submit=${form.submit}><slot></slot></form>`;\n * }\n * });\n * ```\n */\nexport function createFormContext(\n options: {\n onReset?: () => void;\n onSubmit?: (e?: Event) => void | Promise<void>;\n } = {},\n): FormController {\n const fieldValiditySignals = signal<Array<Readable<boolean>>>([]);\n const submitting = signal(false);\n const dirty = signal(false);\n const error = signal<unknown>(null);\n\n // Note: [].every() is vacuously true, so valid is true when no fields are registered.\n // This is intentional — a form with no fields is considered valid (nothing to invalidate it).\n const valid = computed(() => fieldValiditySignals.value.every((s) => s.value));\n\n const submit = async (e?: Event): Promise<void> => {\n e?.preventDefault();\n\n if (submitting.value) return;\n\n submitting.value = true;\n error.value = null;\n\n try {\n await options.onSubmit?.(e);\n dirty.value = false;\n } catch (err) {\n error.value = err;\n } finally {\n submitting.value = false;\n }\n };\n\n const clearStatus = (): void => {\n dirty.value = false;\n error.value = null;\n options.onReset?.();\n };\n\n const markDirty = (): void => {\n dirty.value = true;\n };\n\n const registerField = (validity: Readable<boolean>): (() => void) => {\n fieldValiditySignals.value = [...fieldValiditySignals.value, validity];\n\n return () => {\n fieldValiditySignals.value = fieldValiditySignals.value.filter((s) => s !== validity);\n };\n };\n\n return { clearStatus, dirty, error, markDirty, registerField, submit, submitting, valid };\n}\n"],"mappings":"qGAwCA,IAAa,EAAmB,EAAgC,kBAAkB,EAiBlF,SAAgB,EACd,EAGI,CAAC,EACW,CAChB,IAAM,EAAuB,EAAiC,CAAC,CAAC,EAC1D,EAAa,EAAO,EAAK,EACzB,EAAQ,EAAO,EAAK,EACpB,EAAQ,EAAgB,IAAI,EA0ClC,MAAO,CAAE,gBAlBuB,CAC9B,EAAM,MAAQ,GACd,EAAM,MAAQ,KACd,EAAQ,UAAU,CACpB,EAcsB,QAAO,QAAO,cAZN,CAC5B,EAAM,MAAQ,EAChB,EAU+C,cARxB,IACrB,EAAqB,MAAQ,CAAC,GAAG,EAAqB,MAAO,CAAQ,MAExD,CACX,EAAqB,MAAQ,EAAqB,MAAM,OAAQ,GAAM,IAAM,CAAQ,CACtF,GAG4D,YApCxC,IAA6B,CACjD,MAAG,eAAe,EAEd,GAAW,MAGf,CADA,EAAW,MAAQ,GACnB,EAAM,MAAQ,KAEd,GAAI,CACF,MAAM,EAAQ,WAAW,CAAC,EAC1B,EAAM,MAAQ,EAChB,OAAS,EAAK,CACZ,EAAM,MAAQ,CAChB,QAAU,CACR,EAAW,MAAQ,EACrB,CATc,CAUhB,EAoBsE,aAAY,MAtCpE,MAAe,EAAqB,MAAM,MAAO,GAAM,EAAE,KAAK,CAsCM,CAAM,CAC1F"}
@@ -1,2 +0,0 @@
1
- const e=require("./_dev.cjs"),t=require("./errors.cjs"),n=require("./runtime.cjs");require("@vielzeug/ripple");var r=new WeakMap,i=i=>{let a=i.el??n.getCurrentElement();if(!a.constructor.formAssociated)throw new t.OreApiError(t.ORE_ERRORS.defineFieldRequiresFormAssociated(a.localName));if(r.has(a))throw new t.OreApiError(`useField() was already called on <${a.localName}>. Call it only once per component.`);let o=a.attachInternals();r.set(a,o),n.onCleanup(()=>r.delete(a));let s=i.toFormValue??(e=>e==null?i.emptyStringForNull?``:null:e instanceof File||e instanceof FormData?e:String(e));n.effect(()=>{o.setFormValue(s(i.value.value))});let c=i.disabled;if(c)if(!(`states`in o))e.warn(`useField(): ElementInternals.states (CustomStateSet) is not available in this environment — disabled state tracking skipped.`);else{let e=o.states;n.effect(()=>{c.value?e.add(`disabled`):e.delete(`disabled`)})}return{checkValidity:()=>o.checkValidity(),internals:o,reportValidity:()=>o.reportValidity(),setCustomValidity:e=>e?o.setValidity({customError:!0},e):o.setValidity({}),setValidity:o.setValidity.bind(o)}};exports.useField=i;
2
- //# sourceMappingURL=form-field.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-field.cjs","names":[],"sources":["../src/form-field.ts"],"sourcesContent":["import { type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { warn } from './_dev';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { effect, getCurrentElement, onCleanup } from './runtime';\n\n/** @internal */\nconst internalsRegistry = new WeakMap<HTMLElement, ElementInternals>();\n\nexport type FormFieldOptions<T = unknown> = {\n disabled?: Readable<boolean>;\n /**\n * The host element to attach `ElementInternals` to.\n * Defaults to the element currently being set up via `getCurrentElement()`.\n * Pass this explicitly when calling `useField` from a composable that is\n * not called directly during `setup()` (e.g. a helper factory function).\n */\n el?: HTMLElement;\n /**\n * When `true`, a `null` or `undefined` value is submitted as an empty string\n * (`''`) instead of `null`. This keeps the field's key present in `FormData`\n * even when the value is absent — useful when the server expects the field to\n * always be included.\n *\n * @default false\n */\n emptyStringForNull?: boolean;\n toFormValue?: (value: T) => File | FormData | string | null;\n value: Signal<T> | Readable<T>;\n};\n\nexport type FormFieldHandle = {\n checkValidity: () => boolean;\n readonly internals: ElementInternals;\n reportValidity: () => boolean;\n setCustomValidity: (message: string) => void;\n setValidity: ElementInternals['setValidity'];\n};\n\nexport const useField = <T = unknown>(options: FormFieldOptions<T>): FormFieldHandle => {\n const host = options.el ?? getCurrentElement();\n const ctor = host.constructor as typeof HTMLElement & { formAssociated?: boolean };\n\n if (!ctor.formAssociated) {\n throw new OreApiError(ORE_ERRORS.defineFieldRequiresFormAssociated(host.localName));\n }\n\n if (internalsRegistry.has(host)) {\n throw new OreApiError(`useField() was already called on <${host.localName}>. Call it only once per component.`);\n }\n\n const internals = host.attachInternals();\n\n internalsRegistry.set(host, internals);\n onCleanup(() => internalsRegistry.delete(host));\n\n const toFormValue =\n options.toFormValue ??\n ((v: T): File | FormData | string | null => {\n if (v == null) return options.emptyStringForNull ? '' : null;\n\n if (v instanceof File || v instanceof FormData) return v;\n\n return String(v);\n });\n\n effect(() => {\n internals.setFormValue(toFormValue(options.value.value));\n });\n\n const disabled = options.disabled;\n\n if (disabled) {\n if (!('states' in internals)) {\n warn(\n 'useField(): ElementInternals.states (CustomStateSet) is not available in this environment — disabled state tracking skipped.',\n );\n } else {\n const states = internals.states as CustomStateSet;\n\n effect(() => {\n if (disabled.value) states.add('disabled');\n else states.delete('disabled');\n });\n }\n }\n\n const checkValidity = () => internals.checkValidity();\n const reportValidity = () => internals.reportValidity();\n const setCustomValidity = (message: string) =>\n message ? internals.setValidity({ customError: true }, message) : internals.setValidity({});\n\n return {\n checkValidity,\n internals,\n reportValidity,\n setCustomValidity,\n setValidity: internals.setValidity.bind(internals),\n };\n};\n"],"mappings":"+GAOA,IAAM,EAAoB,IAAI,QAgCjB,EAAyB,GAAkD,CACtF,IAAM,EAAO,EAAQ,IAAM,EAAA,kBAAkB,EAG7C,GAAI,CAFS,EAAK,YAER,eACR,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,kCAAkC,EAAK,SAAS,CAAC,EAGpF,GAAI,EAAkB,IAAI,CAAI,EAC5B,MAAM,IAAI,EAAA,YAAY,qCAAqC,EAAK,UAAU,oCAAoC,EAGhH,IAAM,EAAY,EAAK,gBAAgB,EAEvC,EAAkB,IAAI,EAAM,CAAS,EACrC,EAAA,cAAgB,EAAkB,OAAO,CAAI,CAAC,EAE9C,IAAM,EACJ,EAAQ,cACN,GACI,GAAK,KAAa,EAAQ,mBAAqB,GAAK,KAEpD,aAAa,MAAQ,aAAa,SAAiB,EAEhD,OAAO,CAAC,GAGnB,EAAA,WAAa,CACX,EAAU,aAAa,EAAY,EAAQ,MAAM,KAAK,CAAC,CACzD,CAAC,EAED,IAAM,EAAW,EAAQ,SAEzB,GAAI,EACF,GAAI,EAAE,WAAY,GAChB,EAAA,KACE,8HACF,MACK,CACL,IAAM,EAAS,EAAU,OAEzB,EAAA,WAAa,CACP,EAAS,MAAO,EAAO,IAAI,UAAU,EACpC,EAAO,OAAO,UAAU,CAC/B,CAAC,CACH,CAQF,MAAO,CACL,kBAN0B,EAAU,cAAc,EAOlD,YACA,mBAP2B,EAAU,eAAe,EAQpD,kBAPyB,GACzB,EAAU,EAAU,YAAY,CAAE,YAAa,EAAK,EAAG,CAAO,EAAI,EAAU,YAAY,CAAC,CAAC,EAO1F,YAAa,EAAU,YAAY,KAAK,CAAS,CACnD,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-field.d.ts","sourceRoot":"","sources":["../src/form-field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAS9D,MAAM,MAAM,gBAAgB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC1C,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B;;;;;OAKG;IACH,EAAE,CAAC,EAAE,WAAW,CAAC;IACjB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;IACrC,cAAc,EAAE,MAAM,OAAO,CAAC;IAC9B,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC;CAC9C,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,CAAC,GAAG,OAAO,EAAE,SAAS,gBAAgB,CAAC,CAAC,CAAC,KAAG,eA4DpE,CAAC"}
@@ -1,2 +0,0 @@
1
- import{warn as e}from"./_dev.js";import{ORE_ERRORS as t,OreApiError as n}from"./errors.js";import{effect as r,getCurrentElement as i,onCleanup as a}from"./runtime.js";import"@vielzeug/ripple";var o=new WeakMap,s=s=>{let c=s.el??i();if(!c.constructor.formAssociated)throw new n(t.defineFieldRequiresFormAssociated(c.localName));if(o.has(c))throw new n(`useField() was already called on <${c.localName}>. Call it only once per component.`);let l=c.attachInternals();o.set(c,l),a(()=>o.delete(c));let u=s.toFormValue??(e=>e==null?s.emptyStringForNull?``:null:e instanceof File||e instanceof FormData?e:String(e));r(()=>{l.setFormValue(u(s.value.value))});let d=s.disabled;if(d)if(!(`states`in l))e(`useField(): ElementInternals.states (CustomStateSet) is not available in this environment — disabled state tracking skipped.`);else{let e=l.states;r(()=>{d.value?e.add(`disabled`):e.delete(`disabled`)})}return{checkValidity:()=>l.checkValidity(),internals:l,reportValidity:()=>l.reportValidity(),setCustomValidity:e=>e?l.setValidity({customError:!0},e):l.setValidity({}),setValidity:l.setValidity.bind(l)}};export{s as useField};
2
- //# sourceMappingURL=form-field.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-field.js","names":[],"sources":["../src/form-field.ts"],"sourcesContent":["import { type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { warn } from './_dev';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { effect, getCurrentElement, onCleanup } from './runtime';\n\n/** @internal */\nconst internalsRegistry = new WeakMap<HTMLElement, ElementInternals>();\n\nexport type FormFieldOptions<T = unknown> = {\n disabled?: Readable<boolean>;\n /**\n * The host element to attach `ElementInternals` to.\n * Defaults to the element currently being set up via `getCurrentElement()`.\n * Pass this explicitly when calling `useField` from a composable that is\n * not called directly during `setup()` (e.g. a helper factory function).\n */\n el?: HTMLElement;\n /**\n * When `true`, a `null` or `undefined` value is submitted as an empty string\n * (`''`) instead of `null`. This keeps the field's key present in `FormData`\n * even when the value is absent — useful when the server expects the field to\n * always be included.\n *\n * @default false\n */\n emptyStringForNull?: boolean;\n toFormValue?: (value: T) => File | FormData | string | null;\n value: Signal<T> | Readable<T>;\n};\n\nexport type FormFieldHandle = {\n checkValidity: () => boolean;\n readonly internals: ElementInternals;\n reportValidity: () => boolean;\n setCustomValidity: (message: string) => void;\n setValidity: ElementInternals['setValidity'];\n};\n\nexport const useField = <T = unknown>(options: FormFieldOptions<T>): FormFieldHandle => {\n const host = options.el ?? getCurrentElement();\n const ctor = host.constructor as typeof HTMLElement & { formAssociated?: boolean };\n\n if (!ctor.formAssociated) {\n throw new OreApiError(ORE_ERRORS.defineFieldRequiresFormAssociated(host.localName));\n }\n\n if (internalsRegistry.has(host)) {\n throw new OreApiError(`useField() was already called on <${host.localName}>. Call it only once per component.`);\n }\n\n const internals = host.attachInternals();\n\n internalsRegistry.set(host, internals);\n onCleanup(() => internalsRegistry.delete(host));\n\n const toFormValue =\n options.toFormValue ??\n ((v: T): File | FormData | string | null => {\n if (v == null) return options.emptyStringForNull ? '' : null;\n\n if (v instanceof File || v instanceof FormData) return v;\n\n return String(v);\n });\n\n effect(() => {\n internals.setFormValue(toFormValue(options.value.value));\n });\n\n const disabled = options.disabled;\n\n if (disabled) {\n if (!('states' in internals)) {\n warn(\n 'useField(): ElementInternals.states (CustomStateSet) is not available in this environment — disabled state tracking skipped.',\n );\n } else {\n const states = internals.states as CustomStateSet;\n\n effect(() => {\n if (disabled.value) states.add('disabled');\n else states.delete('disabled');\n });\n }\n }\n\n const checkValidity = () => internals.checkValidity();\n const reportValidity = () => internals.reportValidity();\n const setCustomValidity = (message: string) =>\n message ? internals.setValidity({ customError: true }, message) : internals.setValidity({});\n\n return {\n checkValidity,\n internals,\n reportValidity,\n setCustomValidity,\n setValidity: internals.setValidity.bind(internals),\n };\n};\n"],"mappings":"gMAOA,IAAM,EAAoB,IAAI,QAgCjB,EAAyB,GAAkD,CACtF,IAAM,EAAO,EAAQ,IAAM,EAAkB,EAG7C,GAAI,CAFS,EAAK,YAER,eACR,MAAM,IAAI,EAAY,EAAW,kCAAkC,EAAK,SAAS,CAAC,EAGpF,GAAI,EAAkB,IAAI,CAAI,EAC5B,MAAM,IAAI,EAAY,qCAAqC,EAAK,UAAU,oCAAoC,EAGhH,IAAM,EAAY,EAAK,gBAAgB,EAEvC,EAAkB,IAAI,EAAM,CAAS,EACrC,MAAgB,EAAkB,OAAO,CAAI,CAAC,EAE9C,IAAM,EACJ,EAAQ,cACN,GACI,GAAK,KAAa,EAAQ,mBAAqB,GAAK,KAEpD,aAAa,MAAQ,aAAa,SAAiB,EAEhD,OAAO,CAAC,GAGnB,MAAa,CACX,EAAU,aAAa,EAAY,EAAQ,MAAM,KAAK,CAAC,CACzD,CAAC,EAED,IAAM,EAAW,EAAQ,SAEzB,GAAI,EACF,GAAI,EAAE,WAAY,GAChB,EACE,8HACF,MACK,CACL,IAAM,EAAS,EAAU,OAEzB,MAAa,CACP,EAAS,MAAO,EAAO,IAAI,UAAU,EACpC,EAAO,OAAO,UAAU,CAC/B,CAAC,CACH,CAQF,MAAO,CACL,kBAN0B,EAAU,cAAc,EAOlD,YACA,mBAP2B,EAAU,eAAe,EAQpD,kBAPyB,GACzB,EAAU,EAAU,YAAY,CAAE,YAAa,EAAK,EAAG,CAAO,EAAI,EAAU,YAAY,CAAC,CAAC,EAO1F,YAAa,EAAU,YAAY,KAAK,CAAS,CACnD,CACF"}