@vielzeug/ore 1.2.3 → 2.0.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 (318) hide show
  1. package/README.md +11 -8
  2. package/dist/_component-class.cjs +2 -0
  3. package/dist/_component-class.cjs.map +1 -0
  4. package/dist/_component-class.d.ts +2 -0
  5. package/dist/_component-class.d.ts.map +1 -0
  6. package/dist/_component-class.js +2 -0
  7. package/dist/_component-class.js.map +1 -0
  8. package/dist/_dev.cjs +1 -1
  9. package/dist/_dev.cjs.map +1 -1
  10. package/dist/_dev.js +1 -1
  11. package/dist/_dev.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 +9 -15
  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 +3 -14
  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 +13 -0
  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 +1 -1
  27. package/dist/define.cjs.map +1 -1
  28. package/dist/define.d.ts +6 -5
  29. package/dist/define.d.ts.map +1 -1
  30. package/dist/define.js +1 -1
  31. package/dist/define.js.map +1 -1
  32. package/dist/directives/classMap.cjs +1 -1
  33. package/dist/directives/classMap.cjs.map +1 -1
  34. package/dist/directives/classMap.d.ts.map +1 -1
  35. package/dist/directives/classMap.js +1 -1
  36. package/dist/directives/classMap.js.map +1 -1
  37. package/dist/directives/each.cjs +1 -1
  38. package/dist/directives/each.cjs.map +1 -1
  39. package/dist/directives/each.d.ts +9 -2
  40. package/dist/directives/each.d.ts.map +1 -1
  41. package/dist/directives/each.js +1 -1
  42. package/dist/directives/each.js.map +1 -1
  43. package/dist/directives/live.cjs +1 -1
  44. package/dist/directives/live.cjs.map +1 -1
  45. package/dist/directives/live.d.ts +10 -12
  46. package/dist/directives/live.d.ts.map +1 -1
  47. package/dist/directives/live.js +1 -1
  48. package/dist/directives/live.js.map +1 -1
  49. package/dist/directives/styleMap.cjs +1 -1
  50. package/dist/directives/styleMap.cjs.map +1 -1
  51. package/dist/directives/styleMap.d.ts +1 -1
  52. package/dist/directives/styleMap.d.ts.map +1 -1
  53. package/dist/directives/styleMap.js +1 -1
  54. package/dist/directives/styleMap.js.map +1 -1
  55. package/dist/directives/unsafe-html.cjs +2 -0
  56. package/dist/directives/unsafe-html.cjs.map +1 -0
  57. package/dist/directives/unsafe-html.d.ts +14 -0
  58. package/dist/directives/unsafe-html.d.ts.map +1 -0
  59. package/dist/directives/unsafe-html.js +2 -0
  60. package/dist/directives/unsafe-html.js.map +1 -0
  61. package/dist/directives/when.cjs +1 -1
  62. package/dist/directives/when.cjs.map +1 -1
  63. package/dist/directives/when.d.ts +1 -1
  64. package/dist/directives/when.d.ts.map +1 -1
  65. package/dist/directives/when.js +1 -1
  66. package/dist/directives/when.js.map +1 -1
  67. package/dist/errors.cjs +2 -2
  68. package/dist/errors.cjs.map +1 -1
  69. package/dist/errors.d.ts +21 -6
  70. package/dist/errors.d.ts.map +1 -1
  71. package/dist/errors.js +2 -2
  72. package/dist/errors.js.map +1 -1
  73. package/dist/forms/field.cjs +1 -1
  74. package/dist/forms/field.cjs.map +1 -1
  75. package/dist/forms/field.d.ts +5 -1
  76. package/dist/forms/field.d.ts.map +1 -1
  77. package/dist/forms/field.js +1 -1
  78. package/dist/forms/field.js.map +1 -1
  79. package/dist/host-bind.cjs +1 -1
  80. package/dist/host-bind.cjs.map +1 -1
  81. package/dist/host-bind.d.ts +6 -0
  82. package/dist/host-bind.d.ts.map +1 -1
  83. package/dist/host-bind.js +1 -1
  84. package/dist/host-bind.js.map +1 -1
  85. package/dist/index.cjs +1 -1
  86. package/dist/index.d.ts +15 -5
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +1 -1
  89. package/dist/observers/intersection-observe.cjs +1 -1
  90. package/dist/observers/intersection-observe.cjs.map +1 -1
  91. package/dist/observers/intersection-observe.d.ts.map +1 -1
  92. package/dist/observers/intersection-observe.js +1 -1
  93. package/dist/observers/intersection-observe.js.map +1 -1
  94. package/dist/observers/media-observe.cjs +1 -1
  95. package/dist/observers/media-observe.cjs.map +1 -1
  96. package/dist/observers/media-observe.d.ts.map +1 -1
  97. package/dist/observers/media-observe.js +1 -1
  98. package/dist/observers/media-observe.js.map +1 -1
  99. package/dist/observers/mutation-observe.cjs +1 -1
  100. package/dist/observers/mutation-observe.cjs.map +1 -1
  101. package/dist/observers/mutation-observe.d.ts.map +1 -1
  102. package/dist/observers/mutation-observe.js +1 -1
  103. package/dist/observers/mutation-observe.js.map +1 -1
  104. package/dist/observers/resize-observe.cjs +1 -1
  105. package/dist/observers/resize-observe.cjs.map +1 -1
  106. package/dist/observers/resize-observe.d.ts.map +1 -1
  107. package/dist/observers/resize-observe.js +1 -1
  108. package/dist/observers/resize-observe.js.map +1 -1
  109. package/dist/ore.cjs +2 -2
  110. package/dist/ore.cjs.map +1 -1
  111. package/dist/ore.iife.js +2 -2
  112. package/dist/ore.iife.js.map +1 -1
  113. package/dist/ore.js +2 -2
  114. package/dist/ore.js.map +1 -1
  115. package/dist/props.cjs +1 -1
  116. package/dist/props.cjs.map +1 -1
  117. package/dist/props.d.ts +1 -6
  118. package/dist/props.d.ts.map +1 -1
  119. package/dist/props.js +1 -1
  120. package/dist/props.js.map +1 -1
  121. package/dist/runtime.cjs +1 -1
  122. package/dist/runtime.cjs.map +1 -1
  123. package/dist/runtime.d.ts +7 -10
  124. package/dist/runtime.d.ts.map +1 -1
  125. package/dist/runtime.js +1 -1
  126. package/dist/runtime.js.map +1 -1
  127. package/dist/slots.cjs +1 -1
  128. package/dist/slots.cjs.map +1 -1
  129. package/dist/slots.d.ts.map +1 -1
  130. package/dist/slots.js +1 -1
  131. package/dist/slots.js.map +1 -1
  132. package/dist/template/binding-types.cjs +1 -0
  133. package/dist/template/binding-types.d.ts +59 -0
  134. package/dist/template/binding-types.d.ts.map +1 -0
  135. package/dist/template/binding-types.js +1 -0
  136. package/dist/template/bindings.cjs +1 -1
  137. package/dist/template/bindings.cjs.map +1 -1
  138. package/dist/template/bindings.d.ts +18 -3
  139. package/dist/template/bindings.d.ts.map +1 -1
  140. package/dist/template/bindings.js +1 -1
  141. package/dist/template/bindings.js.map +1 -1
  142. package/dist/template/compiler.cjs +1 -1
  143. package/dist/template/compiler.cjs.map +1 -1
  144. package/dist/template/compiler.d.ts +0 -4
  145. package/dist/template/compiler.d.ts.map +1 -1
  146. package/dist/template/compiler.js +1 -1
  147. package/dist/template/compiler.js.map +1 -1
  148. package/dist/template/instantiator.cjs +1 -1
  149. package/dist/template/instantiator.cjs.map +1 -1
  150. package/dist/template/instantiator.d.ts +6 -1
  151. package/dist/template/instantiator.d.ts.map +1 -1
  152. package/dist/template/instantiator.js +1 -1
  153. package/dist/template/instantiator.js.map +1 -1
  154. package/dist/template/result.cjs +2 -0
  155. package/dist/template/result.cjs.map +1 -0
  156. package/dist/template/result.d.ts +42 -0
  157. package/dist/template/result.d.ts.map +1 -0
  158. package/dist/template/result.js +2 -0
  159. package/dist/template/result.js.map +1 -0
  160. package/dist/testing/flush.cjs +1 -1
  161. package/dist/testing/flush.cjs.map +1 -1
  162. package/dist/testing/flush.d.ts +30 -26
  163. package/dist/testing/flush.d.ts.map +1 -1
  164. package/dist/testing/flush.js +1 -1
  165. package/dist/testing/flush.js.map +1 -1
  166. package/dist/testing/form-internals-polyfill.cjs +1 -1
  167. package/dist/testing/form-internals-polyfill.cjs.map +1 -1
  168. package/dist/testing/form-internals-polyfill.d.ts +9 -7
  169. package/dist/testing/form-internals-polyfill.d.ts.map +1 -1
  170. package/dist/testing/form-internals-polyfill.js +1 -1
  171. package/dist/testing/form-internals-polyfill.js.map +1 -1
  172. package/dist/testing/index.d.ts +35 -1
  173. package/dist/testing/index.d.ts.map +1 -1
  174. package/dist/testing/mount.cjs +1 -1
  175. package/dist/testing/mount.cjs.map +1 -1
  176. package/dist/testing/mount.d.ts +10 -19
  177. package/dist/testing/mount.d.ts.map +1 -1
  178. package/dist/testing/mount.js +1 -1
  179. package/dist/testing/mount.js.map +1 -1
  180. package/dist/testing/render-hook.cjs +1 -1
  181. package/dist/testing/render-hook.cjs.map +1 -1
  182. package/dist/testing/render-hook.js +1 -1
  183. package/dist/testing/render-hook.js.map +1 -1
  184. package/dist/testing/reset.cjs +2 -0
  185. package/dist/testing/reset.cjs.map +1 -0
  186. package/dist/testing/reset.d.ts +12 -0
  187. package/dist/testing/reset.d.ts.map +1 -0
  188. package/dist/testing/reset.js +2 -0
  189. package/dist/testing/reset.js.map +1 -0
  190. package/dist/testing.cjs +2 -1
  191. package/dist/testing.cjs.map +1 -0
  192. package/dist/testing.js +2 -1
  193. package/dist/testing.js.map +1 -0
  194. package/dist/utils/aria.cjs.map +1 -1
  195. package/dist/utils/aria.d.ts +1 -1
  196. package/dist/utils/aria.js.map +1 -1
  197. package/dist/utils/brand.cjs +2 -0
  198. package/dist/utils/brand.cjs.map +1 -0
  199. package/dist/utils/brand.d.ts +16 -0
  200. package/dist/utils/brand.d.ts.map +1 -0
  201. package/dist/utils/brand.js +2 -0
  202. package/dist/utils/brand.js.map +1 -0
  203. package/dist/utils/css.cjs +1 -1
  204. package/dist/utils/css.cjs.map +1 -1
  205. package/dist/utils/css.d.ts.map +1 -1
  206. package/dist/utils/css.js +1 -1
  207. package/dist/utils/css.js.map +1 -1
  208. package/dist/utils/dom.cjs +1 -1
  209. package/dist/utils/dom.cjs.map +1 -1
  210. package/dist/utils/dom.d.ts +34 -0
  211. package/dist/utils/dom.d.ts.map +1 -1
  212. package/dist/utils/dom.js +1 -1
  213. package/dist/utils/dom.js.map +1 -1
  214. package/dist/utils/emit.cjs.map +1 -1
  215. package/dist/utils/emit.d.ts +10 -3
  216. package/dist/utils/emit.d.ts.map +1 -1
  217. package/dist/utils/emit.js.map +1 -1
  218. package/dist/utils/id.cjs +1 -1
  219. package/dist/utils/id.cjs.map +1 -1
  220. package/dist/utils/id.d.ts +11 -2
  221. package/dist/utils/id.d.ts.map +1 -1
  222. package/dist/utils/id.js +1 -1
  223. package/dist/utils/id.js.map +1 -1
  224. package/package.json +15 -30
  225. package/dist/aria.cjs +0 -2
  226. package/dist/aria.cjs.map +0 -1
  227. package/dist/aria.d.ts +0 -20
  228. package/dist/aria.d.ts.map +0 -1
  229. package/dist/aria.js +0 -2
  230. package/dist/aria.js.map +0 -1
  231. package/dist/devtools.cjs +0 -2
  232. package/dist/devtools.cjs.map +0 -1
  233. package/dist/devtools.d.ts +0 -32
  234. package/dist/devtools.d.ts.map +0 -1
  235. package/dist/devtools.js +0 -2
  236. package/dist/devtools.js.map +0 -1
  237. package/dist/directives/index.d.ts +0 -8
  238. package/dist/directives/index.d.ts.map +0 -1
  239. package/dist/directives/model.cjs +0 -2
  240. package/dist/directives/model.cjs.map +0 -1
  241. package/dist/directives/model.d.ts +0 -28
  242. package/dist/directives/model.d.ts.map +0 -1
  243. package/dist/directives/model.js +0 -2
  244. package/dist/directives/model.js.map +0 -1
  245. package/dist/directives/raw.cjs +0 -2
  246. package/dist/directives/raw.cjs.map +0 -1
  247. package/dist/directives/raw.d.ts +0 -16
  248. package/dist/directives/raw.d.ts.map +0 -1
  249. package/dist/directives/raw.js +0 -2
  250. package/dist/directives/raw.js.map +0 -1
  251. package/dist/directives.cjs +0 -1
  252. package/dist/directives.js +0 -1
  253. package/dist/forms/context.cjs +0 -2
  254. package/dist/forms/context.cjs.map +0 -1
  255. package/dist/forms/context.d.ts +0 -56
  256. package/dist/forms/context.d.ts.map +0 -1
  257. package/dist/forms/context.js +0 -2
  258. package/dist/forms/context.js.map +0 -1
  259. package/dist/forms/index.d.ts +0 -11
  260. package/dist/forms/index.d.ts.map +0 -1
  261. package/dist/forms.cjs +0 -1
  262. package/dist/forms.js +0 -1
  263. package/dist/observers/index.d.ts +0 -5
  264. package/dist/observers/index.d.ts.map +0 -1
  265. package/dist/observers.cjs +0 -1
  266. package/dist/observers.js +0 -1
  267. package/dist/template.cjs +0 -1
  268. package/dist/template.d.ts +0 -4
  269. package/dist/template.d.ts.map +0 -1
  270. package/dist/template.js +0 -1
  271. package/dist/testing/events.cjs +0 -2
  272. package/dist/testing/events.cjs.map +0 -1
  273. package/dist/testing/events.d.ts +0 -37
  274. package/dist/testing/events.d.ts.map +0 -1
  275. package/dist/testing/events.js +0 -2
  276. package/dist/testing/events.js.map +0 -1
  277. package/dist/testing/interactions.cjs +0 -2
  278. package/dist/testing/interactions.cjs.map +0 -1
  279. package/dist/testing/interactions.d.ts +0 -27
  280. package/dist/testing/interactions.d.ts.map +0 -1
  281. package/dist/testing/interactions.js +0 -2
  282. package/dist/testing/interactions.js.map +0 -1
  283. package/dist/testing/query.cjs +0 -2
  284. package/dist/testing/query.cjs.map +0 -1
  285. package/dist/testing/query.d.ts +0 -24
  286. package/dist/testing/query.d.ts.map +0 -1
  287. package/dist/testing/query.js +0 -2
  288. package/dist/testing/query.js.map +0 -1
  289. package/dist/testing/testing.cjs +0 -2
  290. package/dist/testing/testing.cjs.map +0 -1
  291. package/dist/testing/testing.d.ts +0 -32
  292. package/dist/testing/testing.d.ts.map +0 -1
  293. package/dist/testing/testing.js +0 -2
  294. package/dist/testing/testing.js.map +0 -1
  295. package/dist/testing/wait.cjs +0 -2
  296. package/dist/testing/wait.cjs.map +0 -1
  297. package/dist/testing/wait.d.ts +0 -35
  298. package/dist/testing/wait.d.ts.map +0 -1
  299. package/dist/testing/wait.js +0 -2
  300. package/dist/testing/wait.js.map +0 -1
  301. package/dist/types/bindings.cjs +0 -2
  302. package/dist/types/bindings.cjs.map +0 -1
  303. package/dist/types/bindings.d.ts +0 -125
  304. package/dist/types/bindings.d.ts.map +0 -1
  305. package/dist/types/bindings.js +0 -2
  306. package/dist/types/bindings.js.map +0 -1
  307. package/dist/types.cjs +0 -2
  308. package/dist/types.cjs.map +0 -1
  309. package/dist/types.d.ts +0 -19
  310. package/dist/types.d.ts.map +0 -1
  311. package/dist/types.js +0 -2
  312. package/dist/types.js.map +0 -1
  313. package/dist/utils/event-modifiers.cjs +0 -2
  314. package/dist/utils/event-modifiers.cjs.map +0 -1
  315. package/dist/utils/event-modifiers.d.ts +0 -11
  316. package/dist/utils/event-modifiers.d.ts.map +0 -1
  317. package/dist/utils/event-modifiers.js +0 -2
  318. package/dist/utils/event-modifiers.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"mount.cjs","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport type { Readable } from '@vielzeug/ripple';\n\nimport { type ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport { _resetLiveSignals } from '../directives/live';\nimport { _resetRawSanitizer } from '../directives/raw';\nimport { type HTMLResult } from '../types/bindings';\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\nimport { flush, type FlushOptions } from './flush';\nimport { queryAllByText, queryByText, type QueryScope } from './query';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface Fixture<T extends HTMLElement = HTMLElement> {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** Query a single element within shadow root */\n query<E extends Element = Element>(selector: string): E | null;\n /** Query all elements within shadow root */\n queryAll<E extends Element = Element>(selector: string): E[];\n /** Query all elements by their `data-testid` attribute */\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n /** Query all elements whose trimmed text content matches */\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n /** Query a single element by its `data-testid` attribute */\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n /** Query the first element whose trimmed text content matches */\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<any>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null | Promise<HTMLResult | null>;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\nexport let _componentTagCounter = 0;\n\n/**\n * Resets global test counters used for deterministic IDs/markers.\n * @internal\n */\nexport const _resetCounters = (): void => {\n _resetIdCounter();\n _componentTagCounter = 0;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n if (value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? '' : String(value));\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<any> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}-${Math.random().toString(36).slice(2, 7)}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup as ComponentDefinition<any>['setup'],\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n return {\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n query<E extends Element = Element>(selector: string): E | null {\n return (element.shadowRoot ?? element).querySelector<E>(selector);\n },\n\n queryAll<E extends Element = Element>(selector: string): E[] {\n return Array.from((element.shadowRoot ?? element).querySelectorAll<E>(selector));\n },\n\n queryAllByTestId<E extends Element = Element>(testId: string): E[] {\n return Array.from((element.shadowRoot ?? element).querySelectorAll<E>(`[data-testid=\"${testId}\"]`));\n },\n\n queryAllByText<E extends Element = Element>(text: string, selector = '*'): E[] {\n return queryAllByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n queryByTestId<E extends Element = Element>(testId: string): E | null {\n return (element.shadowRoot ?? element).querySelector<E>(`[data-testid=\"${testId}\"]`);\n },\n\n queryByText<E extends Element = Element>(text: string, selector = '*'): E | null {\n return queryByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<any>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _resetRawSanitizer();\n _resetLiveSignals();\n _clearStylesheetCache();\n _resetIdCounter();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
1
+ {"version":3,"file":"mount.cjs","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport type { Readable } from '@vielzeug/ripple';\n\nimport { type QueryScope, within } from '@vielzeug/assay';\n\nimport { type ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport { type HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { flush, type FlushOptions } from './flush';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run — never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<Props extends Record<string, unknown>, T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<Props>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
@@ -2,11 +2,15 @@
2
2
  * Component mounting utilities for test environments.
3
3
  */
4
4
  import type { Readable } from '@vielzeug/ripple';
5
+ import { type QueryScope } from '@vielzeug/assay';
5
6
  import { type ComponentDefinition } from '../component-types';
6
- import { type HTMLResult } from '../types/bindings';
7
+ import { type HTMLResult } from '../template/result';
7
8
  import { type FlushOptions } from './flush';
8
- import { type QueryScope } from './query';
9
- export interface Fixture<T extends HTMLElement = HTMLElement> {
9
+ /**
10
+ * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to
11
+ * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).
12
+ */
13
+ export interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {
10
14
  /** Delegates to `dispose()`. Enables `using` declarations. */
11
15
  [Symbol.dispose](): void;
12
16
  /** Run a callback then flush — the standard way to trigger and assert a reactive update */
@@ -23,18 +27,6 @@ export interface Fixture<T extends HTMLElement = HTMLElement> {
23
27
  element: T;
24
28
  /** Wait for all reactive updates and animation frames */
25
29
  flush(options?: FlushOptions): Promise<void>;
26
- /** Query a single element within shadow root */
27
- query<E extends Element = Element>(selector: string): E | null;
28
- /** Query all elements within shadow root */
29
- queryAll<E extends Element = Element>(selector: string): E[];
30
- /** Query all elements by their `data-testid` attribute */
31
- queryAllByTestId<E extends Element = Element>(testId: string): E[];
32
- /** Query all elements whose trimmed text content matches */
33
- queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];
34
- /** Query a single element by its `data-testid` attribute */
35
- queryByTestId<E extends Element = Element>(testId: string): E | null;
36
- /** Query the first element whose trimmed text content matches */
37
- queryByText<E extends Element = Element>(text: string, selector?: string): E | null;
38
30
  /** The component's shadow root (null for light-DOM components) */
39
31
  readonly shadow: ShadowRoot | null;
40
32
  }
@@ -48,16 +40,15 @@ export interface MountOptions {
48
40
  /** Parent container (default: document.body) */
49
41
  container?: HTMLElement;
50
42
  /** Extra component options when passing an inline setup function */
51
- componentOptions?: Omit<ComponentDefinition<any>, 'setup'>;
43
+ componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;
52
44
  }
53
45
  type MountProps = {
54
46
  readonly [x: string]: Readable<unknown>;
55
47
  };
56
48
  export type MountSetup = {
57
- bivarianceHack: (props: MountProps) => HTMLResult | null | Promise<HTMLResult | null>;
49
+ bivarianceHack: (props: MountProps) => HTMLResult | null;
58
50
  }['bivarianceHack'];
59
51
  export declare const _mountedElements: HTMLElement[];
60
- export declare let _componentTagCounter: number;
61
52
  /**
62
53
  * Mount a component into the DOM and return a test fixture.
63
54
  *
@@ -87,7 +78,7 @@ export declare function mount<T extends HTMLElement = HTMLElement>(tagOrSetup: M
87
78
  * setup: (props) => html`<div>${props.count}</div>`,
88
79
  * });
89
80
  */
90
- export declare function mountComponent<T extends HTMLElement = HTMLElement>(tag: string, definition: ComponentDefinition<any>, options?: MountOptions): Promise<Fixture<T>>;
81
+ export declare function mountComponent<Props extends Record<string, unknown>, T extends HTMLElement = HTMLElement>(tag: string, definition: ComponentDefinition<Props>, options?: MountOptions): Promise<Fixture<T>>;
91
82
  /**
92
83
  * Register a stub custom element (no-op if already defined).
93
84
  *
@@ -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,2 +1,2 @@
1
- import{flush as e}from"./flush.js";import"../types/bindings.js";import{_resetLiveSignals as t}from"../directives/live.js";import{_resetRawSanitizer as n}from"../directives/raw.js";import{_clearStylesheetCache as r}from"../utils/css.js";import{define as i}from"../define.js";import{_resetIdCounter as a}from"../utils/id.js";import{queryAllByText as o,queryByText as s}from"./query.js";var c=[],l=0;function u(e,t,n){n===!1?e.removeAttribute(t):e.setAttribute(t,n===!0?``:String(n))}var d=e=>e instanceof Error?e:Error(String(e)),f=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=d(e.error??e.message),e.preventDefault()},r=e=>{t=d(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function p(t,n={}){let{attrs:r={},componentOptions:a,container:d=document.body,html:p,props:m={}}=n,h,g;typeof t==`string`?h=t:(h=`trial-${++l}-${Math.random().toString(36).slice(2,7)}`,g={...a??{},setup:t}),g&&i(h,g);let _=document.createElement(h);p&&(_.innerHTML=p),Object.keys(m).length&&Object.assign(_,m);for(let[e,t]of Object.entries(r))u(_,e,t);try{await f(async()=>{d.appendChild(_),c.push(_),await e()})}catch(e){_.remove();let t=c.indexOf(_);throw t!==-1&&c.splice(t,1),e}let v=!1;function y(){if(v)return;v=!0,_.remove();let e=c.indexOf(_);e!==-1&&c.splice(e,1)}return{async act(t){await t(),await e()},async attr(t,n){u(_,t,n),await e()},async attrs(t){for(let[e,n]of Object.entries(t))u(_,e,n);await e()},dispose:y,get disposed(){return v},element:_,flush:e,query(e){return(_.shadowRoot??_).querySelector(e)},queryAll(e){return Array.from((_.shadowRoot??_).querySelectorAll(e))},queryAllByTestId(e){return Array.from((_.shadowRoot??_).querySelectorAll(`[data-testid="${e}"]`))},queryAllByText(e,t=`*`){return o(_.shadowRoot??_,e,t)},queryByTestId(e){return(_.shadowRoot??_).querySelector(`[data-testid="${e}"]`)},queryByText(e,t=`*`){return s(_.shadowRoot??_,e,t)},get shadow(){return _.shadowRoot},[Symbol.dispose](){y()}}}async function m(e,t,n){return i(e,t),p(e,n)}function h(e,t=``){customElements.get(e)||customElements.define(e,class extends HTMLElement{connectedCallback(){this.innerHTML=t}})}function g(){for(let e of c)e.remove();c.length=0,n(),t(),r(),a()}export{l as _componentTagCounter,c as _mountedElements,g as cleanup,h as mock,p as mount,m as mountComponent};
1
+ import{setAttr as e}from"../utils/dom.js";import"../template/result.js";import{define as t}from"../define.js";import{flush as n}from"./flush.js";import{resetOreForTests as r}from"./reset.js";import{within as i}from"@vielzeug/assay";var a=[],o=0;function s(t,n,r){e(t,n,r)}var c=e=>e instanceof Error?e:Error(String(e)),l=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=c(e.error??e.message),e.preventDefault()},r=e=>{t=c(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function u(e,r={}){let{attrs:c={},componentOptions:u,container:d=document.body,html:f,props:p={}}=r,m,h;typeof e==`string`?m=e:(m=`trial-${++o}`,h={...u??{},setup:e}),h&&t(m,h);let g=document.createElement(m);f&&(g.innerHTML=f),Object.keys(p).length&&Object.assign(g,p);for(let[e,t]of Object.entries(c))s(g,e,t);try{await l(async()=>{d.appendChild(g),a.push(g),await n()})}catch(e){g.remove();let t=a.indexOf(g);throw t!==-1&&a.splice(t,1),e}let _=!1;function v(){if(_)return;_=!0,g.remove();let e=a.indexOf(g);e!==-1&&a.splice(e,1)}return{...i(g.shadowRoot??g),async act(e){await e(),await n()},async attr(e,t){s(g,e,t),await n()},async attrs(e){for(let[t,n]of Object.entries(e))s(g,t,n);await n()},dispose:v,get disposed(){return _},element:g,flush:n,get shadow(){return g.shadowRoot},[Symbol.dispose](){v()}}}async function d(e,n,r){return t(e,n),u(e,r)}function f(e,t=``){customElements.get(e)||customElements.define(e,class extends HTMLElement{connectedCallback(){this.innerHTML=t}})}function p(){for(let e of a)e.remove();a.length=0,r()}export{a as _mountedElements,p as cleanup,f as mock,u as mount,d as mountComponent};
2
2
  //# sourceMappingURL=mount.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mount.js","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport type { Readable } from '@vielzeug/ripple';\n\nimport { type ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport { _resetLiveSignals } from '../directives/live';\nimport { _resetRawSanitizer } from '../directives/raw';\nimport { type HTMLResult } from '../types/bindings';\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\nimport { flush, type FlushOptions } from './flush';\nimport { queryAllByText, queryByText, type QueryScope } from './query';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface Fixture<T extends HTMLElement = HTMLElement> {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** Query a single element within shadow root */\n query<E extends Element = Element>(selector: string): E | null;\n /** Query all elements within shadow root */\n queryAll<E extends Element = Element>(selector: string): E[];\n /** Query all elements by their `data-testid` attribute */\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n /** Query all elements whose trimmed text content matches */\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n /** Query a single element by its `data-testid` attribute */\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n /** Query the first element whose trimmed text content matches */\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<any>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null | Promise<HTMLResult | null>;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\nexport let _componentTagCounter = 0;\n\n/**\n * Resets global test counters used for deterministic IDs/markers.\n * @internal\n */\nexport const _resetCounters = (): void => {\n _resetIdCounter();\n _componentTagCounter = 0;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n if (value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? '' : String(value));\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<any> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}-${Math.random().toString(36).slice(2, 7)}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup as ComponentDefinition<any>['setup'],\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n return {\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n query<E extends Element = Element>(selector: string): E | null {\n return (element.shadowRoot ?? element).querySelector<E>(selector);\n },\n\n queryAll<E extends Element = Element>(selector: string): E[] {\n return Array.from((element.shadowRoot ?? element).querySelectorAll<E>(selector));\n },\n\n queryAllByTestId<E extends Element = Element>(testId: string): E[] {\n return Array.from((element.shadowRoot ?? element).querySelectorAll<E>(`[data-testid=\"${testId}\"]`));\n },\n\n queryAllByText<E extends Element = Element>(text: string, selector = '*'): E[] {\n return queryAllByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n queryByTestId<E extends Element = Element>(testId: string): E | null {\n return (element.shadowRoot ?? element).querySelector<E>(`[data-testid=\"${testId}\"]`);\n },\n\n queryByText<E extends Element = Element>(text: string, selector = '*'): E | null {\n return queryByText<E>((element.shadowRoot ?? element) as Element, text, selector);\n },\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<any>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _resetRawSanitizer();\n _resetLiveSignals();\n _clearStylesheetCache();\n _resetIdCounter();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
1
+ {"version":3,"file":"mount.js","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport type { Readable } from '@vielzeug/ripple';\n\nimport { type QueryScope, within } from '@vielzeug/assay';\n\nimport { type ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport { type HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { flush, type FlushOptions } from './flush';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n}\n\nexport interface MountOptions {\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Inner HTML for slot content */\n html?: string;\n /** Parent container (default: document.body) */\n container?: HTMLElement;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run — never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Register and mount a component definition in a single call.\n *\n * Combines `define(tag, definition)` + `mount(tag, options)` — the standard\n * pattern for testing full custom-element lifecycle (props, reconnect, etc.).\n *\n * @example\n * const { query } = await mountComponent('my-counter', {\n * props: { count: prop.number(0) },\n * setup: (props) => html`<div>${props.count}</div>`,\n * });\n */\nexport async function mountComponent<Props extends Record<string, unknown>, T extends HTMLElement = HTMLElement>(\n tag: string,\n definition: ComponentDefinition<Props>,\n options?: MountOptions,\n): Promise<Fixture<T>> {\n define(tag, definition);\n\n return mount<T>(tag, options);\n}\n\n/**\n * Register a stub custom element (no-op if already defined).\n *\n * @example\n * mock('child-button', '<slot></slot>');\n */\nexport function mock(tagName: string, template = ''): void {\n if (!customElements.get(tagName)) {\n customElements.define(\n tagName,\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = template;\n }\n },\n );\n }\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n resetOreForTests();\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("../errors.cjs"),n=require("../runtime.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,formResetCallbacks:[],mountCallbacks:f},m;try{u.run(()=>{n.runWithContext(p,()=>{let e=c?r.createProps(d,c):{};m=l(e)})});for(let e of f)try{u.run(()=>{n.runWithContext(p,()=>{let t=e();typeof t==`function`&&(0,i.onCleanup)(t)})})}catch(e){t.reportRuntimeError(new t.OreLifecycleError(`renderHook() mount callback failed`,{cause:e instanceof Error?e:Error(String(e)),component:`renderHook`,phase:`mounted`}),d)}}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("../errors.cjs"),t=require("../runtime.cjs"),n=require("../props.cjs"),r=require("./flush.cjs");let i=require("@vielzeug/ripple");async function a(a,o){let s=typeof a!=`function`,c=s?a:void 0,l=s?o:a,u=(0,i.createScope)(),d=document.createElement(`div`);document.body.appendChild(d);let f=t.createRuntimeContext(d),{mountCallbacks:p}=f,m;try{u.run(()=>{t.runWithContext(f,()=>{let e=c?n.createProps(d,c):{};m=l(e)})});for(let n of p)try{u.run(()=>{t.runWithContext(f,()=>{let e=n();typeof e==`function`&&t.onCleanup(e)})})}catch(t){let n=t instanceof Error?t:Error(String(t));e.reportRuntimeError(new e.OreLifecycleError(`renderHook() mount callback failed`,{cause:n,component:`renderHook`,phase:`mounted`}),d)}}catch(e){throw u.dispose(),d.remove(),e}await r.flush();let h=!1;function g(){h||(h=!0,u.dispose(),d.remove())}return{dispose:g,get disposed(){return h},async flush(){await r.flush()},result:m,[Symbol.dispose](){g()}}}exports.renderHook=a;
2
2
  //# sourceMappingURL=render-hook.cjs.map
@@ -1 +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 { OreLifecycleError, reportRuntimeError } from '../errors';\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 formResetCallbacks: Array<() => void> = [];\n const ctx = { element: hostEl, formResetCallbacks, 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 (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\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":"kJAsDA,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,EAEpD,EAAM,CAAE,QAAS,EAAQ,mBAAA,CAAA,EAAoB,gBAAe,EAE9D,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,OAAS,EAAO,CAOd,EAAA,mBACE,IAAI,EAAA,kBAAkB,qCAAsC,CAC1D,MAJQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAKhE,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,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 { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** 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 = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired — renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\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":"kJAsDA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,GAAA,EAAY,EAAA,YAAA,CAAY,EAExB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAMhC,IAAM,EAAM,EAAA,qBAAqB,CAAM,EACjC,CAAE,kBAAmB,EAEvB,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,YAAY,EAAA,UAAU,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAAS,EAAO,CAKd,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEpE,EAAA,mBACE,IAAI,EAAA,kBAAkB,qCAAsC,CAC1D,MAAO,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,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,2 +1,2 @@
1
- import{flush as e}from"./flush.js";import{OreLifecycleError as t,reportRuntimeError as n}from"../errors.js";import{runWithContext as r}from"../runtime.js";import{createProps as i}from"../props.js";import{onCleanup as a,scope as o}from"@vielzeug/ripple";async function s(s,c){let l=typeof s!=`function`,u=l?s:void 0,d=l?c:s,f=o(),p=document.createElement(`div`);document.body.appendChild(p);let m=[],h={element:p,formResetCallbacks:[],mountCallbacks:m},g;try{f.run(()=>{r(h,()=>{let e=u?i(p,u):{};g=d(e)})});for(let e of m)try{f.run(()=>{r(h,()=>{let t=e();typeof t==`function`&&a(t)})})}catch(e){n(new t(`renderHook() mount callback failed`,{cause:e instanceof Error?e:Error(String(e)),component:`renderHook`,phase:`mounted`}),p)}}catch(e){throw f.dispose(),p.remove(),e}await e();let _=!1;function v(){_||(_=!0,f.dispose(),p.remove())}return{dispose:v,get disposed(){return _},async flush(){await e()},result:g,[Symbol.dispose](){v()}}}export{s as renderHook};
1
+ import{OreLifecycleError as e,reportRuntimeError as t}from"../errors.js";import{createRuntimeContext as n,onCleanup as r,runWithContext as i}from"../runtime.js";import{createProps as a}from"../props.js";import{flush as o}from"./flush.js";import{createScope as s}from"@vielzeug/ripple";async function c(c,l){let u=typeof c!=`function`,d=u?c:void 0,f=u?l:c,p=s(),m=document.createElement(`div`);document.body.appendChild(m);let h=n(m),{mountCallbacks:g}=h,_;try{p.run(()=>{i(h,()=>{let e=d?a(m,d):{};_=f(e)})});for(let n of g)try{p.run(()=>{i(h,()=>{let e=n();typeof e==`function`&&r(e)})})}catch(n){let r=n instanceof Error?n:Error(String(n));t(new e(`renderHook() mount callback failed`,{cause:r,component:`renderHook`,phase:`mounted`}),m)}}catch(e){throw p.dispose(),m.remove(),e}await o();let v=!1;function y(){v||(v=!0,p.dispose(),m.remove())}return{dispose:y,get disposed(){return v},async flush(){await o()},result:_,[Symbol.dispose](){y()}}}export{c 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 { OreLifecycleError, reportRuntimeError } from '../errors';\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 formResetCallbacks: Array<() => void> = [];\n const ctx = { element: hostEl, formResetCallbacks, 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 (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\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":"6PAsDA,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,EAEpD,EAAM,CAAE,QAAS,EAAQ,mBAAA,CAAA,EAAoB,gBAAe,EAE9D,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,OAAS,EAAO,CAOd,EACE,IAAI,EAAkB,qCAAsC,CAC1D,MAJQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAKhE,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,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 { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** 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 = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired — renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n }\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":"6RAsDA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,EAAY,EAAY,EAExB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAMhC,IAAM,EAAM,EAAqB,CAAM,EACjC,CAAE,kBAAmB,EAEvB,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,EAAU,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAAS,EAAO,CAKd,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEpE,EACE,IAAI,EAAkB,qCAAsC,CAC1D,MAAO,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../utils/css.cjs"),t=require("../utils/id.cjs");var n=()=>{e._clearStylesheetCache(),t._resetIdCounter()};exports.resetOreForTests=n;
2
+ //# sourceMappingURL=reset.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.cjs","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules — those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and both ID counters. Called by `cleanup()` — call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"iEAcA,IAAa,MAA+B,CAC1C,EAAA,sBAAsB,EACtB,EAAA,gBAAgB,CAClB"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * One honest entry point for cross-test isolation. Replaces the collection of
3
+ * `_`-private reset functions the test harness used to import from individual
4
+ * modules — those stay private to their modules; this is the supported seam.
5
+ */
6
+ /**
7
+ * Reset every piece of global ore state a test can touch: the stylesheet cache
8
+ * and both ID counters. Called by `cleanup()` — call it directly only if you
9
+ * manage mounted elements yourself.
10
+ */
11
+ export declare const resetOreForTests: () => void;
12
+ //# sourceMappingURL=reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../src/testing/reset.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAO,IAGnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import{_clearStylesheetCache as e}from"../utils/css.js";import{_resetIdCounter as t}from"../utils/id.js";var n=()=>{e(),t()};export{n as resetOreForTests};
2
+ //# sourceMappingURL=reset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.js","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules — those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and both ID counters. Called by `cleanup()` — call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"yGAcA,IAAa,MAA+B,CAC1C,EAAsB,EACtB,EAAgB,CAClB"}
package/dist/testing.cjs CHANGED
@@ -1 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./testing/flush.cjs"),t=require("./testing/dom.cjs"),n=require("./testing/form-internals-polyfill.cjs"),r=require("./testing/query.cjs"),i=require("./testing/mount.cjs"),a=require("./testing/events.cjs"),o=require("./testing/interactions.cjs"),s=require("./testing/wait.cjs"),c=require("./testing/render-hook.cjs"),l=require("./testing/testing.cjs");exports.FLUSH_DEEP=e.FLUSH_DEEP,exports.cleanup=i.cleanup,exports.createPointerEvent=a.createPointerEvent,exports.fire=a.fire,exports.flush=e.flush,exports.install=l.install,exports.installFormInternalsPolyfill=n.installFormInternalsPolyfill,exports.mock=i.mock,exports.mount=i.mount,exports.mountComponent=i.mountComponent,exports.renderHook=c.renderHook,exports.user=o.user,exports.waitFor=s.waitFor,exports.waitForEvent=s.waitForEvent,exports.walkFlatTree=t.walkFlatTree,exports.within=r.within;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./testing/dom.cjs"),n=require("./testing/form-internals-polyfill.cjs"),r=require("./testing/flush.cjs"),i=require("./testing/reset.cjs"),a=require("./testing/mount.cjs"),o=require("./testing/render-hook.cjs");function s(e,t={}){e(a.cleanup),t.formInternals&&n.installFormInternalsPolyfill()}exports.OreTimeoutError=e.OreTimeoutError,exports.cleanup=a.cleanup,exports.debugFlush=r.debugFlush,exports.flush=r.flush,exports.install=s,exports.installFormInternalsPolyfill=n.installFormInternalsPolyfill,exports.mock=a.mock,exports.mount=a.mount,exports.mountComponent=a.mountComponent,exports.renderHook=o.renderHook,exports.resetOreForTests=i.resetOreForTests,exports.walkFlatTree=t.walkFlatTree;
2
+ //# sourceMappingURL=testing.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.cjs","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This barrel re-exports the full testing API for convenience.\n * For tree-shaking, import directly from the focused sub-modules.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout —\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API.\nexport { OreTimeoutError } from '../errors';\n\nexport { debugFlush, flush, type FlushOptions } from './flush';\nexport { walkFlatTree } from './dom';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { cleanup, mock, mount, mountComponent, type Fixture, type MountOptions, type MountSetup } from './mount';\nexport { renderHook, type HookFixture } from './render-hook';\nexport { resetOreForTests } from './reset';\n\nexport type InstallOptions = {\n /**\n * Polyfill the jsdom/happy-dom gaps in `ElementInternals`/`FormData`/`<form>.reset()`\n * that `useField()` needs (see `installFormInternalsPolyfill`). Global monkey-patches,\n * so opt in only when the suite tests form-associated components.\n * @default false\n */\n formInternals?: boolean;\n};\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n * Pass `{ formInternals: true }` when testing form-associated (`useField`) components.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach, { formInternals: true });\n */\nexport function install(afterEachHook: (fn: () => void) => void, options: InstallOptions = {}): void {\n afterEachHook(cleanup);\n\n if (options.formInternals) installFormInternalsPolyfill();\n}\n"],"mappings":"+TA6CA,SAAgB,EAAQ,EAAyC,EAA0B,CAAC,EAAS,CACnG,EAAc,EAAA,OAAO,EAEjB,EAAQ,eAAe,EAAA,6BAA6B,CAC1D"}
package/dist/testing.js CHANGED
@@ -1 +1,2 @@
1
- import{FLUSH_DEEP as e,flush as t}from"./testing/flush.js";import{walkFlatTree as n}from"./testing/dom.js";import{installFormInternalsPolyfill as r}from"./testing/form-internals-polyfill.js";import{within as i}from"./testing/query.js";import{cleanup as a,mock as o,mount as s,mountComponent as c}from"./testing/mount.js";import{createPointerEvent as l,fire as u}from"./testing/events.js";import{user as d}from"./testing/interactions.js";import{waitFor as f,waitForEvent as p}from"./testing/wait.js";import{renderHook as m}from"./testing/render-hook.js";import{install as h}from"./testing/testing.js";export{e as FLUSH_DEEP,a as cleanup,l as createPointerEvent,u as fire,t as flush,h as install,r as installFormInternalsPolyfill,o as mock,s as mount,c as mountComponent,m as renderHook,d as user,f as waitFor,p as waitForEvent,n as walkFlatTree,i as within};
1
+ import{OreTimeoutError as e}from"./errors.js";import{walkFlatTree as t}from"./testing/dom.js";import{installFormInternalsPolyfill as n}from"./testing/form-internals-polyfill.js";import{debugFlush as r,flush as i}from"./testing/flush.js";import{resetOreForTests as a}from"./testing/reset.js";import{cleanup as o,mock as s,mount as c,mountComponent as l}from"./testing/mount.js";import{renderHook as u}from"./testing/render-hook.js";function d(e,t={}){e(o),t.formInternals&&n()}export{e as OreTimeoutError,o as cleanup,r as debugFlush,i as flush,d as install,n as installFormInternalsPolyfill,s as mock,c as mount,l as mountComponent,u as renderHook,a as resetOreForTests,t as walkFlatTree};
2
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This barrel re-exports the full testing API for convenience.\n * For tree-shaking, import directly from the focused sub-modules.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout —\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API.\nexport { OreTimeoutError } from '../errors';\n\nexport { debugFlush, flush, type FlushOptions } from './flush';\nexport { walkFlatTree } from './dom';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { cleanup, mock, mount, mountComponent, type Fixture, type MountOptions, type MountSetup } from './mount';\nexport { renderHook, type HookFixture } from './render-hook';\nexport { resetOreForTests } from './reset';\n\nexport type InstallOptions = {\n /**\n * Polyfill the jsdom/happy-dom gaps in `ElementInternals`/`FormData`/`<form>.reset()`\n * that `useField()` needs (see `installFormInternalsPolyfill`). Global monkey-patches,\n * so opt in only when the suite tests form-associated components.\n * @default false\n */\n formInternals?: boolean;\n};\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n * Pass `{ formInternals: true }` when testing form-associated (`useField`) components.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach, { formInternals: true });\n */\nexport function install(afterEachHook: (fn: () => void) => void, options: InstallOptions = {}): void {\n afterEachHook(cleanup);\n\n if (options.formInternals) installFormInternalsPolyfill();\n}\n"],"mappings":"+aA6CA,SAAgB,EAAQ,EAAyC,EAA0B,CAAC,EAAS,CACnG,EAAc,CAAO,EAEjB,EAAQ,eAAe,EAA6B,CAC1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"aria.cjs","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `syncAria()` where every config key is an ARIA attribute.\n *\n * - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.\n * Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.\n */\n\n/** Normalise an aria config key to a full `aria-*` attribute name. */\nexport const normalizeAriaKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', 'expanded' → 'aria-expanded'\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : `aria-${key}`;\n};\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAaA,IAAa,EAAoB,GAC3B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM,QAAQ,IAIpE,EAAwB,GAC/B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM"}
1
+ {"version":3,"file":"aria.cjs","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `aria()` and `bind({ aria: ... })`, where every config key is an ARIA attribute.\n *\n * - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.\n * Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.\n */\n\n/** Normalise an aria config key to a full `aria-*` attribute name. */\nexport const normalizeAriaKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', 'expanded' → 'aria-expanded'\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : `aria-${key}`;\n};\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAaA,IAAa,EAAoB,GAC3B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM,QAAQ,IAIpE,EAAwB,GAC/B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM"}
@@ -4,7 +4,7 @@
4
4
  * Two flavours are needed across the codebase:
5
5
  *
6
6
  * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.
7
- * Used by `syncAria()` where every config key is an ARIA attribute.
7
+ * Used by `aria()` and `bind({ aria: ... })`, where every config key is an ARIA attribute.
8
8
  *
9
9
  * - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.
10
10
  * Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.
@@ -1 +1 @@
1
- {"version":3,"file":"aria.js","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `syncAria()` where every config key is an ARIA attribute.\n *\n * - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.\n * Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.\n */\n\n/** Normalise an aria config key to a full `aria-*` attribute name. */\nexport const normalizeAriaKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', 'expanded' → 'aria-expanded'\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : `aria-${key}`;\n};\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAaA,IAAa,EAAoB,GAC3B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM,QAAQ,IAIpE,EAAwB,GAC/B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM"}
1
+ {"version":3,"file":"aria.js","names":[],"sources":["../../src/utils/aria.ts"],"sourcesContent":["/**\n * Shared ARIA attribute key normalisation helpers.\n *\n * Two flavours are needed across the codebase:\n *\n * - `normalizeAriaKey` — always adds the `aria-` prefix for non-role keys.\n * Used by `aria()` and `bind({ aria: ... })`, where every config key is an ARIA attribute.\n *\n * - `normalizeHostAttrKey` — passes non-ARIA keys through unchanged.\n * Used by `host.bind({ attr: ... })` where keys may be arbitrary HTML attributes.\n */\n\n/** Normalise an aria config key to a full `aria-*` attribute name. */\nexport const normalizeAriaKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', 'expanded' → 'aria-expanded'\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : `aria-${key}`;\n};\n\n/** Normalise a host-bind attr key: aria-camelCase → aria-kebab-case; other keys pass through. */\nexport const normalizeHostAttrKey = (key: string): string => {\n if (key === 'role' || key.startsWith('aria-')) return key;\n\n // 'ariaLabel' → 'aria-label', other keys unchanged\n return key.startsWith('aria') ? `aria-${key.slice(4).toLowerCase()}` : key;\n};\n"],"mappings":"AAaA,IAAa,EAAoB,GAC3B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM,QAAQ,IAIpE,EAAwB,GAC/B,IAAQ,QAAU,EAAI,WAAW,OAAO,EAAU,EAG/C,EAAI,WAAW,MAAM,EAAI,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,YAAY,IAAM"}
@@ -0,0 +1,2 @@
1
+ var e=e=>{let t=Symbol.for(e);return{is:e=>typeof e==`object`&&!!e&&t in e,stamp:e=>Object.assign(e,{[t]:!0})}};exports.makeBrand=e;
2
+ //# sourceMappingURL=brand.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.cjs","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page — the exact failure mode `src/iife.ts`'s header documents). Identity-based checks\n * silently fail across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper — do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAgBA,IAAa,EAA+B,GAA0B,CACpE,IAAM,EAAQ,OAAO,IAAI,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAO,GAAU,YAAY,GAAkB,KAAU,EAEvF,MAHE,GAAc,OAAO,OAAO,EAAK,EAAG,GAAQ,EAAK,CAAC,CAG9C,CACrB"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.
3
+ *
4
+ * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are
5
+ * process-global, so brands survive a duplicated module graph (two copies of ore bundled into
6
+ * one page — the exact failure mode `src/iife.ts`'s header documents). Identity-based checks
7
+ * silently fail across those copies: an object stamped by one graph is invisible to the other.
8
+ * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,
9
+ * `LiveBinding`, `CSSResult`) goes through this helper — do not introduce a second mechanism.
10
+ */
11
+ export type Brand<T extends object> = {
12
+ is: (value: unknown) => value is T;
13
+ stamp: (obj: T) => T;
14
+ };
15
+ export declare const makeBrand: <T extends object>(key: string) => Brand<T>;
16
+ //# sourceMappingURL=brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../../src/utils/brand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,MAAM,IAAI;IACpC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAAE,KAAK,MAAM,KAAG,KAAK,CAAC,CAAC,CAMhE,CAAC"}
@@ -0,0 +1,2 @@
1
+ var e=e=>{let t=Symbol.for(e);return{is:e=>typeof e==`object`&&!!e&&t in e,stamp:e=>Object.assign(e,{[t]:!0})}};export{e as makeBrand};
2
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page — the exact failure mode `src/iife.ts`'s header documents). Identity-based checks\n * silently fail across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper — do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAgBA,IAAa,EAA+B,GAA0B,CACpE,IAAM,EAAQ,OAAO,IAAI,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAO,GAAU,YAAY,GAAkB,KAAU,EAEvF,MAHE,GAAc,OAAO,OAAO,EAAK,EAAG,GAAQ,EAAK,CAAC,CAG9C,CACrB"}
@@ -1,2 +1,2 @@
1
- const e=require("../_dev.cjs");var t=new WeakSet,n=e=>typeof e==`object`&&!!e&&t.has(e),r=function(){return this.content},i=(e,...i)=>{let a=``;for(let t=0;t<e.length;t++)if(a+=e[t],t<i.length){let e=i[t];a+=n(e)?e.content:String(e)}let o={content:a.trim(),toString:r};return t.add(o),o},a=new Map,o=()=>{a.clear()},s=t=>{if(t instanceof CSSStyleSheet)return t;let n=typeof t==`string`?t:t.content,r=a.get(n);if(r)return r;let i=new CSSStyleSheet;try{i.replaceSync(n)}catch(t){e.error(`Style sheet replace failed`,t)}return a.set(n,i),i};exports._clearStylesheetCache=o,exports.css=i,exports.isCssResult=n,exports.loadStylesheet=s;
1
+ require("../_dev.cjs");var e=require("./brand.cjs").makeBrand(`ore:css-result`),t=e.is,n=function(){return this.content},r=(r,...i)=>{let a=``;for(let e=0;e<r.length;e++)if(a+=r[e],e<i.length){let n=i[e];a+=t(n)?n.content:String(n)}return e.stamp({content:a.trim(),toString:n})},i=new Map,a=()=>{i.clear()},o=e=>{if(e instanceof CSSStyleSheet)return e;let t=typeof e==`string`?e:e.content,n=i.get(t);if(n)return n;let r=new CSSStyleSheet;try{r.replaceSync(t)}catch{return r}return i.set(t,r),r};exports._clearStylesheetCache=a,exports.css=r,exports.isCssResult=t,exports.loadStylesheet=o;
2
2
  //# sourceMappingURL=css.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"css.cjs","names":[],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = new WeakSet<CSSResult>();\n\nexport const isCssResult = (value: unknown): value is CSSResult =>\n typeof value === 'object' && value !== null && cssResultBrand.has(value as CSSResult);\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n const result: CSSResult = { content: content.trim(), toString: cssResultToString };\n\n cssResultBrand.add(result);\n\n return result;\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) return cached;\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n error('Style sheet replace failed', err);\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n return sheet;\n};\n"],"mappings":"+BAWA,IAAM,EAAiB,IAAI,QAEd,EAAe,GAC1B,OAAO,GAAU,YAAY,GAAkB,EAAe,IAAI,CAAkB,EAEhF,EAAoB,UAAmC,CAC3D,OAAO,KAAK,OACd,EAEa,GAAO,EAA+B,GAAG,IAA0D,CAC9G,IAAI,EAAU,GAEd,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAGlC,GAFA,GAAW,EAAQ,GAEf,EAAI,EAAO,OAAQ,CACrB,IAAM,EAAI,EAAO,GAEjB,GAAW,EAAY,CAAC,EAAI,EAAE,QAAU,OAAO,CAAC,CAClD,CAGF,IAAM,EAAoB,CAAE,QAAS,EAAQ,KAAK,EAAG,SAAU,CAAkB,EAIjF,OAFA,EAAe,IAAI,CAAM,EAElB,CACT,EAEM,EAAwB,IAAI,IAGrB,MAAoC,CAC/C,EAAsB,MAAM,CAC9B,EAEa,EAAkB,GAA6D,CAC1F,GAAI,aAAiB,cAAe,OAAO,EAE3C,IAAM,EAAU,OAAO,GAAU,SAAW,EAAQ,EAAM,QACpD,EAAS,EAAsB,IAAI,CAAO,EAEhD,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAQ,IAAI,cAElB,GAAI,CACF,EAAM,YAAY,CAAO,CAC3B,OAAS,EAAK,CACZ,EAAA,MAAM,6BAA8B,CAAG,CACzC,CAIA,OAFA,EAAsB,IAAI,EAAS,CAAK,EAEjC,CACT"}
1
+ {"version":3,"file":"css.cjs","names":[],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\nimport { makeBrand } from './brand';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = makeBrand<CSSResult>('ore:css-result');\n\nexport const isCssResult = cssResultBrand.is;\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n return cssResultBrand.stamp({ content: content.trim(), toString: cssResultToString });\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) return cached;\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n // Deliberately not cached: a broken sheet served from cache would fail silently\n // for every subsequent caller with no further error.\n error('Style sheet replace failed', err);\n\n return sheet;\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n return sheet;\n};\n"],"mappings":"uBAYA,IAAM,uBAAiB,CAAA,CAAA,UAAqB,gBAAgB,EAE/C,EAAc,EAAe,GAEpC,EAAoB,UAAmC,CAC3D,OAAO,KAAK,OACd,EAEa,GAAO,EAA+B,GAAG,IAA0D,CAC9G,IAAI,EAAU,GAEd,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAGlC,GAFA,GAAW,EAAQ,GAEf,EAAI,EAAO,OAAQ,CACrB,IAAM,EAAI,EAAO,GAEjB,GAAW,EAAY,CAAC,EAAI,EAAE,QAAU,OAAO,CAAC,CAClD,CAGF,OAAO,EAAe,MAAM,CAAE,QAAS,EAAQ,KAAK,EAAG,SAAU,CAAkB,CAAC,CACtF,EAEM,EAAwB,IAAI,IAGrB,MAAoC,CAC/C,EAAsB,MAAM,CAC9B,EAEa,EAAkB,GAA6D,CAC1F,GAAI,aAAiB,cAAe,OAAO,EAE3C,IAAM,EAAU,OAAO,GAAU,SAAW,EAAQ,EAAM,QACpD,EAAS,EAAsB,IAAI,CAAO,EAEhD,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAQ,IAAI,cAElB,GAAI,CACF,EAAM,YAAY,CAAO,CAC3B,MAAc,CAKZ,OAAO,CACT,CAIA,OAFA,EAAsB,IAAI,EAAS,CAAK,EAEjC,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/utils/css.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,IAAI,MAAM,CAAC;CACpB,CAAC;AAIF,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,SACiC,CAAC;AAMxF,eAAO,MAAM,GAAG,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,KAAK,CAAC,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC,KAAG,SAkBlG,CAAC;AASF,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,aAAa,GAAG,SAAS,KAAG,aAmB1E,CAAC"}
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/utils/css.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,IAAI,MAAM,CAAC;CACpB,CAAC;AAIF,eAAO,MAAM,WAAW,wCAAoB,CAAC;AAM7C,eAAO,MAAM,GAAG,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,KAAK,CAAC,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC,KAAG,SAclG,CAAC;AASF,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,aAAa,GAAG,SAAS,KAAG,aAuB1E,CAAC"}
package/dist/utils/css.js CHANGED
@@ -1,2 +1,2 @@
1
- import{error as e}from"../_dev.js";var t=new WeakSet,n=e=>typeof e==`object`&&!!e&&t.has(e),r=function(){return this.content},i=(e,...i)=>{let a=``;for(let t=0;t<e.length;t++)if(a+=e[t],t<i.length){let e=i[t];a+=n(e)?e.content:String(e)}let o={content:a.trim(),toString:r};return t.add(o),o},a=new Map,o=()=>{a.clear()},s=t=>{if(t instanceof CSSStyleSheet)return t;let n=typeof t==`string`?t:t.content,r=a.get(n);if(r)return r;let i=new CSSStyleSheet;try{i.replaceSync(n)}catch(t){e(`Style sheet replace failed`,t)}return a.set(n,i),i};export{o as _clearStylesheetCache,i as css,n as isCssResult,s as loadStylesheet};
1
+ import"../_dev.js";import{makeBrand as e}from"./brand.js";var t=e(`ore:css-result`),n=t.is,r=function(){return this.content},i=(e,...i)=>{let a=``;for(let t=0;t<e.length;t++)if(a+=e[t],t<i.length){let e=i[t];a+=n(e)?e.content:String(e)}return t.stamp({content:a.trim(),toString:r})},a=new Map,o=()=>{a.clear()},s=e=>{if(e instanceof CSSStyleSheet)return e;let t=typeof e==`string`?e:e.content,n=a.get(t);if(n)return n;let r=new CSSStyleSheet;try{r.replaceSync(t)}catch{return r}return a.set(t,r),r};export{o as _clearStylesheetCache,i as css,n as isCssResult,s as loadStylesheet};
2
2
  //# sourceMappingURL=css.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"css.js","names":[],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = new WeakSet<CSSResult>();\n\nexport const isCssResult = (value: unknown): value is CSSResult =>\n typeof value === 'object' && value !== null && cssResultBrand.has(value as CSSResult);\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n const result: CSSResult = { content: content.trim(), toString: cssResultToString };\n\n cssResultBrand.add(result);\n\n return result;\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) return cached;\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n error('Style sheet replace failed', err);\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n return sheet;\n};\n"],"mappings":"mCAWA,IAAM,EAAiB,IAAI,QAEd,EAAe,GAC1B,OAAO,GAAU,YAAY,GAAkB,EAAe,IAAI,CAAkB,EAEhF,EAAoB,UAAmC,CAC3D,OAAO,KAAK,OACd,EAEa,GAAO,EAA+B,GAAG,IAA0D,CAC9G,IAAI,EAAU,GAEd,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAGlC,GAFA,GAAW,EAAQ,GAEf,EAAI,EAAO,OAAQ,CACrB,IAAM,EAAI,EAAO,GAEjB,GAAW,EAAY,CAAC,EAAI,EAAE,QAAU,OAAO,CAAC,CAClD,CAGF,IAAM,EAAoB,CAAE,QAAS,EAAQ,KAAK,EAAG,SAAU,CAAkB,EAIjF,OAFA,EAAe,IAAI,CAAM,EAElB,CACT,EAEM,EAAwB,IAAI,IAGrB,MAAoC,CAC/C,EAAsB,MAAM,CAC9B,EAEa,EAAkB,GAA6D,CAC1F,GAAI,aAAiB,cAAe,OAAO,EAE3C,IAAM,EAAU,OAAO,GAAU,SAAW,EAAQ,EAAM,QACpD,EAAS,EAAsB,IAAI,CAAO,EAEhD,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAQ,IAAI,cAElB,GAAI,CACF,EAAM,YAAY,CAAO,CAC3B,OAAS,EAAK,CACZ,EAAM,6BAA8B,CAAG,CACzC,CAIA,OAFA,EAAsB,IAAI,EAAS,CAAK,EAEjC,CACT"}
1
+ {"version":3,"file":"css.js","names":[],"sources":["../../src/utils/css.ts"],"sourcesContent":["/**\n * CSS tagged template utility and CSSStyleSheet caching.\n */\n\nimport { error } from '../_dev';\nimport { makeBrand } from './brand';\n\nexport type CSSResult = {\n content: string;\n toString(): string;\n};\n\nconst cssResultBrand = makeBrand<CSSResult>('ore:css-result');\n\nexport const isCssResult = cssResultBrand.is;\n\nconst cssResultToString = function (this: CSSResult): string {\n return this.content;\n};\n\nexport const css = (strings: TemplateStringsArray, ...values: Array<CSSResult | string | number>): CSSResult => {\n let content = '';\n\n for (let i = 0; i < strings.length; i++) {\n content += strings[i];\n\n if (i < values.length) {\n const v = values[i];\n\n content += isCssResult(v) ? v.content : String(v);\n }\n }\n\n return cssResultBrand.stamp({ content: content.trim(), toString: cssResultToString });\n};\n\nconst stylesheetStringCache = new Map<string, CSSStyleSheet>();\n\n/** @internal Clear the stylesheet cache. Used for test isolation and HMR. */\nexport const _clearStylesheetCache = (): void => {\n stylesheetStringCache.clear();\n};\n\nexport const loadStylesheet = (style: string | CSSStyleSheet | CSSResult): CSSStyleSheet => {\n if (style instanceof CSSStyleSheet) return style;\n\n const cssText = typeof style === 'string' ? style : style.content;\n const cached = stylesheetStringCache.get(cssText);\n\n if (cached) return cached;\n\n const sheet = new CSSStyleSheet();\n\n try {\n sheet.replaceSync(cssText);\n } catch (err) {\n // Deliberately not cached: a broken sheet served from cache would fail silently\n // for every subsequent caller with no further error.\n error('Style sheet replace failed', err);\n\n return sheet;\n }\n\n stylesheetStringCache.set(cssText, sheet);\n\n return sheet;\n};\n"],"mappings":"0DAYA,IAAM,EAAiB,EAAqB,gBAAgB,EAE/C,EAAc,EAAe,GAEpC,EAAoB,UAAmC,CAC3D,OAAO,KAAK,OACd,EAEa,GAAO,EAA+B,GAAG,IAA0D,CAC9G,IAAI,EAAU,GAEd,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAGlC,GAFA,GAAW,EAAQ,GAEf,EAAI,EAAO,OAAQ,CACrB,IAAM,EAAI,EAAO,GAEjB,GAAW,EAAY,CAAC,EAAI,EAAE,QAAU,OAAO,CAAC,CAClD,CAGF,OAAO,EAAe,MAAM,CAAE,QAAS,EAAQ,KAAK,EAAG,SAAU,CAAkB,CAAC,CACtF,EAEM,EAAwB,IAAI,IAGrB,MAAoC,CAC/C,EAAsB,MAAM,CAC9B,EAEa,EAAkB,GAA6D,CAC1F,GAAI,aAAiB,cAAe,OAAO,EAE3C,IAAM,EAAU,OAAO,GAAU,SAAW,EAAQ,EAAM,QACpD,EAAS,EAAsB,IAAI,CAAO,EAEhD,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAQ,IAAI,cAElB,GAAI,CACF,EAAM,YAAY,CAAO,CAC3B,MAAc,CAKZ,OAAO,CACT,CAIA,OAFA,EAAsB,IAAI,EAAS,CAAK,EAEjC,CACT"}
@@ -1,2 +1,2 @@
1
- const e=require("../_dev.cjs");var t=e=>{for(let t=e.length-1;t>=0;t--)e[t]()},n=e=>{for(let t of e)t.remove()},r=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),i=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,a=(t,n,a)=>{let o=n.toLowerCase();if(/^on[a-z]/i.test(n)){e.warn(`Blocked setAttribute("${n}", ...) — inline event handler attributes are not supported. Use @${n.slice(2)} binding syntax instead.`),t.removeAttribute(n);return}if(o===`srcdoc`){e.warn(`Blocked setAttribute("srcdoc", ...) — "srcdoc" holds raw HTML, not a URL, and is not supported via attribute binding. Use raw() with a registered sanitizer if you need to inject trusted HTML.`),t.removeAttribute(n);return}if(a==null||a===!1){t.removeAttribute(n);return}let s=a===!0?`true`:String(a);if(r.has(o)&&i.test(s)){e.warn(`Blocked dangerous URL scheme in attribute "${n}". Only safe URLs are permitted in URL-accepting attributes.`),t.removeAttribute(n);return}t.setAttribute(n,s)},o=(e,t,n,r)=>{if(!e)return()=>{};let i=n;return e.addEventListener(t,i,r),()=>e.removeEventListener(t,i,r)},s=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),c=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.isStructuredValue=c,exports.listen=o,exports.removeNodes=n,exports.runAll=t,exports.setAttr=a,exports.toKebab=s;
1
+ require("../_dev.cjs");const e=require("../errors.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t=e.length-1;t>=0;t--)e[t]()},o=e=>{for(let t of e)t.remove()},s=()=>{let e=[],t=[];return{clear(){a(t),o(e),t=[],e=[]},get nodes(){return e},registerCleanup(e){t.push(e)},setNodes(t){e=t}}},c=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=(e,t,n)=>{let r=t.toLowerCase();if(/^on[a-z]/i.test(t)){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r===`srcdoc`){e.removeAttribute(t);return}if(n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);if(c.has(r)&&l.test(i)){`${t}`,e.removeAttribute(t);return}e.setAttribute(t,i)},d=(t,n,r,i)=>{if(!t)return e.ORE_ERRORS.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},f=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),p=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=s,exports.isStructuredValue=p,exports.listen=d,exports.removeNodes=o,exports.resolveMaybeReactive=n,exports.runAll=a,exports.sanitizeCssToken=i,exports.setAttr=u,exports.toKebab=f;
2
2
  //# sourceMappingURL=dom.cjs.map