@vielzeug/ore 26.11.8 → 26.11.10

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 (121) hide show
  1. package/dist/base-element.cjs +1 -1
  2. package/dist/base-element.cjs.map +1 -1
  3. package/dist/base-element.d.ts +12 -26
  4. package/dist/base-element.d.ts.map +1 -1
  5. package/dist/base-element.js +1 -1
  6. package/dist/base-element.js.map +1 -1
  7. package/dist/context.cjs.map +1 -1
  8. package/dist/context.js.map +1 -1
  9. package/dist/define.cjs +1 -1
  10. package/dist/define.cjs.map +1 -1
  11. package/dist/define.d.ts.map +1 -1
  12. package/dist/define.js +1 -1
  13. package/dist/define.js.map +1 -1
  14. package/dist/directives/each.cjs +1 -1
  15. package/dist/directives/each.cjs.map +1 -1
  16. package/dist/directives/each.d.ts +5 -0
  17. package/dist/directives/each.d.ts.map +1 -1
  18. package/dist/directives/each.js +1 -1
  19. package/dist/directives/each.js.map +1 -1
  20. package/dist/directives/model.cjs.map +1 -1
  21. package/dist/directives/model.d.ts +2 -3
  22. package/dist/directives/model.d.ts.map +1 -1
  23. package/dist/directives/model.js.map +1 -1
  24. package/dist/directives/unsafe-html.cjs +1 -1
  25. package/dist/directives/unsafe-html.cjs.map +1 -1
  26. package/dist/directives/unsafe-html.d.ts.map +1 -1
  27. package/dist/directives/unsafe-html.js +1 -1
  28. package/dist/directives/unsafe-html.js.map +1 -1
  29. package/dist/directives/when.cjs +1 -1
  30. package/dist/directives/when.cjs.map +1 -1
  31. package/dist/directives/when.d.ts.map +1 -1
  32. package/dist/directives/when.js +1 -1
  33. package/dist/directives/when.js.map +1 -1
  34. package/dist/errors.cjs +1 -1
  35. package/dist/errors.cjs.map +1 -1
  36. package/dist/errors.d.ts +2 -0
  37. package/dist/errors.d.ts.map +1 -1
  38. package/dist/errors.js +1 -1
  39. package/dist/errors.js.map +1 -1
  40. package/dist/host-bind.cjs +1 -1
  41. package/dist/host-bind.cjs.map +1 -1
  42. package/dist/host-bind.d.ts +2 -2
  43. package/dist/host-bind.js +1 -1
  44. package/dist/host-bind.js.map +1 -1
  45. package/dist/index.cjs +1 -1
  46. package/dist/index.d.ts +0 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1 -1
  49. package/dist/ore.cjs +2 -2
  50. package/dist/ore.cjs.map +1 -1
  51. package/dist/ore.iife.js +2 -2
  52. package/dist/ore.iife.js.map +1 -1
  53. package/dist/ore.js +423 -373
  54. package/dist/ore.js.map +1 -1
  55. package/dist/props.cjs +1 -1
  56. package/dist/props.cjs.map +1 -1
  57. package/dist/props.d.ts +6 -10
  58. package/dist/props.d.ts.map +1 -1
  59. package/dist/props.js +1 -1
  60. package/dist/props.js.map +1 -1
  61. package/dist/runtime.cjs +1 -1
  62. package/dist/runtime.cjs.map +1 -1
  63. package/dist/runtime.d.ts +22 -9
  64. package/dist/runtime.d.ts.map +1 -1
  65. package/dist/runtime.js +1 -1
  66. package/dist/runtime.js.map +1 -1
  67. package/dist/slots.cjs +1 -1
  68. package/dist/slots.cjs.map +1 -1
  69. package/dist/slots.d.ts.map +1 -1
  70. package/dist/slots.js +1 -1
  71. package/dist/slots.js.map +1 -1
  72. package/dist/template/binding-types.d.ts +3 -9
  73. package/dist/template/binding-types.d.ts.map +1 -1
  74. package/dist/template/bindings.cjs +1 -1
  75. package/dist/template/bindings.cjs.map +1 -1
  76. package/dist/template/bindings.d.ts +8 -9
  77. package/dist/template/bindings.d.ts.map +1 -1
  78. package/dist/template/bindings.js +1 -1
  79. package/dist/template/bindings.js.map +1 -1
  80. package/dist/template/instantiator.cjs +1 -1
  81. package/dist/template/instantiator.cjs.map +1 -1
  82. package/dist/template/instantiator.d.ts.map +1 -1
  83. package/dist/template/instantiator.js +1 -1
  84. package/dist/template/instantiator.js.map +1 -1
  85. package/dist/template/result.cjs +1 -1
  86. package/dist/template/result.cjs.map +1 -1
  87. package/dist/template/result.d.ts +10 -16
  88. package/dist/template/result.d.ts.map +1 -1
  89. package/dist/template/result.js +1 -1
  90. package/dist/template/result.js.map +1 -1
  91. package/dist/testing/index.d.ts +1 -1
  92. package/dist/testing/index.d.ts.map +1 -1
  93. package/dist/testing/mount.cjs +1 -1
  94. package/dist/testing/mount.cjs.map +1 -1
  95. package/dist/testing/mount.d.ts.map +1 -1
  96. package/dist/testing/mount.js +1 -1
  97. package/dist/testing/mount.js.map +1 -1
  98. package/dist/testing/render-hook.cjs +1 -1
  99. package/dist/testing/render-hook.cjs.map +1 -1
  100. package/dist/testing/render-hook.d.ts +16 -13
  101. package/dist/testing/render-hook.d.ts.map +1 -1
  102. package/dist/testing/render-hook.js +1 -1
  103. package/dist/testing/render-hook.js.map +1 -1
  104. package/dist/testing.cjs.map +1 -1
  105. package/dist/testing.js.map +1 -1
  106. package/dist/utils/brand.cjs.map +1 -1
  107. package/dist/utils/brand.d.ts +1 -1
  108. package/dist/utils/brand.js.map +1 -1
  109. package/dist/utils/dom.cjs +1 -1
  110. package/dist/utils/dom.cjs.map +1 -1
  111. package/dist/utils/dom.d.ts +12 -10
  112. package/dist/utils/dom.d.ts.map +1 -1
  113. package/dist/utils/dom.js +1 -1
  114. package/dist/utils/dom.js.map +1 -1
  115. package/package.json +5 -5
  116. package/dist/directives/live.cjs +0 -2
  117. package/dist/directives/live.cjs.map +0 -1
  118. package/dist/directives/live.d.ts +0 -23
  119. package/dist/directives/live.d.ts.map +0 -1
  120. package/dist/directives/live.js +0 -2
  121. package/dist/directives/live.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"result.cjs","names":["signal","makeBrand","require_brand","_vielzeug_ripple","ref","directiveBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","createHtmlResult","applyFn","nodes","fragment","parent","anchor","registerCleanup"],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts: Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`): see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment, registerCleanup: (fn: () => void) => void) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function: only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\n/**\n * The output of an `html` tagged template call.\n *\n * Each `html` call produces an independent fragment: there is no shared mutable\n * state between instances, so the same template can be safely rendered multiple\n * times (e.g. inside `each()`).\n *\n * `mount()` is the entire public surface: insertion and reactive wiring in one\n * step, so the two can never get out of sync (the old public `fragment` + `apply`\n * pair made it possible to insert without wiring, or wire without inserting).\n */\nexport interface HTMLResult {\n /**\n * Insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null) and wire up all reactive effects. Returns the inserted\n * nodes so callers can remove them later.\n */\n mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];\n}\n\n/**\n * @internal The full result the template engine itself works with. `fragment` and\n * `apply` are the engine's own two-phase insertion protocol (static-embed merging in\n * the instantiator, `insertHtmlValues` in the binding layer): not part of the\n * public API. Consumers only ever see `HTMLResult`.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(registerCleanup: (fn: () => void) => void): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\nexport function createHtmlResult(\n fragment: DocumentFragment,\n applyFn: (registerCleanup: (fn: () => void) => void) => void,\n): CompiledHTMLResult {\n const mount = (parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[] => {\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn(registerCleanup);\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, fragment, mount });\n}\n"],"mappings":"A,wEAgBA,SAAgBI,GAAiC,CAC/C,OAAA,EAAOJ,EAAAA,OAAAA,CAAiB,IAAI,CAC9B,CAUA,IAAMK,EAAiBJ,EAAAA,UAA2B,eAAe,EAMpDK,EAAyB,GACpCD,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnBE,EAAoBF,EAAe,GAqC1CG,EAAkBP,EAAAA,UAA8B,iBAAiB,EAE1DQ,EAAeD,EAAgB,GAE5C,SAAgBE,EACd,EACA,EACoB,CAUpB,OAAOF,EAAgB,MAAM,CAAE,MAAOG,EAAS,WAAU,OAT1C,EAAoB,EAAqB,IAAsD,CAC5G,IAAMC,EAAQ,MAAM,KAAKC,EAAS,UAAU,EAK5C,OAHAC,EAAO,aAAaD,EAAUE,CAAM,EACpCJ,EAAQK,CAAe,EAEhBJ,CACT,CAE+D,CAAC,CAClE,C"}
1
+ {"version":3,"file":"result.cjs","names":["signal","makeBrand","OreApiError","ORE_ERRORS","require_errors","require_brand","_vielzeug_ripple","ref","directiveBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","asCompiled","result","createHtmlResult","claimed","claim","applyFn","nodes","fragment","parent","anchor"],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts: Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`): see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function: only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\ndeclare const htmlResultTag: unique symbol;\n\n/**\n * The output of an `html` tagged template call: an opaque, single-use value.\n *\n * Each `html` call produces an independent fragment bound to its own interpolated\n * values, so it can be rendered exactly once: return it from `setup()`, interpolate\n * it into another template, or return it from a directive callback. Rendering the\n * same result twice throws; call the template function again for a second copy.\n */\nexport interface HTMLResult {\n readonly [htmlResultTag]: true;\n}\n\n/**\n * @internal The engine-facing shape behind every `HTMLResult`. `fragment` and `apply`\n * are the two-phase insertion protocol (static-embed merging in the instantiator,\n * `insertHtmlValues` in the binding layer); `mount` does both in one step. Every\n * consumer calls `claim()` first, which enforces single use.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(): void;\n /** Mark this result as consumed; throws `OreApiError` when it already was. */\n claim(): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n /**\n * Claim, insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null), and wire up all reactive effects. Returns the inserted nodes.\n * Listeners and nested effects register themselves on the active ripple owner.\n */\n mount(parent: ParentNode, anchor: Node | null): Node[];\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\n/** @internal Narrow a public `HTMLResult` to the engine shape, rejecting anything else. */\nexport const asCompiled = (result: HTMLResult): CompiledHTMLResult => {\n if (!isHtmlResult(result)) throw new OreApiError(ORE_ERRORS.notAnHtmlResult);\n\n return result;\n};\n\nexport function createHtmlResult(fragment: DocumentFragment, applyFn: () => void): CompiledHTMLResult {\n let claimed = false;\n\n const claim = (): void => {\n if (claimed) throw new OreApiError(ORE_ERRORS.htmlResultReused);\n\n claimed = true;\n };\n\n const mount = (parent: ParentNode, anchor: Node | null): Node[] => {\n claim();\n\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn();\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, claim, fragment, mount } as unknown as CompiledHTMLResult);\n}\n"],"mappings":"A,mGAiBA,SAAgBO,GAAiC,CAC/C,OAAA,EAAOP,EAAAA,OAAAA,CAAiB,IAAI,CAC9B,CAUA,IAAMQ,EAAiBP,EAAAA,UAA2B,eAAe,EAMpDQ,EAAyB,GACpCD,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnBE,EAAoBF,EAAe,GAuC1CG,EAAkBV,EAAAA,UAA8B,iBAAiB,EAE1DW,EAAeD,EAAgB,GAG/BE,EAAc,GAA2C,CACpE,GAAI,CAACD,EAAaE,CAAM,EAAG,MAAM,IAAIZ,EAAAA,YAAYC,EAAAA,WAAW,eAAe,EAE3E,OAAOW,CACT,EAEA,SAAgBC,EAAiB,EAA4B,EAAyC,CACpG,IAAIC,EAAU,GAERC,MAAoB,CACxB,GAAID,EAAS,MAAM,IAAId,EAAAA,YAAYC,EAAAA,WAAW,gBAAgB,EAE9D,EAAU,EACZ,EAaA,OAAOQ,EAAgB,MAAM,CAAE,MAAOO,EAAS,QAAO,WAAU,OAXjD,EAAoB,IAAgC,CACjED,EAAM,EAEN,IAAME,EAAQ,MAAM,KAAKC,EAAS,UAAU,EAK5C,OAHAC,EAAO,aAAaD,EAAUE,CAAM,EACpCJ,EAAQ,EAEDC,CACT,CAEsE,CAAkC,CAC1G,C"}
@@ -10,7 +10,7 @@ export type Ref<T extends Element> = Signal<T | null>;
10
10
  export declare function ref<T extends Element>(): Ref<T>;
11
11
  export type RefCallback<T extends Element> = (el: T | null) => void;
12
12
  export type DirectiveResult = {
13
- mount: (anchor: Comment, registerCleanup: (fn: () => void) => void) => void;
13
+ mount: (anchor: Comment) => void;
14
14
  };
15
15
  /**
16
16
  * Creates a registered DirectiveResult. All directive factories must use this
@@ -18,25 +18,19 @@ export type DirectiveResult = {
18
18
  */
19
19
  export declare const createDirectiveResult: (mount: DirectiveResult["mount"]) => DirectiveResult;
20
20
  export declare const isDirectiveResult: (value: unknown) => value is DirectiveResult;
21
+ declare const htmlResultTag: unique symbol;
21
22
  /**
22
- * The output of an `html` tagged template call.
23
+ * The output of an `html` tagged template call: an opaque, single-use value.
23
24
  *
24
- * Each `html` call produces an independent fragment: there is no shared mutable
25
- * state between instances, so the same template can be safely rendered multiple
26
- * times (e.g. inside `each()`).
27
- *
28
- * `mount()` is the entire public surface: insertion and reactive wiring in one
29
- * step, so the two can never get out of sync (the old public `fragment` + `apply`
30
- * pair made it possible to insert without wiring, or wire without inserting).
25
+ * Each `html` call produces an independent fragment bound to its own interpolated
26
+ * values, so it can be rendered exactly once: return it from `setup()`, interpolate
27
+ * it into another template, or return it from a directive callback. Rendering the
28
+ * same result twice throws; call the template function again for a second copy.
31
29
  */
32
30
  export interface HTMLResult {
33
- /**
34
- * Insert the template's nodes before `anchor` (or append to `parent` when
35
- * `anchor` is null) and wire up all reactive effects. Returns the inserted
36
- * nodes so callers can remove them later.
37
- */
38
- mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];
31
+ readonly [htmlResultTag]: true;
39
32
  }
40
33
  export declare const isHtmlResult: (value: unknown) => value is CompiledHTMLResult;
41
- export declare function createHtmlResult(fragment: DocumentFragment, applyFn: (registerCleanup: (fn: () => void) => void) => void): CompiledHTMLResult;
34
+ export declare function createHtmlResult(fragment: DocumentFragment, applyFn: () => void): CompiledHTMLResult;
35
+ export {};
42
36
  //# sourceMappingURL=result.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/template/result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;AAMvD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,IAAI,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAEtD,wBAAgB,GAAG,CAAC,CAAC,SAAS,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAE/C;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;AAIpE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;CAC7E,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,eAAe,CAAC,OAAO,CAAC,KAAG,eACvC,CAAC;AAElC,eAAO,MAAM,iBAAiB,8CAAoB,CAAC;AAInD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;CACnG;AAiBD,eAAO,MAAM,YAAY,iDAAqB,CAAC;AAE/C,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,GAC3D,kBAAkB,CAWpB"}
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/template/result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;AAOvD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,IAAI,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAEtD,wBAAgB,GAAG,CAAC,CAAC,SAAS,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAE/C;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;AAIpE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAClC,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,eAAe,CAAC,OAAO,CAAC,KAAG,eACvC,CAAC;AAElC,eAAO,MAAM,iBAAiB,8CAAoB,CAAC;AAInD,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;CAChC;AAyBD,eAAO,MAAM,YAAY,iDAAqB,CAAC;AAS/C,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,kBAAkB,CAqBpG"}
@@ -1,2 +1,2 @@
1
- import{makeBrand as e}from"../utils/brand.js";import{signal as t}from"@vielzeug/ripple";function n(){return t(null)}var r=e(`ore:directive`),i=e=>r.stamp({mount:e}),a=r.is,o=e(`ore:html-result`),s=o.is;function c(e,t){return o.stamp({apply:t,fragment:e,mount:(n,r,i)=>{let a=Array.from(e.childNodes);return n.insertBefore(e,r),t(i),a}})}export{i as createDirectiveResult,c as createHtmlResult,a as isDirectiveResult,s as isHtmlResult,n as ref};
1
+ import{ORE_ERRORS as e,OreApiError as t}from"../errors.js";import{makeBrand as n}from"../utils/brand.js";import{signal as r}from"@vielzeug/ripple";function i(){return r(null)}var a=n(`ore:directive`),o=e=>a.stamp({mount:e}),s=a.is,c=n(`ore:html-result`),l=c.is,u=n=>{if(!l(n))throw new t(e.notAnHtmlResult);return n};function d(n,r){let i=!1,a=()=>{if(i)throw new t(e.htmlResultReused);i=!0};return c.stamp({apply:r,claim:a,fragment:n,mount:(e,t)=>{a();let i=Array.from(n.childNodes);return e.insertBefore(n,t),r(),i}})}export{u as asCompiled,o as createDirectiveResult,d as createHtmlResult,s as isDirectiveResult,l as isHtmlResult,i as ref};
2
2
  //# sourceMappingURL=result.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"result.js","names":["ref","signal","directiveBrand","makeBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","createHtmlResult","applyFn","nodes","fragment","parent","anchor","registerCleanup"],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts: Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`): see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment, registerCleanup: (fn: () => void) => void) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function: only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\n/**\n * The output of an `html` tagged template call.\n *\n * Each `html` call produces an independent fragment: there is no shared mutable\n * state between instances, so the same template can be safely rendered multiple\n * times (e.g. inside `each()`).\n *\n * `mount()` is the entire public surface: insertion and reactive wiring in one\n * step, so the two can never get out of sync (the old public `fragment` + `apply`\n * pair made it possible to insert without wiring, or wire without inserting).\n */\nexport interface HTMLResult {\n /**\n * Insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null) and wire up all reactive effects. Returns the inserted\n * nodes so callers can remove them later.\n */\n mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];\n}\n\n/**\n * @internal The full result the template engine itself works with. `fragment` and\n * `apply` are the engine's own two-phase insertion protocol (static-embed merging in\n * the instantiator, `insertHtmlValues` in the binding layer): not part of the\n * public API. Consumers only ever see `HTMLResult`.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(registerCleanup: (fn: () => void) => void): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\nexport function createHtmlResult(\n fragment: DocumentFragment,\n applyFn: (registerCleanup: (fn: () => void) => void) => void,\n): CompiledHTMLResult {\n const mount = (parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[] => {\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn(registerCleanup);\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, fragment, mount });\n}\n"],"mappings":"A,wFAgBA,SAAgBA,GAAiC,CAC/C,OAAOC,EAAiB,IAAI,CAC9B,CAUA,IAAMC,EAAiBC,EAA2B,eAAe,EAMpDC,EAAyB,GACpCF,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnBG,EAAoBH,EAAe,GAqC1CI,EAAkBH,EAA8B,iBAAiB,EAE1DI,EAAeD,EAAgB,GAE5C,SAAgBE,EACd,EACA,EACoB,CAUpB,OAAOF,EAAgB,MAAM,CAAE,MAAOG,EAAS,WAAU,OAT1C,EAAoB,EAAqB,IAAsD,CAC5G,IAAMC,EAAQ,MAAM,KAAKC,EAAS,UAAU,EAK5C,OAHAC,EAAO,aAAaD,EAAUE,CAAM,EACpCJ,EAAQK,CAAe,EAEhBJ,CACT,CAE+D,CAAC,CAClE,C"}
1
+ {"version":3,"file":"result.js","names":["ref","signal","directiveBrand","makeBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","asCompiled","result","OreApiError","ORE_ERRORS","createHtmlResult","claimed","claim","applyFn","nodes","fragment","parent","anchor"],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts: Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`): see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function: only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\ndeclare const htmlResultTag: unique symbol;\n\n/**\n * The output of an `html` tagged template call: an opaque, single-use value.\n *\n * Each `html` call produces an independent fragment bound to its own interpolated\n * values, so it can be rendered exactly once: return it from `setup()`, interpolate\n * it into another template, or return it from a directive callback. Rendering the\n * same result twice throws; call the template function again for a second copy.\n */\nexport interface HTMLResult {\n readonly [htmlResultTag]: true;\n}\n\n/**\n * @internal The engine-facing shape behind every `HTMLResult`. `fragment` and `apply`\n * are the two-phase insertion protocol (static-embed merging in the instantiator,\n * `insertHtmlValues` in the binding layer); `mount` does both in one step. Every\n * consumer calls `claim()` first, which enforces single use.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(): void;\n /** Mark this result as consumed; throws `OreApiError` when it already was. */\n claim(): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n /**\n * Claim, insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null), and wire up all reactive effects. Returns the inserted nodes.\n * Listeners and nested effects register themselves on the active ripple owner.\n */\n mount(parent: ParentNode, anchor: Node | null): Node[];\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\n/** @internal Narrow a public `HTMLResult` to the engine shape, rejecting anything else. */\nexport const asCompiled = (result: HTMLResult): CompiledHTMLResult => {\n if (!isHtmlResult(result)) throw new OreApiError(ORE_ERRORS.notAnHtmlResult);\n\n return result;\n};\n\nexport function createHtmlResult(fragment: DocumentFragment, applyFn: () => void): CompiledHTMLResult {\n let claimed = false;\n\n const claim = (): void => {\n if (claimed) throw new OreApiError(ORE_ERRORS.htmlResultReused);\n\n claimed = true;\n };\n\n const mount = (parent: ParentNode, anchor: Node | null): Node[] => {\n claim();\n\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn();\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, claim, fragment, mount } as unknown as CompiledHTMLResult);\n}\n"],"mappings":"A,mJAiBA,SAAgBA,GAAiC,CAC/C,OAAOC,EAAiB,IAAI,CAC9B,CAUA,IAAMC,EAAiBC,EAA2B,eAAe,EAMpDC,EAAyB,GACpCF,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnBG,EAAoBH,EAAe,GAuC1CI,EAAkBH,EAA8B,iBAAiB,EAE1DI,EAAeD,EAAgB,GAG/BE,EAAc,GAA2C,CACpE,GAAI,CAACD,EAAaE,CAAM,EAAG,MAAM,IAAIC,EAAYC,EAAW,eAAe,EAE3E,OAAOF,CACT,EAEA,SAAgBG,EAAiB,EAA4B,EAAyC,CACpG,IAAIC,EAAU,GAERC,MAAoB,CACxB,GAAID,EAAS,MAAM,IAAIH,EAAYC,EAAW,gBAAgB,EAE9D,EAAU,EACZ,EAaA,OAAOL,EAAgB,MAAM,CAAE,MAAOS,EAAS,QAAO,WAAU,OAXjD,EAAoB,IAAgC,CACjED,EAAM,EAEN,IAAME,EAAQ,MAAM,KAAKC,EAAS,UAAU,EAK5C,OAHAC,EAAO,aAAaD,EAAUE,CAAM,EACpCJ,EAAQ,EAEDC,CACT,CAEsE,CAAkC,CAC1G,C"}
@@ -7,7 +7,7 @@
7
7
  */
8
8
  export { type FlushOptions, flush, OreTimeoutError } from './flush';
9
9
  export { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';
10
- export { type HookFixture, renderHook } from './render-hook';
10
+ export { type HookFixture, type RenderHookOptions, renderHook } from './render-hook';
11
11
  /**
12
12
  * Register auto-cleanup after each test. Call once in your test setup file.
13
13
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE7D;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,IAAI,CAErE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAErF;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,IAAI,CAErE"}
@@ -1,2 +1,2 @@
1
- const e=require("../define.cjs"),t=require("../utils/dom.cjs"),n=require("./flush.cjs"),r=require("./render-hook.cjs"),i=require("./reset.cjs");let a=require("@vielzeug/assay");var o=[],s=0;function c(e,n,r){t.setAttr(e,n,r)}var l=e=>e instanceof Error?e:Error(String(e)),u=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=l(e.error??e.message),e.preventDefault()},r=e=>{t=l(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 d(t,r={}){let{attrs:i={},componentOptions:l,container:d=document.body,html:f,props:p={}}=r,m,h;typeof t==`string`?m=t:(m=`trial-${++s}`,h={...l??{},setup:t}),h&&e.define(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(i))c(g,e,t);try{await u(async()=>{d.appendChild(g),o.push(g),await n.flush()})}catch(e){g.remove();let t=o.indexOf(g);throw t!==-1&&o.splice(t,1),e}let _=!1;function v(){if(_)return;_=!0,g.remove();let e=o.indexOf(g);e!==-1&&o.splice(e,1)}return{...(0,a.within)(g.shadowRoot??g),async act(e){await e(),await n.flush()},async attr(e,t){c(g,e,t),await n.flush()},async attrs(e){for(let[t,n]of Object.entries(e))c(g,t,n);await n.flush()},dispose:v,get disposed(){return _},element:g,flush:n.flush,get shadow(){return g.shadowRoot},[Symbol.dispose](){v()}}}function f(){for(let e of o)e.remove();o.length=0,r._disposeRenderHooks(),i.resetOreForTests()}exports._mountedElements=o,exports.cleanup=f,exports.mount=d;
1
+ const e=require("../define.cjs"),t=require("../utils/dom.cjs"),n=require("./flush.cjs"),r=require("./reset.cjs");let i=require("@vielzeug/assay");var a=[],o=0;function s(e,n,r){t.setAttr(e,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(t,r={}){let{attrs:c={},componentOptions:u,container:d=document.body,html:f,props:p={}}=r,m,h;typeof t==`string`?m=t:(m=`trial-${++o}`,h={...u??{},setup:t}),h&&e.define(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.flush()})}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{...(0,i.within)(g.shadowRoot??g),async act(e){await e(),await n.flush()},async attr(e,t){s(g,e,t),await n.flush()},async attrs(e){for(let[t,n]of Object.entries(e))s(g,t,n);await n.flush()},dispose:v,get disposed(){return _},element:g,flush:n.flush,get shadow(){return g.shadowRoot},[Symbol.dispose](){v()}}}function d(){for(let e of a)e.remove();a.length=0,r.resetOreForTests()}exports._mountedElements=a,exports.cleanup=d,exports.mount=u;
2
2
  //# sourceMappingURL=mount.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mount.cjs","names":["flush","within","require_define","require_dom","require_flush","require_render_hook","require_reset","_vielzeug_assay","_mountedElements","_componentTagCounter","applyAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","html","props","attrs","container","err","i","isDisposed","dispose","fn","record","cleanup","el"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
1
+ {"version":3,"file":"mount.cjs","names":["flush","within","require_define","require_dom","require_flush","require_reset","_vielzeug_assay","_mountedElements","_componentTagCounter","applyAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","html","props","attrs","container","err","i","isDisposed","dispose","fn","record","cleanup","el"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,SAAS,CAAC;AAQnD;;;GAGG;AACH,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,CAAE,SAAQ,UAAU;IAC9E,0FAA0F;IAC1F,GAAG,CAAC,EAAE,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,+DAA+D;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,yCAAyC;IACzC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,qDAAqD;IACrD,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4BAA4B;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,yDAAyD;IACzD,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IACnC,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/E,gDAAgD;IAChD,SAAS,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC;IACrC,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,KAAK,UAAU,GAAG;IAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;AAG9D,MAAM,MAAM,UAAU,GAAG;IACvB,cAAc,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,GAAG,IAAI,CAAC;CAC1D,CAAC,gBAAgB,CAAC,CAAC;AAIpB,eAAO,MAAM,gBAAgB,EAAE,WAAW,EAAO,CAAC;AAiDlD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC7D,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACvB,wBAAsB,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC7D,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAqGvB;;;;;;GAMG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAK9B"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,SAAS,CAAC;AAOnD;;;GAGG;AACH,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,CAAE,SAAQ,UAAU;IAC9E,0FAA0F;IAC1F,GAAG,CAAC,EAAE,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,+DAA+D;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,yCAAyC;IACzC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,qDAAqD;IACrD,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4BAA4B;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,yDAAyD;IACzD,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IACnC,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/E,gDAAgD;IAChD,SAAS,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC;IACrC,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,KAAK,UAAU,GAAG;IAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;AAG9D,MAAM,MAAM,UAAU,GAAG;IACvB,cAAc,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,GAAG,IAAI,CAAC;CAC1D,CAAC,gBAAgB,CAAC,CAAC;AAIpB,eAAO,MAAM,gBAAgB,EAAE,WAAW,EAAO,CAAC;AAiDlD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC7D,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACvB,wBAAsB,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC7D,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAqGvB;;;;;;GAMG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAI9B"}
@@ -1,2 +1,2 @@
1
- import{define as e}from"../define.js";import{setAttr as t}from"../utils/dom.js";import{flush as n}from"./flush.js";import{_disposeRenderHooks as r}from"./render-hook.js";import{resetOreForTests as i}from"./reset.js";import{within as a}from"@vielzeug/assay";var o=[],s=0;function c(e,n,r){t(e,n,r)}var l=e=>e instanceof Error?e:Error(String(e)),u=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=l(e.error??e.message),e.preventDefault()},r=e=>{t=l(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 d(t,r={}){let{attrs:i={},componentOptions:l,container:d=document.body,html:f,props:p={}}=r,m,h;typeof t==`string`?m=t:(m=`trial-${++s}`,h={...l??{},setup:t}),h&&e(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(i))c(g,e,t);try{await u(async()=>{d.appendChild(g),o.push(g),await n()})}catch(e){g.remove();let t=o.indexOf(g);throw t!==-1&&o.splice(t,1),e}let _=!1;function v(){if(_)return;_=!0,g.remove();let e=o.indexOf(g);e!==-1&&o.splice(e,1)}return{...a(g.shadowRoot??g),async act(e){await e(),await n()},async attr(e,t){c(g,e,t),await n()},async attrs(e){for(let[t,n]of Object.entries(e))c(g,t,n);await n()},dispose:v,get disposed(){return _},element:g,flush:n,get shadow(){return g.shadowRoot},[Symbol.dispose](){v()}}}function f(){for(let e of o)e.remove();o.length=0,r(),i()}export{o as _mountedElements,f as cleanup,d as mount};
1
+ import{define as e}from"../define.js";import{setAttr as t}from"../utils/dom.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(e,n,r){t(e,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(t,r={}){let{attrs:c={},componentOptions:u,container:d=document.body,html:f,props:p={}}=r,m,h;typeof t==`string`?m=t:(m=`trial-${++o}`,h={...u??{},setup:t}),h&&e(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()}}}function d(){for(let e of a)e.remove();a.length=0,r()}export{a as _mountedElements,d as cleanup,u as mount};
2
2
  //# sourceMappingURL=mount.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mount.js","names":["_mountedElements","_componentTagCounter","applyAttr","setAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","define","html","props","attrs","container","flush","err","i","isDisposed","dispose","within","fn","record","cleanup","el","_disposeRenderHooks","resetOreForTests"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
1
+ {"version":3,"file":"mount.js","names":["_mountedElements","_componentTagCounter","applyAttr","setAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","define","html","props","attrs","container","flush","err","i","isDisposed","dispose","within","fn","record","cleanup","el","resetOreForTests"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n 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("../errors.cjs"),t=require("../props.cjs"),n=require("../runtime.cjs"),r=require("./flush.cjs");let i=require("@vielzeug/ripple");var a=new Set,o=()=>{for(let e of[...a])e()};async function s(o,s){let c=typeof o!=`function`,l=c?o:void 0,u=c?s:o,d=(0,i.createScope)(),f=document.createElement(`div`);document.body.appendChild(f);let p=n.createRuntimeContext(f),{mountCallbacks:m}=p,h;try{d.run(()=>{n.runWithContext(p,()=>{let e=l?t.createProps(f,l):{};h=u(e)})});for(let t of m)try{d.run(()=>{n.runWithContext(p,()=>{let e=t();typeof e==`function`&&n.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`}),f)}}catch(e){throw d.dispose(),f.remove(),e}await r.flush();let g=!1;function _(){g||(g=!0,d.dispose(),f.remove(),a.delete(_))}return a.add(_),{dispose:_,get disposed(){return g},async flush(){await r.flush()},result:h,[Symbol.dispose](){_()}}}exports._disposeRenderHooks=o,exports.renderHook=s;
1
+ const e=require("./mount.cjs");async function t(t,n,r){let i=typeof t!=`function`,a=i?n:t,o=i?r:n,s,c=await e.mount(e=>(s=a(e),null),{componentOptions:{formAssociated:o?.formAssociated,props:i?t:void 0}});return{dispose:c.dispose,get disposed(){return c.disposed},element:c.element,flush:()=>c.flush(),result:s,[Symbol.dispose]:c.dispose}}exports.renderHook=t;
2
2
  //# sourceMappingURL=render-hook.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.cjs","names":["createScope","createRuntimeContext","createProps","OreLifecycleError","flush","require_errors","require_props","require_runtime","require_flush","_vielzeug_ripple","hookDisposers","_disposeRenderHooks","dispose","renderHook","hasPropDefs","setupOrDefs","propDefs","setup","maybeSetup","hostScope","hostEl","ctx","result","props","cb","mountCallbacks","cleanup","error","err","isDisposed"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"A,kJAyBA,IAAMU,EAAgB,IAAI,IAGbC,MAAkC,CAC7C,IAAK,IAAMC,IAAW,CAAC,GAAGF,CAAa,EAAGE,EAAQ,CACpD,EA+BA,eAAsBC,EACpB,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAAWF,EAAeC,EAAoB,IAAA,GAC9CE,EAAQH,EAAcI,EAAeH,EAErCI,GAAA,EAAYnB,EAAAA,YAAAA,CAAY,EAExBoB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAYA,CAAM,EAMhC,IAAMC,EAAMpB,EAAAA,qBAAqBmB,CAAM,EACjC,CAAE,kBAAmBC,EAEvBC,EAEJ,GAAI,CACFH,EAAU,QAAU,CAClBZ,EAAA,eAAec,MAAW,CACxB,IAAME,EAAQP,EAAWd,EAAAA,YAAYkB,EAAQJ,CAAyB,EAAK,CAAC,EAE5E,EAASC,EAAMM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAMC,KAAMC,EACf,GAAI,CACFN,EAAU,QAAU,CAClBZ,EAAA,eAAec,MAAW,CACxB,IAAMK,EAAUF,EAAG,EAEf,OAAOE,GAAY,YAAYnB,EAAA,UAAUmB,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAASC,EAAO,CAKd,IAAMC,EAAMD,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAEpEtB,EAAA,mBACE,IAAIF,EAAAA,kBAAkB,qCAAsC,CAC1D,MAAOyB,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACDR,CACF,CACF,CAEJ,OAASQ,EAAK,CAGZ,MAFAT,EAAU,QAAQ,EAClBC,EAAO,OAAO,EACRQ,CACR,CAEA,MAAMxB,EAAAA,MAAM,EAEZ,IAAIyB,EAAa,GAEjB,SAASjB,GAAU,CACbiB,IAEJ,EAAa,GACbV,EAAU,QAAQ,EAClBC,EAAO,OAAO,EACdV,EAAc,OAAOE,CAAO,EAC9B,CAIA,OAFAF,EAAc,IAAIE,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAOiB,CACT,EAEA,MAAM,OAAQ,CACZ,MAAMzB,EAAAA,MAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjBQ,EAAQ,CACV,CACF,CACF,C"}
1
+ {"version":3,"file":"render-hook.cjs","names":["mount","require_mount","renderHook","hasPropDefs","setupOrDefs","setup","setupOrOptions","options","maybeOptions","result","fixture","props"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without writing a component template.\n */\n\nimport type { InferProps, PropInputDefs } from '../props';\nimport { mount } from './mount';\n\nexport interface HookFixture<T> {\n /** Remove the host component; its cleanups run one microtask later, like `mount().dispose()`. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The generated host component running the hook. */\n readonly element: HTMLElement;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface RenderHookOptions {\n /** Register the host with `formAssociated: true`, for composables that call `useField()`. */\n formAssociated?: boolean;\n}\n\n/**\n * Run a setup function inside a real, generated host component and return its result.\n * Every lifecycle hook (`onMounted`, `onCleanup`, `onFormReset`, `useField`, ...) runs\n * exactly as it does in `define()`: the host is an ordinary Ore component.\n *\n * @example No props\n * ```ts\n * const { result } = 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({ label: prop.string('hello') }, (props) => props.label);\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(\n setup: (_props: Record<never, never>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n setupOrOptions?: ((props: InferProps<D>) => T) | RenderHookOptions,\n maybeOptions?: RenderHookOptions,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const setup = (hasPropDefs ? setupOrOptions : setupOrDefs) as (props: unknown) => T;\n const options = (hasPropDefs ? maybeOptions : setupOrOptions) as RenderHookOptions | undefined;\n\n let result!: T;\n\n const fixture = await mount(\n (props) => {\n result = setup(props);\n\n return null;\n },\n {\n componentOptions: {\n formAssociated: options?.formAssociated,\n props: hasPropDefs ? (setupOrDefs as D) : undefined,\n },\n },\n );\n\n return {\n dispose: fixture.dispose,\n get disposed() {\n return fixture.disposed;\n },\n element: fixture.element,\n flush: () => fixture.flush(),\n result,\n [Symbol.dispose]: fixture.dispose,\n };\n}\n"],"mappings":"A,+BA0DA,eAAsBE,EACpB,EACA,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAASF,EAAcG,EAAiBF,EACxCG,EAAWJ,EAAcK,EAAeF,EAE1CG,EAEEC,EAAU,MAAMV,EAAAA,MACnB,IACC,EAASK,EAAMM,CAAK,EAEb,MAET,CACE,iBAAkB,CAChB,eAAgBJ,GAAS,eACzB,MAAOJ,EAAeC,EAAoB,IAAA,EAC5C,CACF,CACF,EAEA,MAAO,CACL,QAASM,EAAQ,QACjB,IAAI,UAAW,CACb,OAAOA,EAAQ,QACjB,EACA,QAASA,EAAQ,QACjB,UAAaA,EAAQ,MAAM,EAC3B,UACC,OAAO,SAAUA,EAAQ,OAC5B,CACF,C"}
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation
3
- * without rendering a full component template.
3
+ * without writing a component template.
4
4
  */
5
- import { type InferProps, type PropInputDefs } from '../props';
5
+ import type { InferProps, PropInputDefs } from '../props';
6
6
  export interface HookFixture<T> {
7
- /** Teardown the hook (runs cleanups). Idempotent. */
7
+ /** Remove the host component; its cleanups run one microtask later, like `mount().dispose()`. Idempotent. */
8
8
  dispose(): void;
9
9
  /** `true` after `dispose()` has been called. */
10
10
  readonly disposed: boolean;
11
+ /** The generated host component running the hook. */
12
+ readonly element: HTMLElement;
11
13
  /** Flush pending reactive updates */
12
14
  flush(): Promise<void>;
13
15
  /** The value returned by the setup function */
@@ -15,14 +17,18 @@ export interface HookFixture<T> {
15
17
  /** Delegates to `dispose()`. Enables `using` declarations. */
16
18
  [Symbol.dispose](): void;
17
19
  }
20
+ export interface RenderHookOptions {
21
+ /** Register the host with `formAssociated: true`, for composables that call `useField()`. */
22
+ formAssociated?: boolean;
23
+ }
18
24
  /**
19
- * Run a setup function in a component-like context and return the result.
20
- * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real
21
- * `setup()`, since they resolve the same implicit current-component context.
25
+ * Run a setup function inside a real, generated host component and return its result.
26
+ * Every lifecycle hook (`onMounted`, `onCleanup`, `onFormReset`, `useField`, ...) runs
27
+ * exactly as it does in `define()`: the host is an ordinary Ore component.
22
28
  *
23
29
  * @example No props
24
30
  * ```ts
25
- * const { result, flush } = await renderHook(() => {
31
+ * const { result } = await renderHook(() => {
26
32
  * const count = signal(0);
27
33
  * onMounted(() => { count.value = 1; });
28
34
  * return count;
@@ -32,13 +38,10 @@ export interface HookFixture<T> {
32
38
  *
33
39
  * @example With props
34
40
  * ```ts
35
- * const { result } = await renderHook(
36
- * { label: prop.string('hello') },
37
- * (props) => props.label,
38
- * );
41
+ * const { result } = await renderHook({ label: prop.string('hello') }, (props) => props.label);
39
42
  * expect(result.value).toBe('hello');
40
43
  * ```
41
44
  */
42
- export declare function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;
43
- export declare function renderHook<D extends PropInputDefs, T>(propDefs: D, setup: (props: InferProps<D>) => T): Promise<HookFixture<T>>;
45
+ export declare function renderHook<T>(setup: (_props: Record<never, never>) => T, options?: RenderHookOptions): Promise<HookFixture<T>>;
46
+ export declare function renderHook<D extends PropInputDefs, T>(propDefs: D, setup: (props: InferProps<D>) => T, options?: RenderHookOptions): Promise<HookFixture<T>>;
44
47
  //# sourceMappingURL=render-hook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.d.ts","sourceRoot":"","sources":["../../src/testing/render-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAI5E,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,qDAAqD;IACrD,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qCAAqC;IACrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+CAA+C;IAC/C,MAAM,EAAE,CAAC,CAAC;IACV,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AASD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACzG,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EAAE,CAAC,EACzD,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"render-hook.d.ts","sourceRoot":"","sources":["../../src/testing/render-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG1D,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,6GAA6G;IAC7G,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,qCAAqC;IACrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+CAA+C;IAC/C,MAAM,EAAE,CAAC,CAAC;IACV,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3B,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EAAE,CAAC,EACzD,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,EAClC,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -1,2 +1,2 @@
1
- import{OreLifecycleError as e,reportRuntimeError as t}from"../errors.js";import{createProps as n}from"../props.js";import{createRuntimeContext as r,onCleanup as i,runWithContext as a}from"../runtime.js";import{flush as o}from"./flush.js";import{createScope as s}from"@vielzeug/ripple";var c=/* @__PURE__ */ new Set,l=()=>{for(let e of[...c])e()};async function u(l,u){let d=typeof l!=`function`,f=d?l:void 0,p=d?u:l,m=s(),h=document.createElement(`div`);document.body.appendChild(h);let g=r(h),{mountCallbacks:_}=g,v;try{m.run(()=>{a(g,()=>{let e=f?n(h,f):{};v=p(e)})});for(let n of _)try{m.run(()=>{a(g,()=>{let e=n();typeof e==`function`&&i(e)})})}catch(n){let r=n instanceof Error?n:Error(String(n));t(new e(`renderHook() mount callback failed`,{cause:r,component:`renderHook`,phase:`mounted`}),h)}}catch(e){throw m.dispose(),h.remove(),e}await o();let y=!1;function b(){y||(y=!0,m.dispose(),h.remove(),c.delete(b))}return c.add(b),{dispose:b,get disposed(){return y},async flush(){await o()},result:v,[Symbol.dispose](){b()}}}export{l as _disposeRenderHooks,u as renderHook};
1
+ import{mount as e}from"./mount.js";async function t(t,n,r){let i=typeof t!=`function`,a=i?n:t,o=i?r:n,s,c=await e(e=>(s=a(e),null),{componentOptions:{formAssociated:o?.formAssociated,props:i?t:void 0}});return{dispose:c.dispose,get disposed(){return c.disposed},element:c.element,flush:()=>c.flush(),result:s,[Symbol.dispose]:c.dispose}}export{t as renderHook};
2
2
  //# sourceMappingURL=render-hook.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.js","names":["hookDisposers","_disposeRenderHooks","dispose","renderHook","hasPropDefs","setupOrDefs","propDefs","setup","maybeSetup","hostScope","createScope","hostEl","ctx","createRuntimeContext","result","runWithContext","props","createProps","cb","mountCallbacks","cleanup","onCleanup","error","err","reportRuntimeError","OreLifecycleError","flush","isDisposed"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"A,6RAyBA,IAAMA,kBAAgB,IAAI,IAGbC,MAAkC,CAC7C,IAAK,IAAMC,IAAW,CAAC,GAAGF,CAAa,EAAGE,EAAQ,CACpD,EA+BA,eAAsBC,EACpB,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAAWF,EAAeC,EAAoB,IAAA,GAC9CE,EAAQH,EAAcI,EAAeH,EAErCI,EAAYC,EAAY,EAExBC,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAYA,CAAM,EAMhC,IAAMC,EAAMC,EAAqBF,CAAM,EACjC,CAAE,kBAAmBC,EAEvBE,EAEJ,GAAI,CACFL,EAAU,QAAU,CAClBM,EAAeH,MAAW,CACxB,IAAMI,EAAQV,EAAWW,EAAYN,EAAQL,CAAyB,EAAK,CAAC,EAE5E,EAASC,EAAMS,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAME,KAAMC,EACf,GAAI,CACFV,EAAU,QAAU,CAClBM,EAAeH,MAAW,CACxB,IAAMQ,EAAUF,EAAG,EAEf,OAAOE,GAAY,YAAYC,EAAUD,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAASE,EAAO,CAKd,IAAMC,EAAMD,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAEpEE,EACE,IAAIC,EAAkB,qCAAsC,CAC1D,MAAOF,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACDZ,CACF,CACF,CAEJ,OAASY,EAAK,CAGZ,MAFAd,EAAU,QAAQ,EAClBE,EAAO,OAAO,EACRY,CACR,CAEA,MAAMG,EAAM,EAEZ,IAAIC,EAAa,GAEjB,SAASzB,GAAU,CACbyB,IAEJ,EAAa,GACblB,EAAU,QAAQ,EAClBE,EAAO,OAAO,EACdX,EAAc,OAAOE,CAAO,EAC9B,CAIA,OAFAF,EAAc,IAAIE,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAOyB,CACT,EAEA,MAAM,OAAQ,CACZ,MAAMD,EAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjBxB,EAAQ,CACV,CACF,CACF,C"}
1
+ {"version":3,"file":"render-hook.js","names":["renderHook","hasPropDefs","setupOrDefs","setup","setupOrOptions","options","maybeOptions","result","fixture","mount","props"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without writing a component template.\n */\n\nimport type { InferProps, PropInputDefs } from '../props';\nimport { mount } from './mount';\n\nexport interface HookFixture<T> {\n /** Remove the host component; its cleanups run one microtask later, like `mount().dispose()`. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The generated host component running the hook. */\n readonly element: HTMLElement;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface RenderHookOptions {\n /** Register the host with `formAssociated: true`, for composables that call `useField()`. */\n formAssociated?: boolean;\n}\n\n/**\n * Run a setup function inside a real, generated host component and return its result.\n * Every lifecycle hook (`onMounted`, `onCleanup`, `onFormReset`, `useField`, ...) runs\n * exactly as it does in `define()`: the host is an ordinary Ore component.\n *\n * @example No props\n * ```ts\n * const { result } = 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({ label: prop.string('hello') }, (props) => props.label);\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(\n setup: (_props: Record<never, never>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n setupOrOptions?: ((props: InferProps<D>) => T) | RenderHookOptions,\n maybeOptions?: RenderHookOptions,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const setup = (hasPropDefs ? setupOrOptions : setupOrDefs) as (props: unknown) => T;\n const options = (hasPropDefs ? maybeOptions : setupOrOptions) as RenderHookOptions | undefined;\n\n let result!: T;\n\n const fixture = await mount(\n (props) => {\n result = setup(props);\n\n return null;\n },\n {\n componentOptions: {\n formAssociated: options?.formAssociated,\n props: hasPropDefs ? (setupOrDefs as D) : undefined,\n },\n },\n );\n\n return {\n dispose: fixture.dispose,\n get disposed() {\n return fixture.disposed;\n },\n element: fixture.element,\n flush: () => fixture.flush(),\n result,\n [Symbol.dispose]: fixture.dispose,\n };\n}\n"],"mappings":"A,mCA0DA,eAAsBA,EACpB,EACA,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAASF,EAAcG,EAAiBF,EACxCG,EAAWJ,EAAcK,EAAeF,EAE1CG,EAEEC,EAAU,MAAMC,EACnB,IACC,EAASN,EAAMO,CAAK,EAEb,MAET,CACE,iBAAkB,CAChB,eAAgBL,GAAS,eACzB,MAAOJ,EAAeC,EAAoB,IAAA,EAC5C,CACF,CACF,EAEA,MAAO,CACL,QAASM,EAAQ,QACjB,IAAI,UAAW,CACb,OAAOA,EAAQ,QACjB,EACA,QAASA,EAAQ,QACjB,UAAaA,EAAQ,MAAM,EAC3B,UACC,OAAO,SAAUA,EAAQ,OAC5B,CACF,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"testing.cjs","names":["cleanup","require_mount","require_flush","require_render_hook","install","afterEachHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,kLA6BA,SAAgBI,EAAQ,EAA+C,CACrEC,EAAcL,EAAAA,OAAO,CACvB,C"}
1
+ {"version":3,"file":"testing.cjs","names":["cleanup","require_mount","require_flush","require_render_hook","install","afterEachHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, type RenderHookOptions, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,kLA6BA,SAAgBI,EAAQ,EAA+C,CACrEC,EAAcL,EAAAA,OAAO,CACvB,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":["install","afterEachHook","cleanup","OreTimeoutError","flush","mount","renderHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,8KA6BA,SAAgBA,EAAQ,EAA+C,CACrEC,EAAcC,CAAO,CACvB,C"}
1
+ {"version":3,"file":"testing.js","names":["install","afterEachHook","cleanup","OreTimeoutError","flush","mount","renderHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, type RenderHookOptions, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,8KA6BA,SAAgBA,EAAQ,EAA+C,CACrEC,EAAcC,CAAO,CACvB,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"brand.cjs","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
1
+ {"version":3,"file":"brand.cjs","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `ModelSource`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
@@ -7,7 +7,7 @@
7
7
  * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail
8
8
  * across those copies: an object stamped by one graph is invisible to the other.
9
9
  * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,
10
- * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.
10
+ * `ModelSource`, `CSSResult`) goes through this helper: do not introduce a second mechanism.
11
11
  */
12
12
  export type Brand<T extends object> = {
13
13
  is: (value: unknown) => value is T;
@@ -1 +1 @@
1
- {"version":3,"file":"brand.js","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
1
+ {"version":3,"file":"brand.js","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `ModelSource`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
@@ -1,2 +1,2 @@
1
- require("../_dev.cjs");const e=require("../errors.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t=e.length-1;t>=0;t--)e[t]?.()},o=e=>{for(let t of e)t.remove()},s=()=>{let e=[],t=[];return{clear(){a(t),o(e),t=[],e=[]},get nodes(){return e},registerCleanup(e){t.push(e)},setNodes(t){e=t}}},c=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),l=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,u=/[\t\n\r]/g,d=/^[\s\p{C}]+|[\s\p{C}]+$/gu,f=e=>e.replace(u,``).replace(d,``),p=e=>l.test(f(e)),m=(e,t,n)=>{let r=t.toLowerCase();if(/^on[a-z]/i.test(t)){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r===`srcdoc`||n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);c.has(r)&&p(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},h=(t,n,r,i)=>{if(!t)return e.ORE_ERRORS.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},g=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),_=e=>i(e.startsWith(`--`)?e:g(e)),v=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},y=e=>{let t=new Set;for(let[r,i]of Object.entries(e)){if(!n(i))continue;let e=r.replace(/\s+/g,``);e&&t.add(e)}return t},b=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=s,exports.cssDeclarationName=_,exports.cssDeclarationValue=v,exports.isStructuredValue=b,exports.listen=h,exports.removeNodes=o,exports.resolveClassTokens=y,exports.resolveMaybeReactive=n,exports.runAll=a,exports.sanitizeCssToken=i,exports.setAttr=m,exports.toKebab=g;
1
+ require("../_dev.cjs");const e=require("../errors.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t of e)t.remove()},o=()=>{let e=[],n=null,r=()=>{n?.dispose(),n=null,a(e),e=[]};return{clear:r,get nodes(){return e},render(e){r();let i=(0,t.createScope)();n=i;try{return i.run(e)}catch(e){throw i.dispose(),n=null,e}},setNodes(t){e=t}}},s=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),c=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,l=/[\t\n\r]/g,u=/^[\s\p{C}]+|[\s\p{C}]+$/gu,d=e=>e.replace(l,``).replace(u,``),f=e=>c.test(d(e)),p=new Map,m=512,h=e=>{let t=p.get(e);if(t)return t;let n=e.toLowerCase(),r={event:/^on[a-z]/i.test(e),srcdoc:n===`srcdoc`,url:s.has(n)};return p.size>=m&&p.clear(),p.set(e,r),r},g=(e,t,n)=>{let r=h(t);if(r.event){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r.srcdoc||n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);r.url&&f(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},_=(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)},v=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),y=e=>i(e.startsWith(`--`)?e:v(e)),b=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},x=e=>{let t=new Set;for(let[r,i]of Object.entries(e)){if(!n(i))continue;let e=r.replace(/\s+/g,``);e&&t.add(e)}return t},S=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=o,exports.cssDeclarationName=y,exports.cssDeclarationValue=b,exports.isStructuredValue=S,exports.listen=_,exports.removeNodes=a,exports.resolveClassTokens=x,exports.resolveMaybeReactive=n,exports.sanitizeCssToken=i,exports.setAttr=g,exports.toKebab=v;
2
2
  //# sourceMappingURL=dom.cjs.map