@vielzeug/ore 26.10.0 → 26.10.5

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 (93) hide show
  1. package/README.md +1 -1
  2. package/dist/base-element.cjs.map +1 -1
  3. package/dist/base-element.d.ts +2 -2
  4. package/dist/base-element.js.map +1 -1
  5. package/dist/component-types.d.ts +2 -2
  6. package/dist/context.cjs.map +1 -1
  7. package/dist/context.d.ts +2 -2
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/context.js.map +1 -1
  10. package/dist/define.cjs.map +1 -1
  11. package/dist/define.d.ts +3 -3
  12. package/dist/define.js.map +1 -1
  13. package/dist/directives/classMap.cjs.map +1 -1
  14. package/dist/directives/classMap.js.map +1 -1
  15. package/dist/directives/each.cjs.map +1 -1
  16. package/dist/directives/each.d.ts +3 -3
  17. package/dist/directives/each.js.map +1 -1
  18. package/dist/directives/live.cjs.map +1 -1
  19. package/dist/directives/live.d.ts +1 -1
  20. package/dist/directives/live.js.map +1 -1
  21. package/dist/errors.cjs +1 -1
  22. package/dist/errors.cjs.map +1 -1
  23. package/dist/errors.d.ts +10 -10
  24. package/dist/errors.js +1 -1
  25. package/dist/errors.js.map +1 -1
  26. package/dist/forms/field.cjs.map +1 -1
  27. package/dist/forms/field.d.ts +4 -4
  28. package/dist/forms/field.js.map +1 -1
  29. package/dist/host-bind.cjs.map +1 -1
  30. package/dist/host-bind.d.ts +1 -1
  31. package/dist/host-bind.js.map +1 -1
  32. package/dist/ore.cjs +1 -1
  33. package/dist/ore.cjs.map +1 -1
  34. package/dist/ore.iife.js +1 -1
  35. package/dist/ore.iife.js.map +1 -1
  36. package/dist/ore.js +1 -1
  37. package/dist/ore.js.map +1 -1
  38. package/dist/props.cjs.map +1 -1
  39. package/dist/props.d.ts +3 -3
  40. package/dist/props.js.map +1 -1
  41. package/dist/runtime.cjs.map +1 -1
  42. package/dist/runtime.d.ts +5 -5
  43. package/dist/runtime.js.map +1 -1
  44. package/dist/slots.cjs.map +1 -1
  45. package/dist/slots.d.ts +3 -3
  46. package/dist/slots.js.map +1 -1
  47. package/dist/template/binding-types.d.ts +2 -2
  48. package/dist/template/bindings.cjs.map +1 -1
  49. package/dist/template/bindings.d.ts +2 -2
  50. package/dist/template/bindings.js.map +1 -1
  51. package/dist/template/compiler.cjs.map +1 -1
  52. package/dist/template/compiler.d.ts +1 -1
  53. package/dist/template/compiler.js.map +1 -1
  54. package/dist/template/instantiator.cjs.map +1 -1
  55. package/dist/template/instantiator.d.ts +1 -1
  56. package/dist/template/instantiator.js.map +1 -1
  57. package/dist/template/result.cjs.map +1 -1
  58. package/dist/template/result.d.ts +4 -4
  59. package/dist/template/result.js.map +1 -1
  60. package/dist/testing/dom.cjs.map +1 -1
  61. package/dist/testing/dom.d.ts +3 -3
  62. package/dist/testing/dom.js.map +1 -1
  63. package/dist/testing/flush.cjs +1 -1
  64. package/dist/testing/flush.cjs.map +1 -1
  65. package/dist/testing/flush.d.ts +4 -4
  66. package/dist/testing/flush.js +1 -1
  67. package/dist/testing/flush.js.map +1 -1
  68. package/dist/testing/form-internals-polyfill.cjs.map +1 -1
  69. package/dist/testing/form-internals-polyfill.d.ts +2 -2
  70. package/dist/testing/form-internals-polyfill.js.map +1 -1
  71. package/dist/testing/mount.cjs.map +1 -1
  72. package/dist/testing/mount.d.ts +3 -3
  73. package/dist/testing/mount.d.ts.map +1 -1
  74. package/dist/testing/mount.js.map +1 -1
  75. package/dist/testing/render-hook.cjs.map +1 -1
  76. package/dist/testing/render-hook.d.ts +1 -1
  77. package/dist/testing/render-hook.js.map +1 -1
  78. package/dist/testing/reset.cjs.map +1 -1
  79. package/dist/testing/reset.d.ts +2 -2
  80. package/dist/testing/reset.js.map +1 -1
  81. package/dist/testing.cjs.map +1 -1
  82. package/dist/testing.js.map +1 -1
  83. package/dist/utils/brand.cjs.map +1 -1
  84. package/dist/utils/brand.d.ts +2 -2
  85. package/dist/utils/brand.js.map +1 -1
  86. package/dist/utils/dom.cjs.map +1 -1
  87. package/dist/utils/dom.d.ts +5 -5
  88. package/dist/utils/dom.d.ts.map +1 -1
  89. package/dist/utils/dom.js.map +1 -1
  90. package/dist/utils/emit.cjs.map +1 -1
  91. package/dist/utils/emit.d.ts +4 -4
  92. package/dist/utils/emit.js.map +1 -1
  93. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"form-internals-polyfill.js","names":[],"sources":["../../src/testing/form-internals-polyfill.ts"],"sourcesContent":["import { walkFlatTree } from './dom';\n\n/**\n * jsdom implements none of the `ElementInternals` form-association API — not `setFormValue`,\n * not `setValidity`, not the `checkValidity`/`reportValidity`/`validity`/`validationMessage`\n * mixin real browsers put on every `formAssociated: true` element, not even `FormData`\n * collecting a form-associated element's set value, or `<form>.reset()` invoking\n * `formResetCallback()`. Every one of those gaps is exercised by `useField()` (`@vielzeug/ore`),\n * so any package testing a form-associated component needs all of them, not just one.\n *\n * Opt-in via `install(afterEach, { formInternals: true })` (or call this directly) —\n * the patches are global monkey-patches, so suites without form-associated components\n * shouldn't pay for them. Returns an `uninstall()` that restores every patched global.\n *\n * Deliberately lives here rather than in each consuming package's own `vitest.setup.ts`: the\n * gap is in `ore`'s own form-association feature, not in any one consumer, so the fix belongs\n * with the feature. `walkFlatTree` handles form-associated custom elements across shadow\n * boundaries, so consumer packages do not need package-local copies of this polyfill.\n */\nexport const installFormInternalsPolyfill = (): (() => void) => {\n // Checked *before* consuming the flag below — an environment without `ElementInternals`\n // installs nothing and should stay eligible to install for real later (e.g. a differently\n // configured environment in the same process), not get marked \"already installed\" for having\n // done nothing.\n if (typeof ElementInternals === 'undefined') return () => {};\n\n const INSTALLED_FLAG = Symbol.for('vielzeug.ore.testing.formInternalsPolyfillInstalled');\n const flagHost = globalThis as Record<symbol, boolean | undefined>;\n\n if (flagHost[INSTALLED_FLAG]) return () => {};\n\n flagHost[INSTALLED_FLAG] = true;\n\n // Every patch pushes its inverse here, so uninstall() restores the environment exactly.\n const restorers: Array<() => void> = [];\n\n const proto = ElementInternals.prototype as unknown as Record<string, unknown>;\n const validityState = new WeakMap<ElementInternals, { flags: ValidityStateFlags; message: string }>();\n const formValueByHost = new WeakMap<HTMLElement, File | FormData | string | null>();\n const hostByInternals = new WeakMap<ElementInternals, HTMLElement>();\n\n const patchProto = (name: string, descriptor: PropertyDescriptor): void => {\n if (name in proto) return;\n\n Object.defineProperty(proto, name, descriptor);\n restorers.push(() => delete proto[name]);\n };\n\n const isValid = (internals: ElementInternals): boolean => {\n const state = validityState.get(internals);\n\n return !state || !Object.values(state.flags).some(Boolean);\n };\n\n patchProto('setFormValue', {\n configurable: true,\n value: function (this: ElementInternals, value: File | FormData | string | null) {\n const host = hostByInternals.get(this);\n\n if (host) formValueByHost.set(host, value);\n },\n writable: true,\n });\n\n patchProto('setValidity', {\n configurable: true,\n // Matches the real platform contract: throws if any flag is true and message is empty.\n // `useField()` itself already guards against triggering this — see forms/field.ts — but\n // the polyfill enforcing it too means a test would still catch a *different* caller doing\n // the same thing wrong, instead of that bug only surfacing in a real browser.\n value: function (this: ElementInternals, flags: ValidityStateFlags = {}, message = '') {\n if (Object.values(flags).some(Boolean) && !message) {\n throw new TypeError(\n \"Failed to execute 'setValidity' on 'ElementInternals': The second argument must not be empty if \" +\n 'one or more flags in the first argument are true.',\n );\n }\n\n validityState.set(this, { flags, message });\n },\n writable: true,\n });\n\n patchProto('checkValidity', {\n configurable: true,\n value: function (this: ElementInternals) {\n return isValid(this);\n },\n writable: true,\n });\n\n patchProto('reportValidity', {\n configurable: true,\n value: function (this: ElementInternals) {\n return isValid(this);\n },\n writable: true,\n });\n\n patchProto('validationMessage', {\n configurable: true,\n get(this: ElementInternals) {\n return isValid(this) ? '' : (validityState.get(this)?.message ?? '');\n },\n });\n\n // A ValidityState-shaped view of the flags passed to setValidity — every known\n // flag defaults to false, `valid` reflects whether any flag is set.\n patchProto('validity', {\n configurable: true,\n get(this: ElementInternals): ValidityState {\n const state = validityState.get(this);\n\n return {\n badInput: false,\n customError: false,\n patternMismatch: false,\n rangeOverflow: false,\n rangeUnderflow: false,\n stepMismatch: false,\n tooLong: false,\n tooShort: false,\n typeMismatch: false,\n valid: isValid(this),\n valueMissing: false,\n ...(state?.flags ?? {}),\n } as ValidityState;\n },\n });\n\n patchProto('states', {\n configurable: true,\n get: function (this: ElementInternals & { _states?: Set<string> }) {\n this._states ??= new Set();\n\n return this._states;\n },\n });\n\n // Real browsers mix `checkValidity`/`reportValidity`/`validity`/`validationMessage` onto any\n // `formAssociated: true` custom element itself (delegating to its internals) — not just onto\n // `ElementInternals`. jsdom does neither; mirror it here so a component's own\n // `checkValidity()`/`reportValidity()`, and a test asserting against the element directly,\n // behave the same as they would in a real browser.\n const originalAttachInternals = HTMLElement.prototype.attachInternals;\n\n HTMLElement.prototype.attachInternals = function (this: HTMLElement, ...args: []): ElementInternals {\n const internals = originalAttachInternals.apply(this, args);\n\n hostByInternals.set(internals, this);\n\n Object.defineProperties(this, {\n checkValidity: { configurable: true, value: () => internals.checkValidity() },\n reportValidity: { configurable: true, value: () => internals.reportValidity() },\n validationMessage: { configurable: true, get: () => internals.validationMessage },\n validity: { configurable: true, get: () => internals.validity },\n });\n\n return internals;\n };\n restorers.push(() => {\n HTMLElement.prototype.attachInternals = originalAttachInternals;\n });\n\n const appendFormValue = (formData: FormData, name: string, value: File | FormData | string): void => {\n if (value instanceof FormData) {\n for (const [entryName, entryValue] of value.entries()) formData.append(entryName, entryValue);\n\n return;\n }\n\n formData.append(name, value);\n };\n\n // jsdom's `FormData` constructor never collects a form-associated custom element's set form\n // value — real browsers walk the flat tree for every form-associated element with a `name`.\n const NativeFormData = globalThis.FormData;\n\n globalThis.FormData = class FormDataWithFormAssociatedElements extends NativeFormData {\n constructor(form?: HTMLFormElement, submitter?: HTMLElement | null) {\n super(form as HTMLFormElement | undefined, submitter as HTMLElement | null | undefined);\n\n if (!(form instanceof HTMLFormElement)) return;\n\n walkFlatTree(form, (element) => {\n const name = element.getAttribute('name');\n const value = formValueByHost.get(element);\n\n if (!name || value == null || element.hasAttribute('disabled')) return;\n\n appendFormValue(this, name, value);\n });\n }\n };\n restorers.push(() => {\n globalThis.FormData = NativeFormData;\n });\n\n // jsdom never invokes the native `formResetCallback` lifecycle method on form-associated\n // custom elements when their form resets — patch `reset()` to do it (via the same flat-tree\n // walk, since a reset field can be nested behind a shadow boundary too), so `onFormReset()`\n // (see `@vielzeug/ore`'s runtime.ts) is testable against a real `<form>.reset()` call instead\n // of only by calling `element.formResetCallback()` directly.\n const originalReset = HTMLFormElement.prototype.reset;\n\n HTMLFormElement.prototype.reset = function (this: HTMLFormElement, ...args: []) {\n originalReset.apply(this, args);\n\n walkFlatTree(this, (element) => {\n (element as Partial<{ formResetCallback: () => void }>).formResetCallback?.();\n });\n };\n restorers.push(() => {\n HTMLFormElement.prototype.reset = originalReset;\n });\n\n return () => {\n for (const restore of restorers) restore();\n\n restorers.length = 0;\n flagHost[INSTALLED_FLAG] = false;\n };\n};\n"],"mappings":"wCAmBA,IAAa,MAAmD,CAK9D,GAAI,OAAO,iBAAqB,IAAa,UAAa,CAAC,EAE3D,IAAM,EAAiB,OAAO,IAAI,qDAAqD,EACjF,EAAW,WAEjB,GAAI,EAAS,GAAiB,UAAa,CAAC,EAE5C,EAAS,GAAkB,GAG3B,IAAM,EAA+B,CAAC,EAEhC,EAAQ,iBAAiB,UACzB,EAAgB,IAAI,QACpB,EAAkB,IAAI,QACtB,EAAkB,IAAI,QAEtB,GAAc,EAAc,IAAyC,CACrE,KAAQ,IAEZ,OAAO,eAAe,EAAO,EAAM,CAAU,EAC7C,EAAU,SAAW,OAAO,EAAM,EAAK,EACzC,EAEM,EAAW,GAAyC,CACxD,IAAM,EAAQ,EAAc,IAAI,CAAS,EAEzC,MAAO,CAAC,GAAS,CAAC,OAAO,OAAO,EAAM,KAAK,CAAC,CAAC,KAAK,OAAO,CAC3D,EAEA,EAAW,eAAgB,CACzB,aAAc,GACd,MAAO,SAAkC,EAAwC,CAC/E,IAAM,EAAO,EAAgB,IAAI,IAAI,EAEjC,GAAM,EAAgB,IAAI,EAAM,CAAK,CAC3C,EACA,SAAU,EACZ,CAAC,EAED,EAAW,cAAe,CACxB,aAAc,GAKd,MAAO,SAAkC,EAA4B,CAAC,EAAG,EAAU,GAAI,CACrF,GAAI,OAAO,OAAO,CAAK,CAAC,CAAC,KAAK,OAAO,GAAK,CAAC,EACzC,MAAU,UACR,mJAEF,EAGF,EAAc,IAAI,KAAM,CAAE,QAAO,SAAQ,CAAC,CAC5C,EACA,SAAU,EACZ,CAAC,EAED,EAAW,gBAAiB,CAC1B,aAAc,GACd,MAAO,UAAkC,CACvC,OAAO,EAAQ,IAAI,CACrB,EACA,SAAU,EACZ,CAAC,EAED,EAAW,iBAAkB,CAC3B,aAAc,GACd,MAAO,UAAkC,CACvC,OAAO,EAAQ,IAAI,CACrB,EACA,SAAU,EACZ,CAAC,EAED,EAAW,oBAAqB,CAC9B,aAAc,GACd,KAA4B,CAC1B,OAAO,EAAQ,IAAI,EAAI,GAAM,EAAc,IAAI,IAAI,CAAC,EAAE,SAAW,EACnE,CACF,CAAC,EAID,EAAW,WAAY,CACrB,aAAc,GACd,KAA2C,CACzC,IAAM,EAAQ,EAAc,IAAI,IAAI,EAEpC,MAAO,CACL,SAAU,GACV,YAAa,GACb,gBAAiB,GACjB,cAAe,GACf,eAAgB,GAChB,aAAc,GACd,QAAS,GACT,SAAU,GACV,aAAc,GACd,MAAO,EAAQ,IAAI,EACnB,aAAc,GACd,GAAI,GAAO,OAAS,CAAC,CACvB,CACF,CACF,CAAC,EAED,EAAW,SAAU,CACnB,aAAc,GACd,IAAK,UAA8D,CAGjE,MAFA,MAAK,UAAY,IAAI,IAEd,KAAK,OACd,CACF,CAAC,EAOD,IAAM,EAA0B,YAAY,UAAU,gBAEtD,YAAY,UAAU,gBAAkB,SAA6B,GAAG,EAA4B,CAClG,IAAM,EAAY,EAAwB,MAAM,KAAM,CAAI,EAW1D,OATA,EAAgB,IAAI,EAAW,IAAI,EAEnC,OAAO,iBAAiB,KAAM,CAC5B,cAAe,CAAE,aAAc,GAAM,UAAa,EAAU,cAAc,CAAE,EAC5E,eAAgB,CAAE,aAAc,GAAM,UAAa,EAAU,eAAe,CAAE,EAC9E,kBAAmB,CAAE,aAAc,GAAM,QAAW,EAAU,iBAAkB,EAChF,SAAU,CAAE,aAAc,GAAM,QAAW,EAAU,QAAS,CAChE,CAAC,EAEM,CACT,EACA,EAAU,SAAW,CACnB,YAAY,UAAU,gBAAkB,CAC1C,CAAC,EAED,IAAM,GAAmB,EAAoB,EAAc,IAA0C,CACnG,GAAI,aAAiB,SAAU,CAC7B,IAAK,GAAM,CAAC,EAAW,KAAe,EAAM,QAAQ,EAAG,EAAS,OAAO,EAAW,CAAU,EAE5F,MACF,CAEA,EAAS,OAAO,EAAM,CAAK,CAC7B,EAIM,EAAiB,WAAW,SAElC,WAAW,SAAW,cAAiD,CAAe,CACpF,YAAY,EAAwB,EAAgC,CAClE,MAAM,EAAqC,CAA2C,EAEhF,aAAgB,iBAEtB,EAAa,EAAO,GAAY,CAC9B,IAAM,EAAO,EAAQ,aAAa,MAAM,EAClC,EAAQ,EAAgB,IAAI,CAAO,EAEpC,GAAQ,GAAS,MAAQ,GAAQ,aAAa,UAAU,GAE7D,EAAgB,KAAM,EAAM,CAAK,CACnC,CAAC,CACH,CACF,EACA,EAAU,SAAW,CACnB,WAAW,SAAW,CACxB,CAAC,EAOD,IAAM,EAAgB,gBAAgB,UAAU,MAahD,MAXA,iBAAgB,UAAU,MAAQ,SAAiC,GAAG,EAAU,CAC9E,EAAc,MAAM,KAAM,CAAI,EAE9B,EAAa,KAAO,GAAY,CAC9B,EAAwD,oBAAoB,CAC9E,CAAC,CACH,EACA,EAAU,SAAW,CACnB,gBAAgB,UAAU,MAAQ,CACpC,CAAC,MAEY,CACX,IAAK,IAAM,KAAW,EAAW,EAAQ,EAEzC,EAAU,OAAS,EACnB,EAAS,GAAkB,EAC7B,CACF"}
1
+ {"version":3,"file":"form-internals-polyfill.js","names":[],"sources":["../../src/testing/form-internals-polyfill.ts"],"sourcesContent":["import { walkFlatTree } from './dom';\n\n/**\n * jsdom implements none of the `ElementInternals` form-association API: not `setFormValue`,\n * not `setValidity`, not the `checkValidity`/`reportValidity`/`validity`/`validationMessage`\n * mixin real browsers put on every `formAssociated: true` element, not even `FormData`\n * collecting a form-associated element's set value, or `<form>.reset()` invoking\n * `formResetCallback()`. Every one of those gaps is exercised by `useField()` (`@vielzeug/ore`),\n * so any package testing a form-associated component needs all of them, not just one.\n *\n * Opt-in via `install(afterEach, { formInternals: true })` (or call this directly) :\n * the patches are global monkey-patches, so suites without form-associated components\n * shouldn't pay for them. Returns an `uninstall()` that restores every patched global.\n *\n * Deliberately lives here rather than in each consuming package's own `vitest.setup.ts`: the\n * gap is in `ore`'s own form-association feature, not in any one consumer, so the fix belongs\n * with the feature. `walkFlatTree` handles form-associated custom elements across shadow\n * boundaries, so consumer packages do not need package-local copies of this polyfill.\n */\nexport const installFormInternalsPolyfill = (): (() => void) => {\n // Checked *before* consuming the flag below: an environment without `ElementInternals`\n // installs nothing and should stay eligible to install for real later (e.g. a differently\n // configured environment in the same process), not get marked \"already installed\" for having\n // done nothing.\n if (typeof ElementInternals === 'undefined') return () => {};\n\n const INSTALLED_FLAG = Symbol.for('vielzeug.ore.testing.formInternalsPolyfillInstalled');\n const flagHost = globalThis as Record<symbol, boolean | undefined>;\n\n if (flagHost[INSTALLED_FLAG]) return () => {};\n\n flagHost[INSTALLED_FLAG] = true;\n\n // Every patch pushes its inverse here, so uninstall() restores the environment exactly.\n const restorers: Array<() => void> = [];\n\n const proto = ElementInternals.prototype as unknown as Record<string, unknown>;\n const validityState = new WeakMap<ElementInternals, { flags: ValidityStateFlags; message: string }>();\n const formValueByHost = new WeakMap<HTMLElement, File | FormData | string | null>();\n const hostByInternals = new WeakMap<ElementInternals, HTMLElement>();\n\n const patchProto = (name: string, descriptor: PropertyDescriptor): void => {\n if (name in proto) return;\n\n Object.defineProperty(proto, name, descriptor);\n restorers.push(() => delete proto[name]);\n };\n\n const isValid = (internals: ElementInternals): boolean => {\n const state = validityState.get(internals);\n\n return !state || !Object.values(state.flags).some(Boolean);\n };\n\n patchProto('setFormValue', {\n configurable: true,\n value: function (this: ElementInternals, value: File | FormData | string | null) {\n const host = hostByInternals.get(this);\n\n if (host) formValueByHost.set(host, value);\n },\n writable: true,\n });\n\n patchProto('setValidity', {\n configurable: true,\n // Matches the real platform contract: throws if any flag is true and message is empty.\n // `useField()` itself already guards against triggering this: see forms/field.ts, but\n // the polyfill enforcing it too means a test would still catch a *different* caller doing\n // the same thing wrong, instead of that bug only surfacing in a real browser.\n value: function (this: ElementInternals, flags: ValidityStateFlags = {}, message = '') {\n if (Object.values(flags).some(Boolean) && !message) {\n throw new TypeError(\n \"Failed to execute 'setValidity' on 'ElementInternals': The second argument must not be empty if \" +\n 'one or more flags in the first argument are true.',\n );\n }\n\n validityState.set(this, { flags, message });\n },\n writable: true,\n });\n\n patchProto('checkValidity', {\n configurable: true,\n value: function (this: ElementInternals) {\n return isValid(this);\n },\n writable: true,\n });\n\n patchProto('reportValidity', {\n configurable: true,\n value: function (this: ElementInternals) {\n return isValid(this);\n },\n writable: true,\n });\n\n patchProto('validationMessage', {\n configurable: true,\n get(this: ElementInternals) {\n return isValid(this) ? '' : (validityState.get(this)?.message ?? '');\n },\n });\n\n // A ValidityState-shaped view of the flags passed to setValidity: every known\n // flag defaults to false, `valid` reflects whether any flag is set.\n patchProto('validity', {\n configurable: true,\n get(this: ElementInternals): ValidityState {\n const state = validityState.get(this);\n\n return {\n badInput: false,\n customError: false,\n patternMismatch: false,\n rangeOverflow: false,\n rangeUnderflow: false,\n stepMismatch: false,\n tooLong: false,\n tooShort: false,\n typeMismatch: false,\n valid: isValid(this),\n valueMissing: false,\n ...(state?.flags ?? {}),\n } as ValidityState;\n },\n });\n\n patchProto('states', {\n configurable: true,\n get: function (this: ElementInternals & { _states?: Set<string> }) {\n this._states ??= new Set();\n\n return this._states;\n },\n });\n\n // Real browsers mix `checkValidity`/`reportValidity`/`validity`/`validationMessage` onto any\n // `formAssociated: true` custom element itself (delegating to its internals): not just onto\n // `ElementInternals`. jsdom does neither; mirror it here so a component's own\n // `checkValidity()`/`reportValidity()`, and a test asserting against the element directly,\n // behave the same as they would in a real browser.\n const originalAttachInternals = HTMLElement.prototype.attachInternals;\n\n HTMLElement.prototype.attachInternals = function (this: HTMLElement, ...args: []): ElementInternals {\n const internals = originalAttachInternals.apply(this, args);\n\n hostByInternals.set(internals, this);\n\n Object.defineProperties(this, {\n checkValidity: { configurable: true, value: () => internals.checkValidity() },\n reportValidity: { configurable: true, value: () => internals.reportValidity() },\n validationMessage: { configurable: true, get: () => internals.validationMessage },\n validity: { configurable: true, get: () => internals.validity },\n });\n\n return internals;\n };\n restorers.push(() => {\n HTMLElement.prototype.attachInternals = originalAttachInternals;\n });\n\n const appendFormValue = (formData: FormData, name: string, value: File | FormData | string): void => {\n if (value instanceof FormData) {\n for (const [entryName, entryValue] of value.entries()) formData.append(entryName, entryValue);\n\n return;\n }\n\n formData.append(name, value);\n };\n\n // jsdom's `FormData` constructor never collects a form-associated custom element's set form\n // value: real browsers walk the flat tree for every form-associated element with a `name`.\n const NativeFormData = globalThis.FormData;\n\n globalThis.FormData = class FormDataWithFormAssociatedElements extends NativeFormData {\n constructor(form?: HTMLFormElement, submitter?: HTMLElement | null) {\n super(form as HTMLFormElement | undefined, submitter as HTMLElement | null | undefined);\n\n if (!(form instanceof HTMLFormElement)) return;\n\n walkFlatTree(form, (element) => {\n const name = element.getAttribute('name');\n const value = formValueByHost.get(element);\n\n if (!name || value == null || element.hasAttribute('disabled')) return;\n\n appendFormValue(this, name, value);\n });\n }\n };\n restorers.push(() => {\n globalThis.FormData = NativeFormData;\n });\n\n // jsdom never invokes the native `formResetCallback` lifecycle method on form-associated\n // custom elements when their form resets: patch `reset()` to do it (via the same flat-tree\n // walk, since a reset field can be nested behind a shadow boundary too), so `onFormReset()`\n // (see `@vielzeug/ore`'s runtime.ts) is testable against a real `<form>.reset()` call instead\n // of only by calling `element.formResetCallback()` directly.\n const originalReset = HTMLFormElement.prototype.reset;\n\n HTMLFormElement.prototype.reset = function (this: HTMLFormElement, ...args: []) {\n originalReset.apply(this, args);\n\n walkFlatTree(this, (element) => {\n (element as Partial<{ formResetCallback: () => void }>).formResetCallback?.();\n });\n };\n restorers.push(() => {\n HTMLFormElement.prototype.reset = originalReset;\n });\n\n return () => {\n for (const restore of restorers) restore();\n\n restorers.length = 0;\n flagHost[INSTALLED_FLAG] = false;\n };\n};\n"],"mappings":"wCAmBA,IAAa,MAAmD,CAK9D,GAAI,OAAO,iBAAqB,IAAa,UAAa,CAAC,EAE3D,IAAM,EAAiB,OAAO,IAAI,qDAAqD,EACjF,EAAW,WAEjB,GAAI,EAAS,GAAiB,UAAa,CAAC,EAE5C,EAAS,GAAkB,GAG3B,IAAM,EAA+B,CAAC,EAEhC,EAAQ,iBAAiB,UACzB,EAAgB,IAAI,QACpB,EAAkB,IAAI,QACtB,EAAkB,IAAI,QAEtB,GAAc,EAAc,IAAyC,CACrE,KAAQ,IAEZ,OAAO,eAAe,EAAO,EAAM,CAAU,EAC7C,EAAU,SAAW,OAAO,EAAM,EAAK,EACzC,EAEM,EAAW,GAAyC,CACxD,IAAM,EAAQ,EAAc,IAAI,CAAS,EAEzC,MAAO,CAAC,GAAS,CAAC,OAAO,OAAO,EAAM,KAAK,CAAC,CAAC,KAAK,OAAO,CAC3D,EAEA,EAAW,eAAgB,CACzB,aAAc,GACd,MAAO,SAAkC,EAAwC,CAC/E,IAAM,EAAO,EAAgB,IAAI,IAAI,EAEjC,GAAM,EAAgB,IAAI,EAAM,CAAK,CAC3C,EACA,SAAU,EACZ,CAAC,EAED,EAAW,cAAe,CACxB,aAAc,GAKd,MAAO,SAAkC,EAA4B,CAAC,EAAG,EAAU,GAAI,CACrF,GAAI,OAAO,OAAO,CAAK,CAAC,CAAC,KAAK,OAAO,GAAK,CAAC,EACzC,MAAU,UACR,mJAEF,EAGF,EAAc,IAAI,KAAM,CAAE,QAAO,SAAQ,CAAC,CAC5C,EACA,SAAU,EACZ,CAAC,EAED,EAAW,gBAAiB,CAC1B,aAAc,GACd,MAAO,UAAkC,CACvC,OAAO,EAAQ,IAAI,CACrB,EACA,SAAU,EACZ,CAAC,EAED,EAAW,iBAAkB,CAC3B,aAAc,GACd,MAAO,UAAkC,CACvC,OAAO,EAAQ,IAAI,CACrB,EACA,SAAU,EACZ,CAAC,EAED,EAAW,oBAAqB,CAC9B,aAAc,GACd,KAA4B,CAC1B,OAAO,EAAQ,IAAI,EAAI,GAAM,EAAc,IAAI,IAAI,CAAC,EAAE,SAAW,EACnE,CACF,CAAC,EAID,EAAW,WAAY,CACrB,aAAc,GACd,KAA2C,CACzC,IAAM,EAAQ,EAAc,IAAI,IAAI,EAEpC,MAAO,CACL,SAAU,GACV,YAAa,GACb,gBAAiB,GACjB,cAAe,GACf,eAAgB,GAChB,aAAc,GACd,QAAS,GACT,SAAU,GACV,aAAc,GACd,MAAO,EAAQ,IAAI,EACnB,aAAc,GACd,GAAI,GAAO,OAAS,CAAC,CACvB,CACF,CACF,CAAC,EAED,EAAW,SAAU,CACnB,aAAc,GACd,IAAK,UAA8D,CAGjE,MAFA,MAAK,UAAY,IAAI,IAEd,KAAK,OACd,CACF,CAAC,EAOD,IAAM,EAA0B,YAAY,UAAU,gBAEtD,YAAY,UAAU,gBAAkB,SAA6B,GAAG,EAA4B,CAClG,IAAM,EAAY,EAAwB,MAAM,KAAM,CAAI,EAW1D,OATA,EAAgB,IAAI,EAAW,IAAI,EAEnC,OAAO,iBAAiB,KAAM,CAC5B,cAAe,CAAE,aAAc,GAAM,UAAa,EAAU,cAAc,CAAE,EAC5E,eAAgB,CAAE,aAAc,GAAM,UAAa,EAAU,eAAe,CAAE,EAC9E,kBAAmB,CAAE,aAAc,GAAM,QAAW,EAAU,iBAAkB,EAChF,SAAU,CAAE,aAAc,GAAM,QAAW,EAAU,QAAS,CAChE,CAAC,EAEM,CACT,EACA,EAAU,SAAW,CACnB,YAAY,UAAU,gBAAkB,CAC1C,CAAC,EAED,IAAM,GAAmB,EAAoB,EAAc,IAA0C,CACnG,GAAI,aAAiB,SAAU,CAC7B,IAAK,GAAM,CAAC,EAAW,KAAe,EAAM,QAAQ,EAAG,EAAS,OAAO,EAAW,CAAU,EAE5F,MACF,CAEA,EAAS,OAAO,EAAM,CAAK,CAC7B,EAIM,EAAiB,WAAW,SAElC,WAAW,SAAW,cAAiD,CAAe,CACpF,YAAY,EAAwB,EAAgC,CAClE,MAAM,EAAqC,CAA2C,EAEhF,aAAgB,iBAEtB,EAAa,EAAO,GAAY,CAC9B,IAAM,EAAO,EAAQ,aAAa,MAAM,EAClC,EAAQ,EAAgB,IAAI,CAAO,EAEpC,GAAQ,GAAS,MAAQ,GAAQ,aAAa,UAAU,GAE7D,EAAgB,KAAM,EAAM,CAAK,CACnC,CAAC,CACH,CACF,EACA,EAAU,SAAW,CACnB,WAAW,SAAW,CACxB,CAAC,EAOD,IAAM,EAAgB,gBAAgB,UAAU,MAahD,MAXA,iBAAgB,UAAU,MAAQ,SAAiC,GAAG,EAAU,CAC9E,EAAc,MAAM,KAAM,CAAI,EAE9B,EAAa,KAAO,GAAY,CAC9B,EAAwD,oBAAoB,CAC9E,CAAC,CACH,EACA,EAAU,SAAW,CACnB,gBAAgB,UAAU,MAAQ,CACpC,CAAC,MAEY,CACX,IAAK,IAAM,KAAW,EAAW,EAAQ,EAEzC,EAAU,OAAS,EACnB,EAAS,GAAkB,EAC7B,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"mount.cjs","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run — never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"iLAkEA,IAAa,EAAkC,CAAC,EAK5C,EAAuB,EAI3B,SAAS,EAAU,EAAkB,EAAc,EAAwC,CAGzF,EAAA,QAAQ,EAAS,EAAM,CAAK,CAC9B,CAEA,IAAM,EAAW,GACR,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAG3D,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAO,EAAO,EAEjD,IAAI,EAAyB,KACvB,EAAW,GAAsB,CACrC,EAAW,EAAQ,EAAM,OAAS,EAAM,OAAO,EAC/C,EAAM,eAAe,CACvB,EACM,EAAwB,GAAiC,CAC7D,EAAW,EAAQ,EAAM,MAAM,EAC/B,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAAS,CAAO,EACxC,OAAO,iBAAiB,qBAAsB,CAAoB,EAElE,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAE5B,GAAI,EAAU,MAAM,EAEpB,OAAO,CACT,QAAU,CACR,OAAO,oBAAoB,QAAS,CAAO,EAC3C,OAAO,oBAAoB,qBAAsB,CAAoB,CACvE,CACF,EA2BA,eAAsB,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAM,EAElF,EACA,EAEA,OAAO,GAAe,SACxB,EAAU,GAEV,EAAU,SAAS,EAAE,IACrB,EAAmB,CACjB,GAAI,GAAoB,CAAC,EACzB,MAAO,CACT,GAGE,GACF,EAAA,OAAO,EAAS,CAAgB,EAGlC,IAAM,EAAU,SAAS,cAAc,CAAO,EAE1C,IAAM,EAAQ,UAAY,GAE1B,OAAO,KAAK,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAO,EAAS,CAAK,EAE3D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAK,EAAG,EAAU,EAAS,EAAM,CAAK,EAEjF,GAAI,CACF,MAAM,EAAuB,SAAY,CACvC,EAAU,YAAY,CAAO,EAC7B,EAAiB,KAAK,CAAO,EAC7B,MAAM,EAAA,MAAM,CACd,CAAC,CACH,OAAS,EAAK,CACZ,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAI1C,MAFI,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,EAEpC,CACR,CAEA,IAAI,EAAa,GAEjB,SAAS,GAAU,CACjB,GAAI,EAAY,OAEhB,EAAa,GACb,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAEtC,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,IAAA,EAHY,EAAA,OAAA,CAAQ,EAAQ,YAAc,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM,EAAG,EACT,MAAM,EAAA,MAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtB,EAAU,EAAS,EAAM,CAAK,EAC9B,MAAM,EAAA,MAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,EAAU,EAAS,EAAM,CAAK,EAClF,MAAM,EAAA,MAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,UAEA,MAAA,EAAA,MAEA,IAAI,QAA4B,CAC9B,OAAO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1B,EAAA,oBAAoB,EACpB,EAAA,iBAAiB,CACnB"}
1
+ {"version":3,"file":"mount.cjs","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"iLAkEA,IAAa,EAAkC,CAAC,EAK5C,EAAuB,EAI3B,SAAS,EAAU,EAAkB,EAAc,EAAwC,CAGzF,EAAA,QAAQ,EAAS,EAAM,CAAK,CAC9B,CAEA,IAAM,EAAW,GACR,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAG3D,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAO,EAAO,EAEjD,IAAI,EAAyB,KACvB,EAAW,GAAsB,CACrC,EAAW,EAAQ,EAAM,OAAS,EAAM,OAAO,EAC/C,EAAM,eAAe,CACvB,EACM,EAAwB,GAAiC,CAC7D,EAAW,EAAQ,EAAM,MAAM,EAC/B,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAAS,CAAO,EACxC,OAAO,iBAAiB,qBAAsB,CAAoB,EAElE,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAE5B,GAAI,EAAU,MAAM,EAEpB,OAAO,CACT,QAAU,CACR,OAAO,oBAAoB,QAAS,CAAO,EAC3C,OAAO,oBAAoB,qBAAsB,CAAoB,CACvE,CACF,EA2BA,eAAsB,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAM,EAElF,EACA,EAEA,OAAO,GAAe,SACxB,EAAU,GAEV,EAAU,SAAS,EAAE,IACrB,EAAmB,CACjB,GAAI,GAAoB,CAAC,EACzB,MAAO,CACT,GAGE,GACF,EAAA,OAAO,EAAS,CAAgB,EAGlC,IAAM,EAAU,SAAS,cAAc,CAAO,EAE1C,IAAM,EAAQ,UAAY,GAE1B,OAAO,KAAK,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAO,EAAS,CAAK,EAE3D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAK,EAAG,EAAU,EAAS,EAAM,CAAK,EAEjF,GAAI,CACF,MAAM,EAAuB,SAAY,CACvC,EAAU,YAAY,CAAO,EAC7B,EAAiB,KAAK,CAAO,EAC7B,MAAM,EAAA,MAAM,CACd,CAAC,CACH,OAAS,EAAK,CACZ,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAI1C,MAFI,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,EAEpC,CACR,CAEA,IAAI,EAAa,GAEjB,SAAS,GAAU,CACjB,GAAI,EAAY,OAEhB,EAAa,GACb,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAEtC,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,IAAA,EAHY,EAAA,OAAA,CAAQ,EAAQ,YAAc,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM,EAAG,EACT,MAAM,EAAA,MAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtB,EAAU,EAAS,EAAM,CAAK,EAC9B,MAAM,EAAA,MAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,EAAU,EAAS,EAAM,CAAK,EAClF,MAAM,EAAA,MAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,UAEA,MAAA,EAAA,MAEA,IAAI,QAA4B,CAC9B,OAAO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1B,EAAA,oBAAoB,EACpB,EAAA,iBAAiB,CACnB"}
@@ -11,7 +11,7 @@ import { type FlushOptions } from './flush';
11
11
  * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).
12
12
  */
13
13
  export interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {
14
- /** Run a callback then flush — the standard way to trigger and assert a reactive update */
14
+ /** Run a callback then flush: the standard way to trigger and assert a reactive update */
15
15
  act(fn: () => unknown): Promise<void>;
16
16
  /** Set an attribute (boolean `false` removes it) then flush */
17
17
  attr(name: string, value: string | number | boolean): Promise<void>;
@@ -55,13 +55,13 @@ export declare const _mountedElements: HTMLElement[];
55
55
  * Accepts a registered tag name, an inline setup function, or a component
56
56
  * options object. Setup functions are auto-registered with generated tag names.
57
57
  *
58
- * @example — inline setup function
58
+ * @example: inline setup function
59
59
  * const { query } = await mount(() => {
60
60
  * const count = signal(0);
61
61
  * return html`<button @click=${() => count.value++}>${count}</button>`;
62
62
  * });
63
63
  *
64
- * @example — registered tag name
64
+ * @example: registered tag name
65
65
  * const { query } = await mount('my-counter');
66
66
  */
67
67
  export declare function mount<T extends HTMLElement = HTMLElement>(tagOrSetup: string, options?: MountOptions): Promise<Fixture<T>>;
@@ -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,2FAA2F;IAC3F,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;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 +1 @@
1
- {"version":3,"file":"mount.js","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush — the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run — never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example — inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example — registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"iQAkEA,IAAa,EAAkC,CAAC,EAK5C,EAAuB,EAI3B,SAAS,EAAU,EAAkB,EAAc,EAAwC,CAGzF,EAAQ,EAAS,EAAM,CAAK,CAC9B,CAEA,IAAM,EAAW,GACR,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAG3D,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAO,EAAO,EAEjD,IAAI,EAAyB,KACvB,EAAW,GAAsB,CACrC,EAAW,EAAQ,EAAM,OAAS,EAAM,OAAO,EAC/C,EAAM,eAAe,CACvB,EACM,EAAwB,GAAiC,CAC7D,EAAW,EAAQ,EAAM,MAAM,EAC/B,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAAS,CAAO,EACxC,OAAO,iBAAiB,qBAAsB,CAAoB,EAElE,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAE5B,GAAI,EAAU,MAAM,EAEpB,OAAO,CACT,QAAU,CACR,OAAO,oBAAoB,QAAS,CAAO,EAC3C,OAAO,oBAAoB,qBAAsB,CAAoB,CACvE,CACF,EA2BA,eAAsB,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAM,EAElF,EACA,EAEA,OAAO,GAAe,SACxB,EAAU,GAEV,EAAU,SAAS,EAAE,IACrB,EAAmB,CACjB,GAAI,GAAoB,CAAC,EACzB,MAAO,CACT,GAGE,GACF,EAAO,EAAS,CAAgB,EAGlC,IAAM,EAAU,SAAS,cAAc,CAAO,EAE1C,IAAM,EAAQ,UAAY,GAE1B,OAAO,KAAK,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAO,EAAS,CAAK,EAE3D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAK,EAAG,EAAU,EAAS,EAAM,CAAK,EAEjF,GAAI,CACF,MAAM,EAAuB,SAAY,CACvC,EAAU,YAAY,CAAO,EAC7B,EAAiB,KAAK,CAAO,EAC7B,MAAM,EAAM,CACd,CAAC,CACH,OAAS,EAAK,CACZ,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAI1C,MAFI,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,EAEpC,CACR,CAEA,IAAI,EAAa,GAEjB,SAAS,GAAU,CACjB,GAAI,EAAY,OAEhB,EAAa,GACb,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAEtC,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,GAHY,EAAQ,EAAQ,YAAc,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM,EAAG,EACT,MAAM,EAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtB,EAAU,EAAS,EAAM,CAAK,EAC9B,MAAM,EAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,EAAU,EAAS,EAAM,CAAK,EAClF,MAAM,EAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,UAEA,QAEA,IAAI,QAA4B,CAC9B,OAAO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1B,EAAoB,EACpB,EAAiB,CACnB"}
1
+ {"version":3,"file":"mount.js","names":[],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"iQAkEA,IAAa,EAAkC,CAAC,EAK5C,EAAuB,EAI3B,SAAS,EAAU,EAAkB,EAAc,EAAwC,CAGzF,EAAQ,EAAS,EAAM,CAAK,CAC9B,CAEA,IAAM,EAAW,GACR,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAG3D,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAO,EAAO,EAEjD,IAAI,EAAyB,KACvB,EAAW,GAAsB,CACrC,EAAW,EAAQ,EAAM,OAAS,EAAM,OAAO,EAC/C,EAAM,eAAe,CACvB,EACM,EAAwB,GAAiC,CAC7D,EAAW,EAAQ,EAAM,MAAM,EAC/B,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAAS,CAAO,EACxC,OAAO,iBAAiB,qBAAsB,CAAoB,EAElE,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAE5B,GAAI,EAAU,MAAM,EAEpB,OAAO,CACT,QAAU,CACR,OAAO,oBAAoB,QAAS,CAAO,EAC3C,OAAO,oBAAoB,qBAAsB,CAAoB,CACvE,CACF,EA2BA,eAAsB,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAM,EAElF,EACA,EAEA,OAAO,GAAe,SACxB,EAAU,GAEV,EAAU,SAAS,EAAE,IACrB,EAAmB,CACjB,GAAI,GAAoB,CAAC,EACzB,MAAO,CACT,GAGE,GACF,EAAO,EAAS,CAAgB,EAGlC,IAAM,EAAU,SAAS,cAAc,CAAO,EAE1C,IAAM,EAAQ,UAAY,GAE1B,OAAO,KAAK,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAO,EAAS,CAAK,EAE3D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAK,EAAG,EAAU,EAAS,EAAM,CAAK,EAEjF,GAAI,CACF,MAAM,EAAuB,SAAY,CACvC,EAAU,YAAY,CAAO,EAC7B,EAAiB,KAAK,CAAO,EAC7B,MAAM,EAAM,CACd,CAAC,CACH,OAAS,EAAK,CACZ,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAI1C,MAFI,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,EAEpC,CACR,CAEA,IAAI,EAAa,GAEjB,SAAS,GAAU,CACjB,GAAI,EAAY,OAEhB,EAAa,GACb,EAAQ,OAAO,EAEf,IAAM,EAAI,EAAiB,QAAQ,CAAO,EAEtC,IAAM,IAAI,EAAiB,OAAO,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,GAHY,EAAQ,EAAQ,YAAc,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM,EAAG,EACT,MAAM,EAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtB,EAAU,EAAS,EAAM,CAAK,EAC9B,MAAM,EAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,EAAU,EAAS,EAAM,CAAK,EAClF,MAAM,EAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,UAEA,QAEA,IAAI,QAA4B,CAC9B,OAAO,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF,CASA,SAAgB,GAAgB,CAC9B,IAAK,IAAM,KAAM,EAAkB,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1B,EAAoB,EACpB,EAAiB,CACnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.cjs","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired — renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"kJAyBA,IAAM,EAAgB,IAAI,IAGb,MAAkC,CAC7C,IAAK,IAAM,IAAW,CAAC,GAAG,CAAa,EAAG,EAAQ,CACpD,EA+BA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,GAAA,EAAY,EAAA,YAAA,CAAY,EAExB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAMhC,IAAM,EAAM,EAAA,qBAAqB,CAAM,EACjC,CAAE,kBAAmB,EAEvB,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAQ,EAAW,EAAA,YAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,YAAY,EAAA,UAAU,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAAS,EAAO,CAKd,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEpE,EAAA,mBACE,IAAI,EAAA,kBAAkB,qCAAsC,CAC1D,MAAO,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAA,MAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EACd,EAAc,OAAO,CAAO,EAC9B,CAIA,OAFA,EAAc,IAAI,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAA,MAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
1
+ {"version":3,"file":"render-hook.cjs","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"kJAyBA,IAAM,EAAgB,IAAI,IAGb,MAAkC,CAC7C,IAAK,IAAM,IAAW,CAAC,GAAG,CAAa,EAAG,EAAQ,CACpD,EA+BA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,GAAA,EAAY,EAAA,YAAA,CAAY,EAExB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAMhC,IAAM,EAAM,EAAA,qBAAqB,CAAM,EACjC,CAAE,kBAAmB,EAEvB,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAQ,EAAW,EAAA,YAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,YAAY,EAAA,UAAU,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAAS,EAAO,CAKd,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEpE,EAAA,mBACE,IAAI,EAAA,kBAAkB,qCAAsC,CAC1D,MAAO,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAA,MAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EACd,EAAc,OAAO,CAAO,EAC9B,CAIA,OAFA,EAAc,IAAI,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAA,MAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation
2
+ * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation
3
3
  * without rendering a full component template.
4
4
  */
5
5
  import { type InferProps, type PropInputDefs } from '../props';
@@ -1 +1 @@
1
- {"version":3,"file":"render-hook.js","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired — renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"6RAyBA,IAAM,EAAgB,IAAI,IAGb,MAAkC,CAC7C,IAAK,IAAM,IAAW,CAAC,GAAG,CAAa,EAAG,EAAQ,CACpD,EA+BA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,EAAY,EAAY,EAExB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAMhC,IAAM,EAAM,EAAqB,CAAM,EACjC,CAAE,kBAAmB,EAEvB,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAQ,EAAW,EAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,YAAY,EAAU,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAAS,EAAO,CAKd,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEpE,EACE,IAAI,EAAkB,qCAAsC,CAC1D,MAAO,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EACd,EAAc,OAAO,CAAO,EAC9B,CAIA,OAFA,EAAc,IAAI,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
1
+ {"version":3,"file":"render-hook.js","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n /** Teardown the hook (runs cleanups). Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n * { label: prop.string('hello') },\n * (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n const hostScope = createScope();\n // Minimal host element so getHost() works inside setup\n const hostEl = document.createElement('div');\n\n document.body.appendChild(hostEl);\n\n // Built via the runtime's own factory, so the harness can never desync from a\n // new required RuntimeContext field. Note: `onFormReset()` registrations land\n // in this context's formResetCallbacks but are never fired: renderHook has no\n // ancestor <form>; test form-reset behaviour through mount() instead.\n const ctx = createRuntimeContext(hostEl);\n const { mountCallbacks } = ctx;\n\n let result!: T;\n\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n result = setup(props as InferProps<D>);\n });\n });\n\n // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n for (const cb of mountCallbacks) {\n try {\n hostScope.run(() => {\n runWithContext(ctx, () => {\n const cleanup = cb();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n } catch (error) {\n // Mirror BaseElement's real onMounted error handling (see\n // base-element.ts's `_scheduleMountCallbacks`): report the error\n // (console + `ore:error` event on the host) and keep running the\n // remaining callbacks, instead of hiding the failure entirely.\n const err = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`renderHook() mount callback failed`, {\n cause: err,\n component: 'renderHook',\n phase: 'mounted',\n }),\n hostEl,\n );\n }\n }\n } catch (err) {\n hostScope.dispose();\n hostEl.remove();\n throw err;\n }\n\n await flush();\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n hostScope.dispose();\n hostEl.remove();\n hookDisposers.delete(dispose);\n }\n\n hookDisposers.add(dispose);\n\n return {\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n async flush() {\n await flush();\n },\n\n result,\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n"],"mappings":"6RAyBA,IAAM,EAAgB,IAAI,IAGb,MAAkC,CAC7C,IAAK,IAAM,IAAW,CAAC,GAAG,CAAa,EAAG,EAAQ,CACpD,EA+BA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,EAAY,EAAY,EAExB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAMhC,IAAM,EAAM,EAAqB,CAAM,EACjC,CAAE,kBAAmB,EAEvB,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAQ,EAAW,EAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,YAAY,EAAU,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAAS,EAAO,CAKd,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEpE,EACE,IAAI,EAAkB,qCAAsC,CAC1D,MAAO,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EACd,EAAc,OAAO,CAAO,EAC9B,CAIA,OAFA,EAAc,IAAI,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"reset.cjs","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules — those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()` — call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"iEAcA,IAAa,MAA+B,CAC1C,EAAA,sBAAsB,EACtB,EAAA,gBAAgB,CAClB"}
1
+ {"version":3,"file":"reset.cjs","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules: those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()`: call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"iEAcA,IAAa,MAA+B,CAC1C,EAAA,sBAAsB,EACtB,EAAA,gBAAgB,CAClB"}
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * One honest entry point for cross-test isolation. Replaces the collection of
3
3
  * `_`-private reset functions the test harness used to import from individual
4
- * modules — those stay private to their modules; this is the supported seam.
4
+ * modules: those stay private to their modules; this is the supported seam.
5
5
  */
6
6
  /**
7
7
  * Reset every piece of global ore state a test can touch: the stylesheet cache
8
- * and the ID counter. Called by `cleanup()` — call it directly only if you
8
+ * and the ID counter. Called by `cleanup()`: call it directly only if you
9
9
  * manage mounted elements yourself.
10
10
  */
11
11
  export declare const resetOreForTests: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"reset.js","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules — those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()` — call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"yGAcA,IAAa,MAA+B,CAC1C,EAAsB,EACtB,EAAgB,CAClB"}
1
+ {"version":3,"file":"reset.js","names":[],"sources":["../../src/testing/reset.ts"],"sourcesContent":["/**\n * One honest entry point for cross-test isolation. Replaces the collection of\n * `_`-private reset functions the test harness used to import from individual\n * modules: those stay private to their modules; this is the supported seam.\n */\n\nimport { _clearStylesheetCache } from '../utils/css';\nimport { _resetIdCounter } from '../utils/id';\n\n/**\n * Reset every piece of global ore state a test can touch: the stylesheet cache\n * and the ID counter. Called by `cleanup()`: call it directly only if you\n * manage mounted elements yourself.\n */\nexport const resetOreForTests = (): void => {\n _clearStylesheetCache();\n _resetIdCounter();\n};\n"],"mappings":"yGAcA,IAAa,MAA+B,CAC1C,EAAsB,EACtB,EAAgB,CAClB"}
@@ -1 +1 @@
1
- {"version":3,"file":"testing.cjs","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout —\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API.\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\nexport type InstallOptions = {\n /**\n * Polyfill the jsdom/happy-dom gaps in `ElementInternals`/`FormData`/`<form>.reset()`\n * that `useField()` needs (see `installFormInternalsPolyfill`). Global monkey-patches,\n * so opt in only when the suite tests form-associated components.\n * @default false\n */\n formInternals?: boolean;\n};\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n * Pass `{ formInternals: true }` when testing form-associated (`useField`) components.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach, { formInternals: true });\n */\nexport function install(afterEachHook: (fn: () => void) => void, options: InstallOptions = {}): void {\n afterEachHook(cleanup);\n\n if (options.formInternals) installFormInternalsPolyfill();\n}\n"],"mappings":"qOAwCA,SAAgB,EAAQ,EAAyC,EAA0B,CAAC,EAAS,CACnG,EAAc,EAAA,OAAO,EAEjB,EAAQ,eAAe,EAAA,6BAA6B,CAC1D"}
1
+ {"version":3,"file":"testing.cjs","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API.\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\nexport type InstallOptions = {\n /**\n * Polyfill the jsdom/happy-dom gaps in `ElementInternals`/`FormData`/`<form>.reset()`\n * that `useField()` needs (see `installFormInternalsPolyfill`). Global monkey-patches,\n * so opt in only when the suite tests form-associated components.\n * @default false\n */\n formInternals?: boolean;\n};\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n * Pass `{ formInternals: true }` when testing form-associated (`useField`) components.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach, { formInternals: true });\n */\nexport function install(afterEachHook: (fn: () => void) => void, options: InstallOptions = {}): void {\n afterEachHook(cleanup);\n\n if (options.formInternals) installFormInternalsPolyfill();\n}\n"],"mappings":"qOAwCA,SAAgB,EAAQ,EAAyC,EAA0B,CAAC,EAAS,CACnG,EAAc,EAAA,OAAO,EAEjB,EAAQ,eAAe,EAAA,6BAA6B,CAC1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout —\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API.\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\nexport type InstallOptions = {\n /**\n * Polyfill the jsdom/happy-dom gaps in `ElementInternals`/`FormData`/`<form>.reset()`\n * that `useField()` needs (see `installFormInternalsPolyfill`). Global monkey-patches,\n * so opt in only when the suite tests form-associated components.\n * @default false\n */\n formInternals?: boolean;\n};\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n * Pass `{ formInternals: true }` when testing form-associated (`useField`) components.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach, { formInternals: true });\n */\nexport function install(afterEachHook: (fn: () => void) => void, options: InstallOptions = {}): void {\n afterEachHook(cleanup);\n\n if (options.formInternals) installFormInternalsPolyfill();\n}\n"],"mappings":"kQAwCA,SAAgB,EAAQ,EAAyC,EAA0B,CAAC,EAAS,CACnG,EAAc,CAAO,EAEjB,EAAQ,eAAe,EAA6B,CAC1D"}
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { installFormInternalsPolyfill } from './form-internals-polyfill';\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API.\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { installFormInternalsPolyfill } from './form-internals-polyfill';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\nexport type InstallOptions = {\n /**\n * Polyfill the jsdom/happy-dom gaps in `ElementInternals`/`FormData`/`<form>.reset()`\n * that `useField()` needs (see `installFormInternalsPolyfill`). Global monkey-patches,\n * so opt in only when the suite tests form-associated components.\n * @default false\n */\n formInternals?: boolean;\n};\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n * Pass `{ formInternals: true }` when testing form-associated (`useField`) components.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach, { formInternals: true });\n */\nexport function install(afterEachHook: (fn: () => void) => void, options: InstallOptions = {}): void {\n afterEachHook(cleanup);\n\n if (options.formInternals) installFormInternalsPolyfill();\n}\n"],"mappings":"kQAwCA,SAAgB,EAAQ,EAAyC,EAA0B,CAAC,EAAS,CACnG,EAAc,CAAO,EAEjB,EAAQ,eAAe,EAA6B,CAC1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"brand.cjs","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page — the exact failure mode `src/iife.ts`'s header documents). Identity-based checks\n * silently fail across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper — do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAgBA,IAAa,EAA+B,GAA0B,CACpE,IAAM,EAAQ,OAAO,IAAI,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAO,GAAU,YAAY,GAAkB,KAAU,EAEvF,MAHE,GAAc,OAAO,OAAO,EAAK,EAAG,GAAQ,EAAK,CAAC,CAG9C,CACrB"}
1
+ {"version":3,"file":"brand.cjs","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: the exact failure mode `src/iife.ts`'s header documents). Identity-based checks\n * silently fail across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAgBA,IAAa,EAA+B,GAA0B,CACpE,IAAM,EAAQ,OAAO,IAAI,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAO,GAAU,YAAY,GAAkB,KAAU,EAEvF,MAHE,GAAc,OAAO,OAAO,EAAK,EAAG,GAAQ,EAAK,CAAC,CAG9C,CACrB"}
@@ -3,10 +3,10 @@
3
3
  *
4
4
  * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are
5
5
  * process-global, so brands survive a duplicated module graph (two copies of ore bundled into
6
- * one page — the exact failure mode `src/iife.ts`'s header documents). Identity-based checks
6
+ * one page: the exact failure mode `src/iife.ts`'s header documents). Identity-based checks
7
7
  * silently fail across those copies: an object stamped by one graph is invisible to the other.
8
8
  * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,
9
- * `LiveBinding`, `CSSResult`) goes through this helper — do not introduce a second mechanism.
9
+ * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.
10
10
  */
11
11
  export type Brand<T extends object> = {
12
12
  is: (value: unknown) => value is T;
@@ -1 +1 @@
1
- {"version":3,"file":"brand.js","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page — the exact failure mode `src/iife.ts`'s header documents). Identity-based checks\n * silently fail across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper — do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAgBA,IAAa,EAA+B,GAA0B,CACpE,IAAM,EAAQ,OAAO,IAAI,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAO,GAAU,YAAY,GAAkB,KAAU,EAEvF,MAHE,GAAc,OAAO,OAAO,EAAK,EAAG,GAAQ,EAAK,CAAC,CAG9C,CACrB"}
1
+ {"version":3,"file":"brand.js","names":[],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: the exact failure mode `src/iife.ts`'s header documents). Identity-based checks\n * silently fail across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `LiveBinding`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAgBA,IAAa,EAA+B,GAA0B,CACpE,IAAM,EAAQ,OAAO,IAAI,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAO,GAAU,YAAY,GAAkB,KAAU,EAEvF,MAHE,GAAc,OAAO,OAAO,EAAK,EAAG,GAAQ,EAAK,CAAC,CAG9C,CACrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"dom.cjs","names":[],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function —\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal — the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\" — a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping — this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes — read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply — it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const lowerName = name.toLowerCase();\n\n if (/^on[a-z]/i.test(name)) {\n warn(\n `Blocked setAttribute(\"${name}\", ...) — inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (lowerName === 'srcdoc') {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...) — \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (URL_ATTRS.has(lowerName) && DANGEROUS_SCHEME_RE.test(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"0FAoBA,IAAa,EAA2B,GACtC,OAAO,GAAU,WAAc,EAAkB,GAAA,EAAI,EAAA,WAAA,CAAW,CAAK,EAAI,EAAM,MAAQ,EAO5E,EAAmB,SAEnB,EAAoB,GAA0B,EAAM,QAAQ,EAAkB,EAAE,EAEhF,EAAU,GAA8B,CACnD,IAAK,IAAI,EAAI,EAAI,OAAS,EAAG,GAAK,EAAG,IAAK,EAAI,EAAE,GAAG,CACrD,EAEa,EAAe,GAAwB,CAClD,IAAK,IAAM,KAAQ,EACjB,EAAoB,OAAO,CAE/B,EAuBa,MAA+C,CAC1D,IAAI,EAAgB,CAAC,EACjB,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACN,EAAO,CAAQ,EACf,EAAY,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAO,CACT,EACA,gBAAgB,EAAI,CAClB,EAAS,KAAK,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQ,CACV,CACF,CACF,EASM,EAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASK,EACJ,sGAEW,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAM,EAAY,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAK,CAAI,EAAG,CAExB,GAAyB,EAAzB,EAAkG,EAAK,MAAM,CAAC,EAA9G,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,IAAc,SAAU,CAI1B,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,GAAO,MAAQ,IAAQ,GAAO,CAChC,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,IAAM,EAAS,IAAQ,GAAO,OAAS,OAAO,CAAG,EAEjD,GAAI,EAAU,IAAI,CAAS,GAAK,EAAoB,KAAK,CAAM,EAAG,CAE9D,GAA8C,EAA9C,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,EAAG,aAAa,EAAM,CAAM,CAC9B,EAEa,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAAC,EAGH,OAFK,EAAA,WAAW,iBAAiB,CAAI,MAExB,CAAC,EAGhB,IAAM,EAA0B,EAIhC,OAFA,EAAG,iBAAiB,EAAM,EAAU,CAAO,MAE9B,EAAG,oBAAoB,EAAM,EAAU,CAAO,CAC7D,EAEa,EAAW,GAAwB,EAAI,QAAQ,SAAW,GAAM,IAAI,EAAE,YAAY,GAAG,EAErF,EAAqB,GAChC,MAAM,QAAQ,CAAK,GAAM,OAAO,GAAU,YAAY"}
1
+ {"version":3,"file":"dom.cjs","names":[],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function :\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal: the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\": a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping: this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes: read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply: it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const lowerName = name.toLowerCase();\n\n if (/^on[a-z]/i.test(name)) {\n warn(\n `Blocked setAttribute(\"${name}\", ...): inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (lowerName === 'srcdoc') {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...): \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (URL_ATTRS.has(lowerName) && DANGEROUS_SCHEME_RE.test(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"0FAoBA,IAAa,EAA2B,GACtC,OAAO,GAAU,WAAc,EAAkB,GAAA,EAAI,EAAA,WAAA,CAAW,CAAK,EAAI,EAAM,MAAQ,EAO5E,EAAmB,SAEnB,EAAoB,GAA0B,EAAM,QAAQ,EAAkB,EAAE,EAEhF,EAAU,GAA8B,CACnD,IAAK,IAAI,EAAI,EAAI,OAAS,EAAG,GAAK,EAAG,IAAK,EAAI,EAAE,GAAG,CACrD,EAEa,EAAe,GAAwB,CAClD,IAAK,IAAM,KAAQ,EACjB,EAAoB,OAAO,CAE/B,EAuBa,MAA+C,CAC1D,IAAI,EAAgB,CAAC,EACjB,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACN,EAAO,CAAQ,EACf,EAAY,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAO,CACT,EACA,gBAAgB,EAAI,CAClB,EAAS,KAAK,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQ,CACV,CACF,CACF,EASM,EAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASK,EACJ,sGAEW,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAM,EAAY,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAK,CAAI,EAAG,CAExB,GAAyB,EAAzB,EAAiG,EAAK,MAAM,CAAC,EAA7G,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,IAAc,SAAU,CAI1B,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,GAAO,MAAQ,IAAQ,GAAO,CAChC,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,IAAM,EAAS,IAAQ,GAAO,OAAS,OAAO,CAAG,EAEjD,GAAI,EAAU,IAAI,CAAS,GAAK,EAAoB,KAAK,CAAM,EAAG,CAE9D,GAA8C,EAA9C,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,EAAG,aAAa,EAAM,CAAM,CAC9B,EAEa,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAAC,EAGH,OAFK,EAAA,WAAW,iBAAiB,CAAI,MAExB,CAAC,EAGhB,IAAM,EAA0B,EAIhC,OAFA,EAAG,iBAAiB,EAAM,EAAU,CAAO,MAE9B,EAAG,oBAAoB,EAAM,EAAU,CAAO,CAC7D,EAEa,EAAW,GAAwB,EAAI,QAAQ,SAAW,GAAM,IAAI,EAAE,YAAY,GAAG,EAErF,EAAqB,GAChC,MAAM,QAAQ,CAAK,GAAM,OAAO,GAAU,YAAY"}
@@ -3,14 +3,14 @@
3
3
  */
4
4
  import { type Readable } from '@vielzeug/ripple';
5
5
  /**
6
- * A value that may be a plain value, a reactive signal, or a getter function —
6
+ * A value that may be a plain value, a reactive signal, or a getter function :
7
7
  * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.
8
8
  * Exported so consumers can annotate their own composable parameters with it.
9
9
  */
10
10
  export type MaybeReactive<T> = T | Readable<T> | (() => T);
11
11
  /**
12
12
  * Resolves a value that may be a plain value, a getter function, or a reactive
13
- * signal — the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.
13
+ * signal: the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.
14
14
  */
15
15
  export declare const resolveMaybeReactive: <T>(value: MaybeReactive<T>) => T;
16
16
  /**
@@ -23,19 +23,19 @@ export declare const sanitizeCssToken: (value: string) => string;
23
23
  export declare const runAll: (fns: (() => void)[]) => void;
24
24
  export declare const removeNodes: (nodes: Node[]) => void;
25
25
  /**
26
- * Tracks "whatever is currently rendered in one spot" — a list of live DOM nodes plus the
26
+ * Tracks "whatever is currently rendered in one spot": a list of live DOM nodes plus the
27
27
  * cleanup functions that were registered while mounting them. `clear()` tears both down and
28
28
  * resets to empty, ready for the next render.
29
29
  *
30
30
  * Every directive/binding that swaps its rendered content when a reactive source changes
31
31
  * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this
32
- * bookkeeping — this is the one shared implementation instead of four independently-maintained
32
+ * bookkeeping: this is the one shared implementation instead of four independently-maintained
33
33
  * `currentNodes`/`currentCleanups` variable pairs.
34
34
  */
35
35
  export type ReplaceableSlot = {
36
36
  /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */
37
37
  clear(): void;
38
- /** Currently tracked nodes — read after mounting to know what's live. */
38
+ /** Currently tracked nodes: read after mounting to know what's live. */
39
39
  readonly nodes: Node[];
40
40
  /** Pass as the `registerCleanup` callback to whatever mounts the next render. */
41
41
  registerCleanup(fn: () => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK7D;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,EAAE,OAAO,aAAa,CAAC,CAAC,CAAC,KAAG,CAC4B,CAAC;AAE/F;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAW,CAAC;AAEzC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAA6C,CAAC;AAE/F,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,KAAG,IAE5C,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,IAAI,EAAE,KAAG,IAI3C,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gGAAgG;IAChG,KAAK,IAAI,IAAI,CAAC;IACd,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,eAqBxC,CAAC;AAiCF,eAAO,MAAM,OAAO,GAAI,IAAI,OAAO,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,KAAG,IAuCjE,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,IAAI,WAAW,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,MAAM,EACZ,SAAS,aAAa,EACtB,UAAU,uBAAuB,KAChC,CAAC,MAAM,IAAI,CAYb,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,MAA6D,CAAC;AAEpG,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACW,CAAC"}
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK7D;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,EAAE,OAAO,aAAa,CAAC,CAAC,CAAC,KAAG,CAC4B,CAAC;AAE/F;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAW,CAAC;AAEzC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAA6C,CAAC;AAE/F,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,KAAG,IAE5C,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,IAAI,EAAE,KAAG,IAI3C,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gGAAgG;IAChG,KAAK,IAAI,IAAI,CAAC;IACd,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,eAqBxC,CAAC;AAiCF,eAAO,MAAM,OAAO,GAAI,IAAI,OAAO,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,KAAG,IAuCjE,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,IAAI,WAAW,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,MAAM,EACZ,SAAS,aAAa,EACtB,UAAU,uBAAuB,KAChC,CAAC,MAAM,IAAI,CAYb,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,MAA6D,CAAC;AAEpG,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"dom.js","names":[],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function —\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal — the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\" — a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping — this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes — read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply — it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const lowerName = name.toLowerCase();\n\n if (/^on[a-z]/i.test(name)) {\n warn(\n `Blocked setAttribute(\"${name}\", ...) — inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (lowerName === 'srcdoc') {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...) — \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (URL_ATTRS.has(lowerName) && DANGEROUS_SCHEME_RE.test(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"2GAoBA,IAAa,EAA2B,GACtC,OAAO,GAAU,WAAc,EAAkB,EAAI,EAAW,CAAK,EAAI,EAAM,MAAQ,EAO5E,EAAmB,SAEnB,EAAoB,GAA0B,EAAM,QAAQ,EAAkB,EAAE,EAEhF,EAAU,GAA8B,CACnD,IAAK,IAAI,EAAI,EAAI,OAAS,EAAG,GAAK,EAAG,IAAK,EAAI,EAAE,GAAG,CACrD,EAEa,EAAe,GAAwB,CAClD,IAAK,IAAM,KAAQ,EACjB,EAAoB,OAAO,CAE/B,EAuBa,MAA+C,CAC1D,IAAI,EAAgB,CAAC,EACjB,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACN,EAAO,CAAQ,EACf,EAAY,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAO,CACT,EACA,gBAAgB,EAAI,CAClB,EAAS,KAAK,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQ,CACV,CACF,CACF,EASM,EAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASK,EACJ,sGAEW,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAM,EAAY,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAK,CAAI,EAAG,CAExB,GAAyB,EAAzB,EAAkG,EAAK,MAAM,CAAC,EAA9G,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,IAAc,SAAU,CAI1B,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,GAAO,MAAQ,IAAQ,GAAO,CAChC,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,IAAM,EAAS,IAAQ,GAAO,OAAS,OAAO,CAAG,EAEjD,GAAI,EAAU,IAAI,CAAS,GAAK,EAAoB,KAAK,CAAM,EAAG,CAE9D,GAA8C,EAA9C,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,EAAG,aAAa,EAAM,CAAM,CAC9B,EAEa,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAAC,EAGH,OAFK,EAAW,iBAAiB,CAAI,MAExB,CAAC,EAGhB,IAAM,EAA0B,EAIhC,OAFA,EAAG,iBAAiB,EAAM,EAAU,CAAO,MAE9B,EAAG,oBAAoB,EAAM,EAAU,CAAO,CAC7D,EAEa,EAAW,GAAwB,EAAI,QAAQ,SAAW,GAAM,IAAI,EAAE,YAAY,GAAG,EAErF,EAAqB,GAChC,MAAM,QAAQ,CAAK,GAAM,OAAO,GAAU,YAAY"}
1
+ {"version":3,"file":"dom.js","names":[],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * Low-level DOM utilities used throughout the runtime and binding layers.\n */\n\nimport { isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS } from '../errors';\n\n/**\n * A value that may be a plain value, a reactive signal, or a getter function :\n * the shape `when()`, `each()`, `classMap()`, and `bind()` accept.\n * Exported so consumers can annotate their own composable parameters with it.\n */\nexport type MaybeReactive<T> = T | Readable<T> | (() => T);\n\n/**\n * Resolves a value that may be a plain value, a getter function, or a reactive\n * signal: the one shared three-way branch used by `classMap`/`styleMap`/`bind()`.\n */\nexport const resolveMaybeReactive = <T>(value: MaybeReactive<T>): T =>\n typeof value === 'function' ? (value as () => T)() : isReactive(value) ? value.value : value;\n\n/**\n * Characters that can break out of a CSS declaration in an inline style value or property name.\n * Semicolons end the current declaration; braces are meaningful in stylesheet rules but not\n * inline style values, and signal an injection attempt there.\n */\nexport const UNSAFE_CSS_CHARS = /[;{}]/g;\n\nexport const sanitizeCssToken = (value: string): string => value.replace(UNSAFE_CSS_CHARS, '');\n\nexport const runAll = (fns: (() => void)[]): void => {\n for (let i = fns.length - 1; i >= 0; i--) fns[i]?.();\n};\n\nexport const removeNodes = (nodes: Node[]): void => {\n for (const node of nodes) {\n (node as ChildNode).remove();\n }\n};\n\n/**\n * Tracks \"whatever is currently rendered in one spot\": a list of live DOM nodes plus the\n * cleanup functions that were registered while mounting them. `clear()` tears both down and\n * resets to empty, ready for the next render.\n *\n * Every directive/binding that swaps its rendered content when a reactive source changes\n * (`when()`, `unsafeHtml()`, `each()`'s empty-list fallback, `applyHtmlBinding()`) needs exactly this\n * bookkeeping: this is the one shared implementation instead of four independently-maintained\n * `currentNodes`/`currentCleanups` variable pairs.\n */\nexport type ReplaceableSlot = {\n /** Tears down every registered cleanup and removes every tracked node, then resets to empty. */\n clear(): void;\n /** Currently tracked nodes: read after mounting to know what's live. */\n readonly nodes: Node[];\n /** Pass as the `registerCleanup` callback to whatever mounts the next render. */\n registerCleanup(fn: () => void): void;\n /** Replace the tracked node list (call once mounting the next render is complete). */\n setNodes(nodes: Node[]): void;\n};\n\nexport const createReplaceableSlot = (): ReplaceableSlot => {\n let nodes: Node[] = [];\n let cleanups: (() => void)[] = [];\n\n return {\n clear() {\n runAll(cleanups);\n removeNodes(nodes);\n cleanups = [];\n nodes = [];\n },\n get nodes() {\n return nodes;\n },\n registerCleanup(fn) {\n cleanups.push(fn);\n },\n setNodes(next) {\n nodes = next;\n },\n };\n};\n\n/**\n * HTML attributes that accept URLs. Values bound to these attributes are\n * checked for dangerous schemes before being set.\n *\n * `srcdoc` is deliberately excluded: it holds raw HTML (not a URL), so scheme\n * checking doesn't apply: it's blocked unconditionally below, alongside `on*`.\n */\nconst URL_ATTRS = new Set([\n 'action',\n 'cite',\n 'codebase',\n 'data',\n 'formaction',\n 'href',\n 'manifest',\n 'ping',\n 'poster',\n 'src',\n 'xlink:href',\n]);\n\n/**\n * Schemes that execute JavaScript or can embed arbitrary HTML. Blocked\n * unconditionally in URL-accepting attributes.\n * Covers: javascript:, vbscript:, blob:, and data: variants that carry HTML/XML\n * or script-capable SVG. Plain data: image URIs (e.g. data:image/png) are\n * intentionally allowed.\n */\nconst DANGEROUS_SCHEME_RE =\n /^\\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\\/(?:html|svg\\+xml)|application\\/(?:xhtml|xml)))/i;\n\nexport const setAttr = (el: Element, name: string, val: unknown): void => {\n const lowerName = name.toLowerCase();\n\n if (/^on[a-z]/i.test(name)) {\n warn(\n `Blocked setAttribute(\"${name}\", ...): inline event handler attributes are not supported. Use @${name.slice(2)} binding syntax instead.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (lowerName === 'srcdoc') {\n warn(\n `Blocked setAttribute(\"srcdoc\", ...): \"srcdoc\" holds raw HTML, not a URL, and is not supported via attribute binding. Sanitize untrusted content, then use unsafeHtml() if HTML injection is required.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n if (val == null || val === false) {\n el.removeAttribute(name);\n\n return;\n }\n\n const strVal = val === true ? 'true' : String(val);\n\n if (URL_ATTRS.has(lowerName) && DANGEROUS_SCHEME_RE.test(strVal)) {\n warn(\n `Blocked dangerous URL scheme in attribute \"${name}\". Only safe URLs are permitted in URL-accepting attributes.`,\n );\n el.removeAttribute(name);\n\n return;\n }\n\n el.setAttribute(name, strVal);\n};\n\nexport const listen = (\n el: EventTarget | null | undefined,\n name: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) {\n warn(ORE_ERRORS.listenNullTarget(name));\n\n return () => {};\n }\n\n const listener: EventListener = handler;\n\n el.addEventListener(name, listener, options);\n\n return () => el.removeEventListener(name, listener, options);\n};\n\nexport const toKebab = (str: string): string => str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);\n\nexport const isStructuredValue = (value: unknown): value is object =>\n Array.isArray(value) || (typeof value === 'object' && value !== null);\n"],"mappings":"2GAoBA,IAAa,EAA2B,GACtC,OAAO,GAAU,WAAc,EAAkB,EAAI,EAAW,CAAK,EAAI,EAAM,MAAQ,EAO5E,EAAmB,SAEnB,EAAoB,GAA0B,EAAM,QAAQ,EAAkB,EAAE,EAEhF,EAAU,GAA8B,CACnD,IAAK,IAAI,EAAI,EAAI,OAAS,EAAG,GAAK,EAAG,IAAK,EAAI,EAAE,GAAG,CACrD,EAEa,EAAe,GAAwB,CAClD,IAAK,IAAM,KAAQ,EACjB,EAAoB,OAAO,CAE/B,EAuBa,MAA+C,CAC1D,IAAI,EAAgB,CAAC,EACjB,EAA2B,CAAC,EAEhC,MAAO,CACL,OAAQ,CACN,EAAO,CAAQ,EACf,EAAY,CAAK,EACjB,EAAW,CAAC,EACZ,EAAQ,CAAC,CACX,EACA,IAAI,OAAQ,CACV,OAAO,CACT,EACA,gBAAgB,EAAI,CAClB,EAAS,KAAK,CAAE,CAClB,EACA,SAAS,EAAM,CACb,EAAQ,CACV,CACF,CACF,EASM,EAAY,IAAI,IAAI,CACxB,SACA,OACA,WACA,OACA,aACA,OACA,WACA,OACA,SACA,MACA,YACF,CAAC,EASK,EACJ,sGAEW,GAAW,EAAa,EAAc,IAAuB,CACxE,IAAM,EAAY,EAAK,YAAY,EAEnC,GAAI,YAAY,KAAK,CAAI,EAAG,CAExB,GAAyB,EAAzB,EAAiG,EAAK,MAAM,CAAC,EAA7G,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,IAAc,SAAU,CAI1B,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,GAAI,GAAO,MAAQ,IAAQ,GAAO,CAChC,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,IAAM,EAAS,IAAQ,GAAO,OAAS,OAAO,CAAG,EAEjD,GAAI,EAAU,IAAI,CAAS,GAAK,EAAoB,KAAK,CAAM,EAAG,CAE9D,GAA8C,EAA9C,EAEF,EAAG,gBAAgB,CAAI,EAEvB,MACF,CAEA,EAAG,aAAa,EAAM,CAAM,CAC9B,EAEa,GACX,EACA,EACA,EACA,IACiB,CACjB,GAAI,CAAC,EAGH,OAFK,EAAW,iBAAiB,CAAI,MAExB,CAAC,EAGhB,IAAM,EAA0B,EAIhC,OAFA,EAAG,iBAAiB,EAAM,EAAU,CAAO,MAE9B,EAAG,oBAAoB,EAAM,EAAU,CAAO,CAC7D,EAEa,EAAW,GAAwB,EAAI,QAAQ,SAAW,GAAM,IAAI,EAAE,YAAY,GAAG,EAErF,EAAqB,GAChC,MAAM,QAAQ,CAAK,GAAM,OAAO,GAAU,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"emit.cjs","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site — an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()` — the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result — `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true` — the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"kCAqBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAK,EAgClE,MAAsF,CACjG,IAAM,EAAO,EAAA,QAAQ,EAErB,QAAS,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,OAAO,EAAK,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CACpF,EACF"}
1
+ {"version":3,"file":"emit.cjs","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site: an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()`: the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result: `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true`: the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"kCAqBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAK,EAgClE,MAAsF,CACjG,IAAM,EAAO,EAAA,QAAQ,EAErB,QAAS,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,OAAO,EAAK,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CACpF,EACF"}
@@ -8,7 +8,7 @@ type KeysWithoutDetail<T extends Record<string, unknown>> = {
8
8
  /**
9
9
  * Typed emit function: only declared event names type-check, and `detail` is
10
10
  * required exactly when its payload type is non-void. Dynamic event names must
11
- * be cast at the call site — an intentional speed bump, not a silent escape.
11
+ * be cast at the call site: an intentional speed bump, not a silent escape.
12
12
  */
13
13
  export type EmitFn<T extends Record<string, unknown>> = {
14
14
  <K extends KeysWithoutDetail<T>>(event: K): boolean;
@@ -16,15 +16,15 @@ export type EmitFn<T extends Record<string, unknown>> = {
16
16
  };
17
17
  /**
18
18
  * Returns a typed `emit()` function bound to the current component's host element.
19
- * Call once during `setup()` — the `Emits` type parameter maps event names to
19
+ * Call once during `setup()`: the `Emits` type parameter maps event names to
20
20
  * their `detail` payload type.
21
21
  *
22
22
  * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s
23
- * own boolean result — `false` when a listener called `preventDefault()`. Components
23
+ * own boolean result: `false` when a listener called `preventDefault()`. Components
24
24
  * that need to know whether a listener cancelled an event (e.g. to skip a default
25
25
  * action) read this return value directly instead of hand-rolling `dispatchEvent`.
26
26
  *
27
- * Events are dispatched `bubbles: true, composed: true` — the same convention as
27
+ * Events are dispatched `bubbles: true, composed: true`: the same convention as
28
28
  * `ore:error` and native form-control re-dispatches. A listener on the host, an
29
29
  * ancestor, or `document` observes the event even when the component is nested
30
30
  * inside another component's shadow root, so composed events integrate with
@@ -1 +1 @@
1
- {"version":3,"file":"emit.js","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site — an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()` — the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result — `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true` — the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"wCAqBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAK,EAgClE,MAAsF,CACjG,IAAM,EAAO,EAAQ,EAErB,QAAS,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,OAAO,EAAK,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CACpF,EACF"}
1
+ {"version":3,"file":"emit.js","names":[],"sources":["../../src/utils/emit.ts"],"sourcesContent":["/**\n * Type-safe custom event emission for components.\n */\n\nimport { getHost } from '../runtime';\n\ntype NoDetail = undefined | undefined | never;\ntype KeysWithoutDetail<T extends Record<string, unknown>> = {\n [P in keyof T]: [T[P]] extends [NoDetail] ? P : never;\n}[keyof T];\n\n/**\n * Typed emit function: only declared event names type-check, and `detail` is\n * required exactly when its payload type is non-void. Dynamic event names must\n * be cast at the call site: an intentional speed bump, not a silent escape.\n */\nexport type EmitFn<T extends Record<string, unknown>> = {\n <K extends KeysWithoutDetail<T>>(event: K): boolean;\n <K extends Exclude<keyof T, KeysWithoutDetail<T>>>(event: K, detail: T[K]): boolean;\n};\n\nconst DEFAULT_FIRE_OPTIONS = { bubbles: true, cancelable: true, composed: true };\n\n/**\n * Returns a typed `emit()` function bound to the current component's host element.\n * Call once during `setup()`: the `Emits` type parameter maps event names to\n * their `detail` payload type.\n *\n * Every event is dispatched `cancelable: true`, and `emit()` returns `dispatchEvent`'s\n * own boolean result: `false` when a listener called `preventDefault()`. Components\n * that need to know whether a listener cancelled an event (e.g. to skip a default\n * action) read this return value directly instead of hand-rolling `dispatchEvent`.\n *\n * Events are dispatched `bubbles: true, composed: true`: the same convention as\n * `ore:error` and native form-control re-dispatches. A listener on the host, an\n * ancestor, or `document` observes the event even when the component is nested\n * inside another component's shadow root, so composed events integrate with\n * document-level and framework (e.g. React synthetic) listeners without an\n * escape hatch.\n *\n * @example\n * ```ts\n * type Events = { close: undefined; change: { value: string } };\n *\n * setup(props) {\n * const emit = useEmit<Events>();\n * emit('close');\n *\n * const notCancelled = emit('change', { value: 'ok' });\n * if (notCancelled) props.value.value = 'ok';\n * }\n * ```\n */\nexport const useEmit = <T extends Record<string, unknown> = Record<string, never>>(): EmitFn<T> => {\n const host = getHost();\n\n return ((event: keyof T, ...rest: unknown[]) => {\n const customEventInit = rest.length > 0 ? { ...DEFAULT_FIRE_OPTIONS, detail: rest[0] } : DEFAULT_FIRE_OPTIONS;\n\n return host.dispatchEvent(new CustomEvent<unknown>(String(event), customEventInit));\n }) as EmitFn<T>;\n};\n"],"mappings":"wCAqBA,IAAM,EAAuB,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,EAAK,EAgClE,MAAsF,CACjG,IAAM,EAAO,EAAQ,EAErB,QAAS,EAAgB,GAAG,IAAoB,CAC9C,IAAM,EAAkB,EAAK,OAAS,EAAI,CAAE,GAAG,EAAsB,OAAQ,EAAK,EAAG,EAAI,EAEzF,OAAO,EAAK,cAAc,IAAI,YAAqB,OAAO,CAAK,EAAG,CAAe,CAAC,CACpF,EACF"}