@vielzeug/ore 26.10.0 → 26.10.1

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
package/README.md CHANGED
@@ -55,4 +55,4 @@ define('my-counter', {
55
55
 
56
56
  ## License
57
57
 
58
- MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
58
+ MIT © [Helmuth Saatkamp](https://github.com/helmuthdu): part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
@@ -1 +1 @@
1
- {"version":3,"file":"base-element.cjs","names":[],"sources":["../src/base-element.ts"],"sourcesContent":["import { createScope, type Scope, untrack } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from './component-types';\n\nimport { ORE_ERRORS, OreApiError, type OreErrorPhase, OreLifecycleError, reportRuntimeError } from './errors';\nimport { createProps, getPropMeta, type InferProps, type PropInputDefs, type PropsDef } from './props';\nimport {\n beginPendingWork,\n createRuntimeContext,\n type OnFormResetCallback,\n type OnMountedCallback,\n onCleanup,\n runWithContext,\n} from './runtime';\nimport type { HTMLResult } from './template/result';\nimport { loadStylesheet } from './utils/css';\n\n// ─── Component phases ─────────────────────────────────────────────────────────\n// Internal to BaseElement — the only state machine in the package.\n\nconst ComponentPhase = {\n SETUP_DONE: 'setup_done',\n SETUP_RUNNING: 'setup_running',\n UNINITIALIZED: 'uninitialized',\n UNMOUNTED: 'unmounted',\n} as const;\n\ntype ComponentPhase = (typeof ComponentPhase)[keyof typeof ComponentPhase];\n\n// ─── Internal component state ─────────────────────────────────────────────────\n\ntype ComponentState = {\n /** Registered via `onFormReset()` — persists across mount callbacks, unlike `mountCallbacks`. */\n formResetCallbacks: OnFormResetCallback[];\n /** Incremented on every disconnect — invalidates queued mount callbacks. */\n generation: number;\n mountCallbacks: OnMountedCallback[];\n phase: ComponentPhase;\n scope: Scope;\n templateResult: HTMLResult | null;\n};\n\nconst createComponentState = (): ComponentState => ({\n formResetCallbacks: [],\n generation: 0,\n mountCallbacks: [],\n phase: ComponentPhase.UNINITIALIZED,\n scope: createScope(),\n templateResult: null,\n});\n\nconst isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function';\n\n// ─── BaseElement ──────────────────────────────────────────────────────────────\n\n/**\n * Phase transitions:\n *\n * ```\n * UNINITIALIZED ──_runSetup()──► SETUP_DONE\n * SETUP_DONE ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED\n * ```\n *\n * `generation` increments on every disconnect. Scheduled mount callbacks capture\n * it so callbacks belonging to a disconnected instance cannot run after a\n * reconnect (see `_isStale`).\n *\n * Why this lives on the class instead of a standalone pure reducer: every\n * transition here is triggered by running actual user code (`def.setup()`,\n * `onMounted` callbacks) inside a reactive `scope.run()` + `runWithContext()`\n * — there is no meaningful \"decide the next phase\" step that can be separated\n * from \"run the side-effecting thing that produces the phase change\" without\n * introducing a data-only effect-description layer that this package has no\n * other use for. That's why the methods below stay as direct, readable\n * procedural steps instead of a reducer + effect interpreter.\n */\nexport class BaseElement extends HTMLElement {\n static _definition: ComponentDefinition;\n static _normalizedPropDefs: PropsDef<Record<never, never>> | undefined;\n static formAssociated = false;\n static observedAttributes: string[] = [];\n\n private _component: ComponentState;\n\n constructor() {\n super();\n\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (def?.shadow !== false) {\n this.attachShadow({ mode: 'open', ...(def?.shadow as Partial<ShadowRootInit> | undefined) });\n }\n\n this._component = createComponentState();\n }\n\n connectedCallback(): void {\n untrack(() => {\n if (this._component.phase === ComponentPhase.UNINITIALIZED) this._runSetup();\n\n this._init();\n });\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n const propMeta = getPropMeta(this, name);\n\n if (!propMeta) return;\n\n const parsed = propMeta.parse(newValue);\n\n if (\n !Object.is(\n untrack(() => propMeta.signal.value),\n parsed,\n )\n )\n propMeta.signal.value = parsed as never;\n }\n\n disconnectedCallback(): void {\n this._component.generation++;\n this._component.phase = ComponentPhase.UNMOUNTED;\n this._resetSetupState();\n }\n\n /** Dispose one connection's resources before its state can be rebuilt. */\n private _resetSetupState(): void {\n this._component.scope.dispose();\n // Reset mutable fields for next connection, keeping the same object for stable references.\n this._component.formResetCallbacks = [];\n this._component.mountCallbacks = [];\n this._component.phase = ComponentPhase.UNINITIALIZED;\n this._component.scope = createScope();\n this._component.templateResult = null;\n }\n\n /**\n * Native form-association lifecycle callback — the browser calls this on every\n * `formAssociated: true` element inside a `<form>` when that form is reset.\n * Runs every `onFormReset()` callback registered during `setup()`.\n */\n formResetCallback(): void {\n for (const callback of this._component.formResetCallbacks) {\n try {\n callback();\n } catch (error) {\n this._reportLifecycleError(error, 'form-reset');\n }\n }\n }\n\n private _reportLifecycleError(error: unknown, phase: OreErrorPhase): void {\n const err = error instanceof Error ? error : new Error(String(error));\n const oreError = new OreLifecycleError(`<${this.localName}> failed during ${this._component.phase} (${phase})`, {\n cause: err,\n component: this.localName,\n phase,\n });\n\n reportRuntimeError(oreError, this);\n }\n\n private _runSetup(): void {\n this._component.phase = ComponentPhase.SETUP_RUNNING;\n\n const def = (this.constructor as typeof BaseElement)._definition;\n const normalizedPropDefs = (this.constructor as typeof BaseElement)._normalizedPropDefs;\n const ctx = createRuntimeContext(this);\n\n try {\n let setupResult: HTMLResult | null | undefined;\n\n this._component.scope.run(() => {\n setupResult = runWithContext(ctx, () => {\n const setupProps = normalizedPropDefs\n ? createProps(this, normalizedPropDefs)\n : ({} as InferProps<PropInputDefs>);\n\n return def.setup(setupProps as InferProps<PropInputDefs>);\n });\n });\n this._component.mountCallbacks.push(...ctx.mountCallbacks);\n this._component.formResetCallbacks.push(...ctx.formResetCallbacks);\n\n if (isPromiseLike(setupResult)) throw new OreApiError(ORE_ERRORS.asyncSetupUnsupported);\n\n this._component.templateResult = setupResult ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n } catch (error) {\n this._reportLifecycleError(error, 'setup');\n // Setup is atomic: a failed run must not leave partial effects or cleanups\n // live until a later disconnect.\n this._resetSetupState();\n throw error;\n }\n }\n\n private _isStale(capturedGeneration: number): boolean {\n return this._component.generation !== capturedGeneration || !this.isConnected;\n }\n\n private _applyResult(result: HTMLResult | null): void {\n if (!result) return;\n\n const host: Element | ShadowRoot = this.shadowRoot ?? this;\n\n // Mounting can register component cleanup, so preserve a runtime context here too.\n const context = createRuntimeContext(this);\n\n host.replaceChildren();\n this._component.scope.run(() => {\n runWithContext(context, () => {\n result.mount(host, null, onCleanup);\n });\n });\n }\n\n private _init(): void {\n this._applyStyles();\n this._mountTemplate();\n\n // Setup completes before the template mounts, so callbacks always observe live DOM.\n if (this._component.phase === ComponentPhase.SETUP_DONE) this._scheduleMountCallbacks();\n }\n\n private _applyStyles(): void {\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (this.shadowRoot && def?.styles?.length) {\n this.shadowRoot.adoptedStyleSheets = def.styles.map(loadStylesheet);\n }\n }\n\n private _mountTemplate(): void {\n const result = this._component.templateResult;\n\n if (!result) return;\n\n this._applyResult(result);\n }\n\n private _scheduleMountCallbacks(): void {\n if (this._component.mountCallbacks.length === 0) return;\n\n const capturedGeneration = this._component.generation;\n // Tracked as pending work for the duration of this microtask — ended in a `finally`\n // so a thrown callback (already caught per-callback below, but defensive here too)\n // never leaves the counter stuck above zero. See runtime.ts's beginPendingWork().\n const endWork = beginPendingWork();\n\n queueMicrotask(() => {\n try {\n if (this._isStale(capturedGeneration)) return;\n\n // Snapshot callbacks so in-loop registrations don't extend this iteration.\n // Nested onMounted registrations are appended to `batch` and run in the same\n // microtask — no recursive scheduling. Index-based loop because the array\n // grows as nested callbacks are discovered.\n const batch = this._component.mountCallbacks.splice(0);\n\n for (let i = 0; i < batch.length; i++) {\n const callback = batch[i];\n\n try {\n const nestedCtx = createRuntimeContext(this);\n\n this._component.scope.run(() => {\n runWithContext(nestedCtx, () => {\n const cleanup = callback();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n\n if (nestedCtx.mountCallbacks.length > 0) {\n batch.push(...nestedCtx.mountCallbacks);\n }\n\n if (nestedCtx.formResetCallbacks.length > 0) {\n this._component.formResetCallbacks.push(...nestedCtx.formResetCallbacks);\n }\n } catch (error) {\n this._reportLifecycleError(error, 'mounted');\n }\n }\n } finally {\n endWork();\n }\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base-element.cjs","names":[],"sources":["../src/base-element.ts"],"sourcesContent":["import { createScope, type Scope, untrack } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from './component-types';\n\nimport { ORE_ERRORS, OreApiError, type OreErrorPhase, OreLifecycleError, reportRuntimeError } from './errors';\nimport { createProps, getPropMeta, type InferProps, type PropInputDefs, type PropsDef } from './props';\nimport {\n beginPendingWork,\n createRuntimeContext,\n type OnFormResetCallback,\n type OnMountedCallback,\n onCleanup,\n runWithContext,\n} from './runtime';\nimport type { HTMLResult } from './template/result';\nimport { loadStylesheet } from './utils/css';\n\n// ─── Component phases ─────────────────────────────────────────────────────────\n// Internal to BaseElement: the only state machine in the package.\n\nconst ComponentPhase = {\n SETUP_DONE: 'setup_done',\n SETUP_RUNNING: 'setup_running',\n UNINITIALIZED: 'uninitialized',\n UNMOUNTED: 'unmounted',\n} as const;\n\ntype ComponentPhase = (typeof ComponentPhase)[keyof typeof ComponentPhase];\n\n// ─── Internal component state ─────────────────────────────────────────────────\n\ntype ComponentState = {\n /** Registered via `onFormReset()`: persists across mount callbacks, unlike `mountCallbacks`. */\n formResetCallbacks: OnFormResetCallback[];\n /** Incremented on every disconnect: invalidates queued mount callbacks. */\n generation: number;\n mountCallbacks: OnMountedCallback[];\n phase: ComponentPhase;\n scope: Scope;\n templateResult: HTMLResult | null;\n};\n\nconst createComponentState = (): ComponentState => ({\n formResetCallbacks: [],\n generation: 0,\n mountCallbacks: [],\n phase: ComponentPhase.UNINITIALIZED,\n scope: createScope(),\n templateResult: null,\n});\n\nconst isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function';\n\n// ─── BaseElement ──────────────────────────────────────────────────────────────\n\n/**\n * Phase transitions:\n *\n * ```\n * UNINITIALIZED ──_runSetup()──► SETUP_DONE\n * SETUP_DONE ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED\n * ```\n *\n * `generation` increments on every disconnect. Scheduled mount callbacks capture\n * it so callbacks belonging to a disconnected instance cannot run after a\n * reconnect (see `_isStale`).\n *\n * Why this lives on the class instead of a standalone pure reducer: every\n * transition here is triggered by running actual user code (`def.setup()`,\n * `onMounted` callbacks) inside a reactive `scope.run()` + `runWithContext()`\n *: there is no meaningful \"decide the next phase\" step that can be separated\n * from \"run the side-effecting thing that produces the phase change\" without\n * introducing a data-only effect-description layer that this package has no\n * other use for. That's why the methods below stay as direct, readable\n * procedural steps instead of a reducer + effect interpreter.\n */\nexport class BaseElement extends HTMLElement {\n static _definition: ComponentDefinition;\n static _normalizedPropDefs: PropsDef<Record<never, never>> | undefined;\n static formAssociated = false;\n static observedAttributes: string[] = [];\n\n private _component: ComponentState;\n\n constructor() {\n super();\n\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (def?.shadow !== false) {\n this.attachShadow({ mode: 'open', ...(def?.shadow as Partial<ShadowRootInit> | undefined) });\n }\n\n this._component = createComponentState();\n }\n\n connectedCallback(): void {\n untrack(() => {\n if (this._component.phase === ComponentPhase.UNINITIALIZED) this._runSetup();\n\n this._init();\n });\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n const propMeta = getPropMeta(this, name);\n\n if (!propMeta) return;\n\n const parsed = propMeta.parse(newValue);\n\n if (\n !Object.is(\n untrack(() => propMeta.signal.value),\n parsed,\n )\n )\n propMeta.signal.value = parsed as never;\n }\n\n disconnectedCallback(): void {\n this._component.generation++;\n this._component.phase = ComponentPhase.UNMOUNTED;\n this._resetSetupState();\n }\n\n /** Dispose one connection's resources before its state can be rebuilt. */\n private _resetSetupState(): void {\n this._component.scope.dispose();\n // Reset mutable fields for next connection, keeping the same object for stable references.\n this._component.formResetCallbacks = [];\n this._component.mountCallbacks = [];\n this._component.phase = ComponentPhase.UNINITIALIZED;\n this._component.scope = createScope();\n this._component.templateResult = null;\n }\n\n /**\n * Native form-association lifecycle callback: the browser calls this on every\n * `formAssociated: true` element inside a `<form>` when that form is reset.\n * Runs every `onFormReset()` callback registered during `setup()`.\n */\n formResetCallback(): void {\n for (const callback of this._component.formResetCallbacks) {\n try {\n callback();\n } catch (error) {\n this._reportLifecycleError(error, 'form-reset');\n }\n }\n }\n\n private _reportLifecycleError(error: unknown, phase: OreErrorPhase): void {\n const err = error instanceof Error ? error : new Error(String(error));\n const oreError = new OreLifecycleError(`<${this.localName}> failed during ${this._component.phase} (${phase})`, {\n cause: err,\n component: this.localName,\n phase,\n });\n\n reportRuntimeError(oreError, this);\n }\n\n private _runSetup(): void {\n this._component.phase = ComponentPhase.SETUP_RUNNING;\n\n const def = (this.constructor as typeof BaseElement)._definition;\n const normalizedPropDefs = (this.constructor as typeof BaseElement)._normalizedPropDefs;\n const ctx = createRuntimeContext(this);\n\n try {\n let setupResult: HTMLResult | null | undefined;\n\n this._component.scope.run(() => {\n setupResult = runWithContext(ctx, () => {\n const setupProps = normalizedPropDefs\n ? createProps(this, normalizedPropDefs)\n : ({} as InferProps<PropInputDefs>);\n\n return def.setup(setupProps as InferProps<PropInputDefs>);\n });\n });\n this._component.mountCallbacks.push(...ctx.mountCallbacks);\n this._component.formResetCallbacks.push(...ctx.formResetCallbacks);\n\n if (isPromiseLike(setupResult)) throw new OreApiError(ORE_ERRORS.asyncSetupUnsupported);\n\n this._component.templateResult = setupResult ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n } catch (error) {\n this._reportLifecycleError(error, 'setup');\n // Setup is atomic: a failed run must not leave partial effects or cleanups\n // live until a later disconnect.\n this._resetSetupState();\n throw error;\n }\n }\n\n private _isStale(capturedGeneration: number): boolean {\n return this._component.generation !== capturedGeneration || !this.isConnected;\n }\n\n private _applyResult(result: HTMLResult | null): void {\n if (!result) return;\n\n const host: Element | ShadowRoot = this.shadowRoot ?? this;\n\n // Mounting can register component cleanup, so preserve a runtime context here too.\n const context = createRuntimeContext(this);\n\n host.replaceChildren();\n this._component.scope.run(() => {\n runWithContext(context, () => {\n result.mount(host, null, onCleanup);\n });\n });\n }\n\n private _init(): void {\n this._applyStyles();\n this._mountTemplate();\n\n // Setup completes before the template mounts, so callbacks always observe live DOM.\n if (this._component.phase === ComponentPhase.SETUP_DONE) this._scheduleMountCallbacks();\n }\n\n private _applyStyles(): void {\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (this.shadowRoot && def?.styles?.length) {\n this.shadowRoot.adoptedStyleSheets = def.styles.map(loadStylesheet);\n }\n }\n\n private _mountTemplate(): void {\n const result = this._component.templateResult;\n\n if (!result) return;\n\n this._applyResult(result);\n }\n\n private _scheduleMountCallbacks(): void {\n if (this._component.mountCallbacks.length === 0) return;\n\n const capturedGeneration = this._component.generation;\n // Tracked as pending work for the duration of this microtask: ended in a `finally`\n // so a thrown callback (already caught per-callback below, but defensive here too)\n // never leaves the counter stuck above zero. See runtime.ts's beginPendingWork().\n const endWork = beginPendingWork();\n\n queueMicrotask(() => {\n try {\n if (this._isStale(capturedGeneration)) return;\n\n // Snapshot callbacks so in-loop registrations don't extend this iteration.\n // Nested onMounted registrations are appended to `batch` and run in the same\n // microtask: no recursive scheduling. Index-based loop because the array\n // grows as nested callbacks are discovered.\n const batch = this._component.mountCallbacks.splice(0);\n\n for (let i = 0; i < batch.length; i++) {\n const callback = batch[i];\n\n try {\n const nestedCtx = createRuntimeContext(this);\n\n this._component.scope.run(() => {\n runWithContext(nestedCtx, () => {\n const cleanup = callback();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n\n if (nestedCtx.mountCallbacks.length > 0) {\n batch.push(...nestedCtx.mountCallbacks);\n }\n\n if (nestedCtx.formResetCallbacks.length > 0) {\n this._component.formResetCallbacks.push(...nestedCtx.formResetCallbacks);\n }\n } catch (error) {\n this._reportLifecycleError(error, 'mounted');\n }\n }\n } finally {\n endWork();\n }\n });\n }\n}\n"],"mappings":"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"}
@@ -15,7 +15,7 @@ import { type PropsDef } from './props';
15
15
  * Why this lives on the class instead of a standalone pure reducer: every
16
16
  * transition here is triggered by running actual user code (`def.setup()`,
17
17
  * `onMounted` callbacks) inside a reactive `scope.run()` + `runWithContext()`
18
- * — there is no meaningful "decide the next phase" step that can be separated
18
+ *: there is no meaningful "decide the next phase" step that can be separated
19
19
  * from "run the side-effecting thing that produces the phase change" without
20
20
  * introducing a data-only effect-description layer that this package has no
21
21
  * other use for. That's why the methods below stay as direct, readable
@@ -34,7 +34,7 @@ export declare class BaseElement extends HTMLElement {
34
34
  /** Dispose one connection's resources before its state can be rebuilt. */
35
35
  private _resetSetupState;
36
36
  /**
37
- * Native form-association lifecycle callback — the browser calls this on every
37
+ * Native form-association lifecycle callback: the browser calls this on every
38
38
  * `formAssociated: true` element inside a `<form>` when that form is reset.
39
39
  * Runs every `onFormReset()` callback registered during `setup()`.
40
40
  */
@@ -1 +1 @@
1
- {"version":3,"file":"base-element.js","names":[],"sources":["../src/base-element.ts"],"sourcesContent":["import { createScope, type Scope, untrack } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from './component-types';\n\nimport { ORE_ERRORS, OreApiError, type OreErrorPhase, OreLifecycleError, reportRuntimeError } from './errors';\nimport { createProps, getPropMeta, type InferProps, type PropInputDefs, type PropsDef } from './props';\nimport {\n beginPendingWork,\n createRuntimeContext,\n type OnFormResetCallback,\n type OnMountedCallback,\n onCleanup,\n runWithContext,\n} from './runtime';\nimport type { HTMLResult } from './template/result';\nimport { loadStylesheet } from './utils/css';\n\n// ─── Component phases ─────────────────────────────────────────────────────────\n// Internal to BaseElement — the only state machine in the package.\n\nconst ComponentPhase = {\n SETUP_DONE: 'setup_done',\n SETUP_RUNNING: 'setup_running',\n UNINITIALIZED: 'uninitialized',\n UNMOUNTED: 'unmounted',\n} as const;\n\ntype ComponentPhase = (typeof ComponentPhase)[keyof typeof ComponentPhase];\n\n// ─── Internal component state ─────────────────────────────────────────────────\n\ntype ComponentState = {\n /** Registered via `onFormReset()` — persists across mount callbacks, unlike `mountCallbacks`. */\n formResetCallbacks: OnFormResetCallback[];\n /** Incremented on every disconnect — invalidates queued mount callbacks. */\n generation: number;\n mountCallbacks: OnMountedCallback[];\n phase: ComponentPhase;\n scope: Scope;\n templateResult: HTMLResult | null;\n};\n\nconst createComponentState = (): ComponentState => ({\n formResetCallbacks: [],\n generation: 0,\n mountCallbacks: [],\n phase: ComponentPhase.UNINITIALIZED,\n scope: createScope(),\n templateResult: null,\n});\n\nconst isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function';\n\n// ─── BaseElement ──────────────────────────────────────────────────────────────\n\n/**\n * Phase transitions:\n *\n * ```\n * UNINITIALIZED ──_runSetup()──► SETUP_DONE\n * SETUP_DONE ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED\n * ```\n *\n * `generation` increments on every disconnect. Scheduled mount callbacks capture\n * it so callbacks belonging to a disconnected instance cannot run after a\n * reconnect (see `_isStale`).\n *\n * Why this lives on the class instead of a standalone pure reducer: every\n * transition here is triggered by running actual user code (`def.setup()`,\n * `onMounted` callbacks) inside a reactive `scope.run()` + `runWithContext()`\n * — there is no meaningful \"decide the next phase\" step that can be separated\n * from \"run the side-effecting thing that produces the phase change\" without\n * introducing a data-only effect-description layer that this package has no\n * other use for. That's why the methods below stay as direct, readable\n * procedural steps instead of a reducer + effect interpreter.\n */\nexport class BaseElement extends HTMLElement {\n static _definition: ComponentDefinition;\n static _normalizedPropDefs: PropsDef<Record<never, never>> | undefined;\n static formAssociated = false;\n static observedAttributes: string[] = [];\n\n private _component: ComponentState;\n\n constructor() {\n super();\n\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (def?.shadow !== false) {\n this.attachShadow({ mode: 'open', ...(def?.shadow as Partial<ShadowRootInit> | undefined) });\n }\n\n this._component = createComponentState();\n }\n\n connectedCallback(): void {\n untrack(() => {\n if (this._component.phase === ComponentPhase.UNINITIALIZED) this._runSetup();\n\n this._init();\n });\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n const propMeta = getPropMeta(this, name);\n\n if (!propMeta) return;\n\n const parsed = propMeta.parse(newValue);\n\n if (\n !Object.is(\n untrack(() => propMeta.signal.value),\n parsed,\n )\n )\n propMeta.signal.value = parsed as never;\n }\n\n disconnectedCallback(): void {\n this._component.generation++;\n this._component.phase = ComponentPhase.UNMOUNTED;\n this._resetSetupState();\n }\n\n /** Dispose one connection's resources before its state can be rebuilt. */\n private _resetSetupState(): void {\n this._component.scope.dispose();\n // Reset mutable fields for next connection, keeping the same object for stable references.\n this._component.formResetCallbacks = [];\n this._component.mountCallbacks = [];\n this._component.phase = ComponentPhase.UNINITIALIZED;\n this._component.scope = createScope();\n this._component.templateResult = null;\n }\n\n /**\n * Native form-association lifecycle callback — the browser calls this on every\n * `formAssociated: true` element inside a `<form>` when that form is reset.\n * Runs every `onFormReset()` callback registered during `setup()`.\n */\n formResetCallback(): void {\n for (const callback of this._component.formResetCallbacks) {\n try {\n callback();\n } catch (error) {\n this._reportLifecycleError(error, 'form-reset');\n }\n }\n }\n\n private _reportLifecycleError(error: unknown, phase: OreErrorPhase): void {\n const err = error instanceof Error ? error : new Error(String(error));\n const oreError = new OreLifecycleError(`<${this.localName}> failed during ${this._component.phase} (${phase})`, {\n cause: err,\n component: this.localName,\n phase,\n });\n\n reportRuntimeError(oreError, this);\n }\n\n private _runSetup(): void {\n this._component.phase = ComponentPhase.SETUP_RUNNING;\n\n const def = (this.constructor as typeof BaseElement)._definition;\n const normalizedPropDefs = (this.constructor as typeof BaseElement)._normalizedPropDefs;\n const ctx = createRuntimeContext(this);\n\n try {\n let setupResult: HTMLResult | null | undefined;\n\n this._component.scope.run(() => {\n setupResult = runWithContext(ctx, () => {\n const setupProps = normalizedPropDefs\n ? createProps(this, normalizedPropDefs)\n : ({} as InferProps<PropInputDefs>);\n\n return def.setup(setupProps as InferProps<PropInputDefs>);\n });\n });\n this._component.mountCallbacks.push(...ctx.mountCallbacks);\n this._component.formResetCallbacks.push(...ctx.formResetCallbacks);\n\n if (isPromiseLike(setupResult)) throw new OreApiError(ORE_ERRORS.asyncSetupUnsupported);\n\n this._component.templateResult = setupResult ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n } catch (error) {\n this._reportLifecycleError(error, 'setup');\n // Setup is atomic: a failed run must not leave partial effects or cleanups\n // live until a later disconnect.\n this._resetSetupState();\n throw error;\n }\n }\n\n private _isStale(capturedGeneration: number): boolean {\n return this._component.generation !== capturedGeneration || !this.isConnected;\n }\n\n private _applyResult(result: HTMLResult | null): void {\n if (!result) return;\n\n const host: Element | ShadowRoot = this.shadowRoot ?? this;\n\n // Mounting can register component cleanup, so preserve a runtime context here too.\n const context = createRuntimeContext(this);\n\n host.replaceChildren();\n this._component.scope.run(() => {\n runWithContext(context, () => {\n result.mount(host, null, onCleanup);\n });\n });\n }\n\n private _init(): void {\n this._applyStyles();\n this._mountTemplate();\n\n // Setup completes before the template mounts, so callbacks always observe live DOM.\n if (this._component.phase === ComponentPhase.SETUP_DONE) this._scheduleMountCallbacks();\n }\n\n private _applyStyles(): void {\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (this.shadowRoot && def?.styles?.length) {\n this.shadowRoot.adoptedStyleSheets = def.styles.map(loadStylesheet);\n }\n }\n\n private _mountTemplate(): void {\n const result = this._component.templateResult;\n\n if (!result) return;\n\n this._applyResult(result);\n }\n\n private _scheduleMountCallbacks(): void {\n if (this._component.mountCallbacks.length === 0) return;\n\n const capturedGeneration = this._component.generation;\n // Tracked as pending work for the duration of this microtask — ended in a `finally`\n // so a thrown callback (already caught per-callback below, but defensive here too)\n // never leaves the counter stuck above zero. See runtime.ts's beginPendingWork().\n const endWork = beginPendingWork();\n\n queueMicrotask(() => {\n try {\n if (this._isStale(capturedGeneration)) return;\n\n // Snapshot callbacks so in-loop registrations don't extend this iteration.\n // Nested onMounted registrations are appended to `batch` and run in the same\n // microtask — no recursive scheduling. Index-based loop because the array\n // grows as nested callbacks are discovered.\n const batch = this._component.mountCallbacks.splice(0);\n\n for (let i = 0; i < batch.length; i++) {\n const callback = batch[i];\n\n try {\n const nestedCtx = createRuntimeContext(this);\n\n this._component.scope.run(() => {\n runWithContext(nestedCtx, () => {\n const cleanup = callback();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n\n if (nestedCtx.mountCallbacks.length > 0) {\n batch.push(...nestedCtx.mountCallbacks);\n }\n\n if (nestedCtx.formResetCallbacks.length > 0) {\n this._component.formResetCallbacks.push(...nestedCtx.formResetCallbacks);\n }\n } catch (error) {\n this._reportLifecycleError(error, 'mounted');\n }\n }\n } finally {\n endWork();\n }\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base-element.js","names":[],"sources":["../src/base-element.ts"],"sourcesContent":["import { createScope, type Scope, untrack } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from './component-types';\n\nimport { ORE_ERRORS, OreApiError, type OreErrorPhase, OreLifecycleError, reportRuntimeError } from './errors';\nimport { createProps, getPropMeta, type InferProps, type PropInputDefs, type PropsDef } from './props';\nimport {\n beginPendingWork,\n createRuntimeContext,\n type OnFormResetCallback,\n type OnMountedCallback,\n onCleanup,\n runWithContext,\n} from './runtime';\nimport type { HTMLResult } from './template/result';\nimport { loadStylesheet } from './utils/css';\n\n// ─── Component phases ─────────────────────────────────────────────────────────\n// Internal to BaseElement: the only state machine in the package.\n\nconst ComponentPhase = {\n SETUP_DONE: 'setup_done',\n SETUP_RUNNING: 'setup_running',\n UNINITIALIZED: 'uninitialized',\n UNMOUNTED: 'unmounted',\n} as const;\n\ntype ComponentPhase = (typeof ComponentPhase)[keyof typeof ComponentPhase];\n\n// ─── Internal component state ─────────────────────────────────────────────────\n\ntype ComponentState = {\n /** Registered via `onFormReset()`: persists across mount callbacks, unlike `mountCallbacks`. */\n formResetCallbacks: OnFormResetCallback[];\n /** Incremented on every disconnect: invalidates queued mount callbacks. */\n generation: number;\n mountCallbacks: OnMountedCallback[];\n phase: ComponentPhase;\n scope: Scope;\n templateResult: HTMLResult | null;\n};\n\nconst createComponentState = (): ComponentState => ({\n formResetCallbacks: [],\n generation: 0,\n mountCallbacks: [],\n phase: ComponentPhase.UNINITIALIZED,\n scope: createScope(),\n templateResult: null,\n});\n\nconst isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function';\n\n// ─── BaseElement ──────────────────────────────────────────────────────────────\n\n/**\n * Phase transitions:\n *\n * ```\n * UNINITIALIZED ──_runSetup()──► SETUP_DONE\n * SETUP_DONE ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED\n * ```\n *\n * `generation` increments on every disconnect. Scheduled mount callbacks capture\n * it so callbacks belonging to a disconnected instance cannot run after a\n * reconnect (see `_isStale`).\n *\n * Why this lives on the class instead of a standalone pure reducer: every\n * transition here is triggered by running actual user code (`def.setup()`,\n * `onMounted` callbacks) inside a reactive `scope.run()` + `runWithContext()`\n *: there is no meaningful \"decide the next phase\" step that can be separated\n * from \"run the side-effecting thing that produces the phase change\" without\n * introducing a data-only effect-description layer that this package has no\n * other use for. That's why the methods below stay as direct, readable\n * procedural steps instead of a reducer + effect interpreter.\n */\nexport class BaseElement extends HTMLElement {\n static _definition: ComponentDefinition;\n static _normalizedPropDefs: PropsDef<Record<never, never>> | undefined;\n static formAssociated = false;\n static observedAttributes: string[] = [];\n\n private _component: ComponentState;\n\n constructor() {\n super();\n\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (def?.shadow !== false) {\n this.attachShadow({ mode: 'open', ...(def?.shadow as Partial<ShadowRootInit> | undefined) });\n }\n\n this._component = createComponentState();\n }\n\n connectedCallback(): void {\n untrack(() => {\n if (this._component.phase === ComponentPhase.UNINITIALIZED) this._runSetup();\n\n this._init();\n });\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n const propMeta = getPropMeta(this, name);\n\n if (!propMeta) return;\n\n const parsed = propMeta.parse(newValue);\n\n if (\n !Object.is(\n untrack(() => propMeta.signal.value),\n parsed,\n )\n )\n propMeta.signal.value = parsed as never;\n }\n\n disconnectedCallback(): void {\n this._component.generation++;\n this._component.phase = ComponentPhase.UNMOUNTED;\n this._resetSetupState();\n }\n\n /** Dispose one connection's resources before its state can be rebuilt. */\n private _resetSetupState(): void {\n this._component.scope.dispose();\n // Reset mutable fields for next connection, keeping the same object for stable references.\n this._component.formResetCallbacks = [];\n this._component.mountCallbacks = [];\n this._component.phase = ComponentPhase.UNINITIALIZED;\n this._component.scope = createScope();\n this._component.templateResult = null;\n }\n\n /**\n * Native form-association lifecycle callback: the browser calls this on every\n * `formAssociated: true` element inside a `<form>` when that form is reset.\n * Runs every `onFormReset()` callback registered during `setup()`.\n */\n formResetCallback(): void {\n for (const callback of this._component.formResetCallbacks) {\n try {\n callback();\n } catch (error) {\n this._reportLifecycleError(error, 'form-reset');\n }\n }\n }\n\n private _reportLifecycleError(error: unknown, phase: OreErrorPhase): void {\n const err = error instanceof Error ? error : new Error(String(error));\n const oreError = new OreLifecycleError(`<${this.localName}> failed during ${this._component.phase} (${phase})`, {\n cause: err,\n component: this.localName,\n phase,\n });\n\n reportRuntimeError(oreError, this);\n }\n\n private _runSetup(): void {\n this._component.phase = ComponentPhase.SETUP_RUNNING;\n\n const def = (this.constructor as typeof BaseElement)._definition;\n const normalizedPropDefs = (this.constructor as typeof BaseElement)._normalizedPropDefs;\n const ctx = createRuntimeContext(this);\n\n try {\n let setupResult: HTMLResult | null | undefined;\n\n this._component.scope.run(() => {\n setupResult = runWithContext(ctx, () => {\n const setupProps = normalizedPropDefs\n ? createProps(this, normalizedPropDefs)\n : ({} as InferProps<PropInputDefs>);\n\n return def.setup(setupProps as InferProps<PropInputDefs>);\n });\n });\n this._component.mountCallbacks.push(...ctx.mountCallbacks);\n this._component.formResetCallbacks.push(...ctx.formResetCallbacks);\n\n if (isPromiseLike(setupResult)) throw new OreApiError(ORE_ERRORS.asyncSetupUnsupported);\n\n this._component.templateResult = setupResult ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n } catch (error) {\n this._reportLifecycleError(error, 'setup');\n // Setup is atomic: a failed run must not leave partial effects or cleanups\n // live until a later disconnect.\n this._resetSetupState();\n throw error;\n }\n }\n\n private _isStale(capturedGeneration: number): boolean {\n return this._component.generation !== capturedGeneration || !this.isConnected;\n }\n\n private _applyResult(result: HTMLResult | null): void {\n if (!result) return;\n\n const host: Element | ShadowRoot = this.shadowRoot ?? this;\n\n // Mounting can register component cleanup, so preserve a runtime context here too.\n const context = createRuntimeContext(this);\n\n host.replaceChildren();\n this._component.scope.run(() => {\n runWithContext(context, () => {\n result.mount(host, null, onCleanup);\n });\n });\n }\n\n private _init(): void {\n this._applyStyles();\n this._mountTemplate();\n\n // Setup completes before the template mounts, so callbacks always observe live DOM.\n if (this._component.phase === ComponentPhase.SETUP_DONE) this._scheduleMountCallbacks();\n }\n\n private _applyStyles(): void {\n const def = (this.constructor as typeof BaseElement)._definition;\n\n if (this.shadowRoot && def?.styles?.length) {\n this.shadowRoot.adoptedStyleSheets = def.styles.map(loadStylesheet);\n }\n }\n\n private _mountTemplate(): void {\n const result = this._component.templateResult;\n\n if (!result) return;\n\n this._applyResult(result);\n }\n\n private _scheduleMountCallbacks(): void {\n if (this._component.mountCallbacks.length === 0) return;\n\n const capturedGeneration = this._component.generation;\n // Tracked as pending work for the duration of this microtask: ended in a `finally`\n // so a thrown callback (already caught per-callback below, but defensive here too)\n // never leaves the counter stuck above zero. See runtime.ts's beginPendingWork().\n const endWork = beginPendingWork();\n\n queueMicrotask(() => {\n try {\n if (this._isStale(capturedGeneration)) return;\n\n // Snapshot callbacks so in-loop registrations don't extend this iteration.\n // Nested onMounted registrations are appended to `batch` and run in the same\n // microtask: no recursive scheduling. Index-based loop because the array\n // grows as nested callbacks are discovered.\n const batch = this._component.mountCallbacks.splice(0);\n\n for (let i = 0; i < batch.length; i++) {\n const callback = batch[i];\n\n try {\n const nestedCtx = createRuntimeContext(this);\n\n this._component.scope.run(() => {\n runWithContext(nestedCtx, () => {\n const cleanup = callback();\n\n if (typeof cleanup === 'function') onCleanup(cleanup);\n });\n });\n\n if (nestedCtx.mountCallbacks.length > 0) {\n batch.push(...nestedCtx.mountCallbacks);\n }\n\n if (nestedCtx.formResetCallbacks.length > 0) {\n this._component.formResetCallbacks.push(...nestedCtx.formResetCallbacks);\n }\n } catch (error) {\n this._reportLifecycleError(error, 'mounted');\n }\n }\n } finally {\n endWork();\n }\n });\n }\n}\n"],"mappings":"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"}
@@ -2,13 +2,13 @@
2
2
  * Public component definition types for ore web components.
3
3
  * These are the types consumers interact with when calling define().
4
4
  *
5
- * `setup()` takes only `props` and returns its template synchronously — everything else (lifecycle hooks, bindings,
5
+ * `setup()` takes only `props` and returns its template synchronously: everything else (lifecycle hooks, bindings,
6
6
  * context, slots, emit) is a free function imported from `@vielzeug/ore`
7
7
  * (see `runtime.ts`, `host-bind.ts`, `aria.ts`, `context.ts`, `slots.ts`,
8
8
  * `utils/emit.ts`) and resolved through the implicit "current component"
9
9
  * context rather than threaded through a second `setup()` parameter. This
10
10
  * keeps `setup()` a plain function you can call composables from. Note:
11
- * ore's `watchEffect()` is intentionally not named `watch` — `@vielzeug/ripple`
11
+ * ore's `watchEffect()` is intentionally not named `watch`: `@vielzeug/ripple`
12
12
  * already exports a `watch(source, callback)` with different semantics
13
13
  * (explicit source + old/new value pair), so reusing the name here would
14
14
  * silently shadow it whenever both are imported in the same file.
@@ -1 +1 @@
1
- {"version":3,"file":"context.cjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["/**\n * Component context injection API — `inject` / `injectStrict` / `provide` / `createContext`.\n *\n * Context values are stored on the providing element via a WeakMap registry and\n * resolved by walking up the DOM tree (including through shadow boundaries).\n * Providing is done via `provide(key, value)` inside `setup()`.\n *\n * Each `createContext()` call creates a unique key. Descriptions are diagnostic\n * labels only and never determine identity.\n */\n\nimport { warn } from './_dev';\nimport { ORE_ERRORS, OreApiError } from './errors';\nimport { onCleanup, type RuntimeContext, requireSetupContext } from './runtime';\n\nconst contextRegistry = new WeakMap<HTMLElement, Map<InjectionKey<unknown>, unknown>>();\n\nexport type InjectionKey<T> = symbol & {\n readonly __ore_injection_key?: T;\n};\n\n/**\n * Build a linear ancestor chain (including shadow host boundaries).\n * Walks `parentNode` (not `parentElement`) so non-HTML intermediate parents\n * (e.g. an `SVGElement` between child and provider) don't break the chain.\n */\nconst buildAncestorChain = (start: HTMLElement): HTMLElement[] => {\n const chain: HTMLElement[] = [];\n let node: Node | null = start;\n\n while (node) {\n if (node instanceof HTMLElement) chain.push(node);\n\n // A ShadowRoot's parentNode is null — hop to its host to keep walking.\n node = node.parentNode ?? (node instanceof ShadowRoot ? node.host : null);\n }\n\n return chain;\n};\n\n/**\n * Register a context value on a specific element.\n * @internal Backs the public `provide()` — do not call directly.\n */\nconst provideOnElement = <T>(el: HTMLElement, key: InjectionKey<T>, value: T): void => {\n const map = contextRegistry.get(el) ?? new Map<InjectionKey<unknown>, unknown>();\n\n // `inject()` memoizes its result per consumer (see resolvedCache below), so a\n // provider swapping the raw value after a descendant already read it would be\n // silently ignored downstream. Provide a `Readable` (signal/computed) instead\n // of a raw value so descendants observe updates through the value itself.\n if (map.has(key)) {\n warn(\n `provide(): key already provided on <${el.localName}> — overwriting. Provide a Readable to update it instead.`,\n );\n }\n\n map.set(key, value);\n contextRegistry.set(el, map);\n};\n\n/**\n * Register a context value on the current component's host element, making it\n * available to descendant components via `inject(key)`.\n *\n * Provide a `Readable` (signal/computed) rather than a raw value if descendants\n * need to observe later changes — `inject()` resolves and caches the value once\n * per consumer, so re-calling `provide()` with a new raw value later is not seen.\n */\nexport const provide = <T>(key: InjectionKey<T>, value: T): void => {\n const el = requireSetupContext('provide').element;\n\n provideOnElement(el, key, value);\n\n onCleanup(() => {\n const map = contextRegistry.get(el);\n\n if (!map) return;\n\n map.delete(key);\n\n if (map.size === 0) contextRegistry.delete(el);\n });\n};\n\nconst NOT_FOUND_SENTINEL = Symbol('inject.not_found');\n\n/** Per-setup-context cache: avoids repeated ancestor walks for the same key. */\nconst resolvedCache = new WeakMap<object, Map<InjectionKey<unknown>, unknown>>();\n\nconst walkAndFind = <T>(element: HTMLElement, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n const chain = buildAncestorChain(element);\n\n for (const node of chain) {\n const map = contextRegistry.get(node);\n\n if (map?.has(key)) return map.get(key) as T;\n }\n\n return NOT_FOUND_SENTINEL;\n};\n\n/** Cached ancestor-walk lookup shared by `inject()` and `injectStrict()`. */\nconst lookup = <T>(ctx: RuntimeContext, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n let cache = resolvedCache.get(ctx);\n\n if (!cache) {\n cache = new Map();\n resolvedCache.set(ctx, cache);\n }\n\n const cacheKey = key as InjectionKey<unknown>;\n\n if (!cache.has(cacheKey)) cache.set(cacheKey, walkAndFind(ctx.element, key));\n\n return cache.get(cacheKey) as T | typeof NOT_FOUND_SENTINEL;\n};\n\nexport function inject<T>(key: InjectionKey<T>): T | undefined;\nexport function inject<T>(key: InjectionKey<T>, fallback: T): T;\nexport function inject<T>(key: InjectionKey<T>, ...rest: [T?]): T | undefined {\n const found = lookup(requireSetupContext('inject'), key);\n\n if (found === NOT_FOUND_SENTINEL) return rest.length > 0 ? rest[0] : undefined;\n\n return found;\n}\n\nexport const injectStrict = <T>(key: InjectionKey<T>): T => {\n const ctx = requireSetupContext('injectStrict');\n const found = lookup(ctx, key);\n\n if (found !== NOT_FOUND_SENTINEL) return found;\n\n throw new OreApiError(ORE_ERRORS.injectStrictFailed(String(key), ctx.element.localName));\n};\n\n/** Create a unique typed context key with a diagnostic description. */\nexport function createContext<T>(description: string): InjectionKey<T> {\n return Symbol(description) as InjectionKey<T>;\n}\n"],"mappings":"iFAeA,IAAM,EAAkB,IAAI,QAWtB,EAAsB,GAAsC,CAChE,IAAM,EAAuB,CAAC,EAC1B,EAAoB,EAExB,KAAO,GACD,aAAgB,aAAa,EAAM,KAAK,CAAI,EAGhD,EAAO,EAAK,aAAe,aAAgB,WAAa,EAAK,KAAO,MAGtE,OAAO,CACT,EAMM,GAAuB,EAAiB,EAAsB,IAAmB,CACrF,IAAM,EAAM,EAAgB,IAAI,CAAE,GAAK,IAAI,IAMvC,EAAI,IAAI,CAAG,GAEX,GAAuC,EAAG,UAA1C,EAIJ,EAAI,IAAI,EAAK,CAAK,EAClB,EAAgB,IAAI,EAAI,CAAG,CAC7B,EAUa,GAAc,EAAsB,IAAmB,CAClE,IAAM,EAAK,EAAA,oBAAoB,SAAS,CAAC,CAAC,QAE1C,EAAiB,EAAI,EAAK,CAAK,EAE/B,EAAA,cAAgB,CACd,IAAM,EAAM,EAAgB,IAAI,CAAE,EAE7B,IAEL,EAAI,OAAO,CAAG,EAEV,EAAI,OAAS,GAAG,EAAgB,OAAO,CAAE,EAC/C,CAAC,CACH,EAEM,EAAqB,OAAO,kBAAkB,EAG9C,EAAgB,IAAI,QAEpB,GAAkB,EAAsB,IAAwD,CACpG,IAAM,EAAQ,EAAmB,CAAO,EAExC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAM,EAAgB,IAAI,CAAI,EAEpC,GAAI,GAAK,IAAI,CAAG,EAAG,OAAO,EAAI,IAAI,CAAG,CACvC,CAEA,OAAO,CACT,EAGM,GAAa,EAAqB,IAAwD,CAC9F,IAAI,EAAQ,EAAc,IAAI,CAAG,EAE5B,IACH,EAAQ,IAAI,IACZ,EAAc,IAAI,EAAK,CAAK,GAG9B,IAAM,EAAW,EAIjB,OAFK,EAAM,IAAI,CAAQ,GAAG,EAAM,IAAI,EAAU,EAAY,EAAI,QAAS,CAAG,CAAC,EAEpE,EAAM,IAAI,CAAQ,CAC3B,EAIA,SAAgB,EAAU,EAAsB,GAAG,EAA2B,CAC5E,IAAM,EAAQ,EAAO,EAAA,oBAAoB,QAAQ,EAAG,CAAG,EAIvD,OAFI,IAAU,EAA2B,EAAK,OAAS,EAAI,EAAK,GAAK,IAAA,GAE9D,CACT,CAEA,IAAa,EAAmB,GAA4B,CAC1D,IAAM,EAAM,EAAA,oBAAoB,cAAc,EACxC,EAAQ,EAAO,EAAK,CAAG,EAE7B,GAAI,IAAU,EAAoB,OAAO,EAEzC,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,mBAAmB,OAAO,CAAG,EAAG,EAAI,QAAQ,SAAS,CAAC,CACzF,EAGA,SAAgB,EAAiB,EAAsC,CACrE,OAAO,OAAO,CAAW,CAC3B"}
1
+ {"version":3,"file":"context.cjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["/**\n * Component context injection API: `inject` / `injectStrict` / `provide` / `createContext`.\n *\n * Context values are stored on the providing element via a WeakMap registry and\n * resolved by walking up the DOM tree (including through shadow boundaries).\n * Providing is done via `provide(key, value)` inside `setup()`.\n *\n * Each `createContext()` call creates a unique key. Descriptions are diagnostic\n * labels only and never determine identity.\n */\n\nimport { warn } from './_dev';\nimport { ORE_ERRORS, OreApiError } from './errors';\nimport { onCleanup, type RuntimeContext, requireSetupContext } from './runtime';\n\nconst contextRegistry = new WeakMap<HTMLElement, Map<InjectionKey<unknown>, unknown>>();\n\nexport type InjectionKey<T> = symbol & {\n readonly __ore_injection_key?: T;\n};\n\n/**\n * Build a linear ancestor chain (including shadow host boundaries).\n * Walks `parentNode` (not `parentElement`) so non-HTML intermediate parents\n * (e.g. an `SVGElement` between child and provider) don't break the chain.\n */\nconst buildAncestorChain = (start: HTMLElement): HTMLElement[] => {\n const chain: HTMLElement[] = [];\n let node: Node | null = start;\n\n while (node) {\n if (node instanceof HTMLElement) chain.push(node);\n\n // A ShadowRoot's parentNode is null: hop to its host to keep walking.\n node = node.parentNode ?? (node instanceof ShadowRoot ? node.host : null);\n }\n\n return chain;\n};\n\n/**\n * Register a context value on a specific element.\n * @internal Backs the public `provide()`: do not call directly.\n */\nconst provideOnElement = <T>(el: HTMLElement, key: InjectionKey<T>, value: T): void => {\n const map = contextRegistry.get(el) ?? new Map<InjectionKey<unknown>, unknown>();\n\n // `inject()` memoizes its result per consumer (see resolvedCache below), so a\n // provider swapping the raw value after a descendant already read it would be\n // silently ignored downstream. Provide a `Readable` (signal/computed) instead\n // of a raw value so descendants observe updates through the value itself.\n if (map.has(key)) {\n warn(`provide(): key already provided on <${el.localName}>: overwriting. Provide a Readable to update it instead.`);\n }\n\n map.set(key, value);\n contextRegistry.set(el, map);\n};\n\n/**\n * Register a context value on the current component's host element, making it\n * available to descendant components via `inject(key)`.\n *\n * Provide a `Readable` (signal/computed) rather than a raw value if descendants\n * need to observe later changes: `inject()` resolves and caches the value once\n * per consumer, so re-calling `provide()` with a new raw value later is not seen.\n */\nexport const provide = <T>(key: InjectionKey<T>, value: T): void => {\n const el = requireSetupContext('provide').element;\n\n provideOnElement(el, key, value);\n\n onCleanup(() => {\n const map = contextRegistry.get(el);\n\n if (!map) return;\n\n map.delete(key);\n\n if (map.size === 0) contextRegistry.delete(el);\n });\n};\n\nconst NOT_FOUND_SENTINEL = Symbol('inject.not_found');\n\n/** Per-setup-context cache: avoids repeated ancestor walks for the same key. */\nconst resolvedCache = new WeakMap<object, Map<InjectionKey<unknown>, unknown>>();\n\nconst walkAndFind = <T>(element: HTMLElement, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n const chain = buildAncestorChain(element);\n\n for (const node of chain) {\n const map = contextRegistry.get(node);\n\n if (map?.has(key)) return map.get(key) as T;\n }\n\n return NOT_FOUND_SENTINEL;\n};\n\n/** Cached ancestor-walk lookup shared by `inject()` and `injectStrict()`. */\nconst lookup = <T>(ctx: RuntimeContext, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n let cache = resolvedCache.get(ctx);\n\n if (!cache) {\n cache = new Map();\n resolvedCache.set(ctx, cache);\n }\n\n const cacheKey = key as InjectionKey<unknown>;\n\n if (!cache.has(cacheKey)) cache.set(cacheKey, walkAndFind(ctx.element, key));\n\n return cache.get(cacheKey) as T | typeof NOT_FOUND_SENTINEL;\n};\n\nexport function inject<T>(key: InjectionKey<T>): T | undefined;\nexport function inject<T>(key: InjectionKey<T>, fallback: T): T;\nexport function inject<T>(key: InjectionKey<T>, ...rest: [T?]): T | undefined {\n const found = lookup(requireSetupContext('inject'), key);\n\n if (found === NOT_FOUND_SENTINEL) return rest.length > 0 ? rest[0] : undefined;\n\n return found;\n}\n\nexport const injectStrict = <T>(key: InjectionKey<T>): T => {\n const ctx = requireSetupContext('injectStrict');\n const found = lookup(ctx, key);\n\n if (found !== NOT_FOUND_SENTINEL) return found;\n\n throw new OreApiError(ORE_ERRORS.injectStrictFailed(String(key), ctx.element.localName));\n};\n\n/** Create a unique typed context key with a diagnostic description. */\nexport function createContext<T>(description: string): InjectionKey<T> {\n return Symbol(description) as InjectionKey<T>;\n}\n"],"mappings":"iFAeA,IAAM,EAAkB,IAAI,QAWtB,EAAsB,GAAsC,CAChE,IAAM,EAAuB,CAAC,EAC1B,EAAoB,EAExB,KAAO,GACD,aAAgB,aAAa,EAAM,KAAK,CAAI,EAGhD,EAAO,EAAK,aAAe,aAAgB,WAAa,EAAK,KAAO,MAGtE,OAAO,CACT,EAMM,GAAuB,EAAiB,EAAsB,IAAmB,CACrF,IAAM,EAAM,EAAgB,IAAI,CAAE,GAAK,IAAI,IAMvC,EAAI,IAAI,CAAG,GACR,GAAuC,EAAG,UAA1C,EAGP,EAAI,IAAI,EAAK,CAAK,EAClB,EAAgB,IAAI,EAAI,CAAG,CAC7B,EAUa,GAAc,EAAsB,IAAmB,CAClE,IAAM,EAAK,EAAA,oBAAoB,SAAS,CAAC,CAAC,QAE1C,EAAiB,EAAI,EAAK,CAAK,EAE/B,EAAA,cAAgB,CACd,IAAM,EAAM,EAAgB,IAAI,CAAE,EAE7B,IAEL,EAAI,OAAO,CAAG,EAEV,EAAI,OAAS,GAAG,EAAgB,OAAO,CAAE,EAC/C,CAAC,CACH,EAEM,EAAqB,OAAO,kBAAkB,EAG9C,EAAgB,IAAI,QAEpB,GAAkB,EAAsB,IAAwD,CACpG,IAAM,EAAQ,EAAmB,CAAO,EAExC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAM,EAAgB,IAAI,CAAI,EAEpC,GAAI,GAAK,IAAI,CAAG,EAAG,OAAO,EAAI,IAAI,CAAG,CACvC,CAEA,OAAO,CACT,EAGM,GAAa,EAAqB,IAAwD,CAC9F,IAAI,EAAQ,EAAc,IAAI,CAAG,EAE5B,IACH,EAAQ,IAAI,IACZ,EAAc,IAAI,EAAK,CAAK,GAG9B,IAAM,EAAW,EAIjB,OAFK,EAAM,IAAI,CAAQ,GAAG,EAAM,IAAI,EAAU,EAAY,EAAI,QAAS,CAAG,CAAC,EAEpE,EAAM,IAAI,CAAQ,CAC3B,EAIA,SAAgB,EAAU,EAAsB,GAAG,EAA2B,CAC5E,IAAM,EAAQ,EAAO,EAAA,oBAAoB,QAAQ,EAAG,CAAG,EAIvD,OAFI,IAAU,EAA2B,EAAK,OAAS,EAAI,EAAK,GAAK,IAAA,GAE9D,CACT,CAEA,IAAa,EAAmB,GAA4B,CAC1D,IAAM,EAAM,EAAA,oBAAoB,cAAc,EACxC,EAAQ,EAAO,EAAK,CAAG,EAE7B,GAAI,IAAU,EAAoB,OAAO,EAEzC,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,mBAAmB,OAAO,CAAG,EAAG,EAAI,QAAQ,SAAS,CAAC,CACzF,EAGA,SAAgB,EAAiB,EAAsC,CACrE,OAAO,OAAO,CAAW,CAC3B"}
package/dist/context.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Component context injection API — `inject` / `injectStrict` / `provide` / `createContext`.
2
+ * Component context injection API: `inject` / `injectStrict` / `provide` / `createContext`.
3
3
  *
4
4
  * Context values are stored on the providing element via a WeakMap registry and
5
5
  * resolved by walking up the DOM tree (including through shadow boundaries).
@@ -16,7 +16,7 @@ export type InjectionKey<T> = symbol & {
16
16
  * available to descendant components via `inject(key)`.
17
17
  *
18
18
  * Provide a `Readable` (signal/computed) rather than a raw value if descendants
19
- * need to observe later changes — `inject()` resolves and caches the value once
19
+ * need to observe later changes: `inject()` resolves and caches the value once
20
20
  * per consumer, so re-calling `provide()` with a new raw value later is not seen.
21
21
  */
22
22
  export declare const provide: <T>(key: InjectionKey<T>, value: T) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,MAAM,GAAG;IACrC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;CAClC,CAAC;AA0CF;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAG,IAc3D,CAAC;AAmCF,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;AAC/D,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;AAShE,eAAO,MAAM,YAAY,GAAI,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,KAAG,CAOtD,CAAC;AAEF,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAErE"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,MAAM,GAAG;IACrC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;CAClC,CAAC;AAwCF;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAG,IAc3D,CAAC;AAmCF,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;AAC/D,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;AAShE,eAAO,MAAM,YAAY,GAAI,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,KAAG,CAOtD,CAAC;AAEF,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAErE"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["/**\n * Component context injection API — `inject` / `injectStrict` / `provide` / `createContext`.\n *\n * Context values are stored on the providing element via a WeakMap registry and\n * resolved by walking up the DOM tree (including through shadow boundaries).\n * Providing is done via `provide(key, value)` inside `setup()`.\n *\n * Each `createContext()` call creates a unique key. Descriptions are diagnostic\n * labels only and never determine identity.\n */\n\nimport { warn } from './_dev';\nimport { ORE_ERRORS, OreApiError } from './errors';\nimport { onCleanup, type RuntimeContext, requireSetupContext } from './runtime';\n\nconst contextRegistry = new WeakMap<HTMLElement, Map<InjectionKey<unknown>, unknown>>();\n\nexport type InjectionKey<T> = symbol & {\n readonly __ore_injection_key?: T;\n};\n\n/**\n * Build a linear ancestor chain (including shadow host boundaries).\n * Walks `parentNode` (not `parentElement`) so non-HTML intermediate parents\n * (e.g. an `SVGElement` between child and provider) don't break the chain.\n */\nconst buildAncestorChain = (start: HTMLElement): HTMLElement[] => {\n const chain: HTMLElement[] = [];\n let node: Node | null = start;\n\n while (node) {\n if (node instanceof HTMLElement) chain.push(node);\n\n // A ShadowRoot's parentNode is null — hop to its host to keep walking.\n node = node.parentNode ?? (node instanceof ShadowRoot ? node.host : null);\n }\n\n return chain;\n};\n\n/**\n * Register a context value on a specific element.\n * @internal Backs the public `provide()` — do not call directly.\n */\nconst provideOnElement = <T>(el: HTMLElement, key: InjectionKey<T>, value: T): void => {\n const map = contextRegistry.get(el) ?? new Map<InjectionKey<unknown>, unknown>();\n\n // `inject()` memoizes its result per consumer (see resolvedCache below), so a\n // provider swapping the raw value after a descendant already read it would be\n // silently ignored downstream. Provide a `Readable` (signal/computed) instead\n // of a raw value so descendants observe updates through the value itself.\n if (map.has(key)) {\n warn(\n `provide(): key already provided on <${el.localName}> — overwriting. Provide a Readable to update it instead.`,\n );\n }\n\n map.set(key, value);\n contextRegistry.set(el, map);\n};\n\n/**\n * Register a context value on the current component's host element, making it\n * available to descendant components via `inject(key)`.\n *\n * Provide a `Readable` (signal/computed) rather than a raw value if descendants\n * need to observe later changes — `inject()` resolves and caches the value once\n * per consumer, so re-calling `provide()` with a new raw value later is not seen.\n */\nexport const provide = <T>(key: InjectionKey<T>, value: T): void => {\n const el = requireSetupContext('provide').element;\n\n provideOnElement(el, key, value);\n\n onCleanup(() => {\n const map = contextRegistry.get(el);\n\n if (!map) return;\n\n map.delete(key);\n\n if (map.size === 0) contextRegistry.delete(el);\n });\n};\n\nconst NOT_FOUND_SENTINEL = Symbol('inject.not_found');\n\n/** Per-setup-context cache: avoids repeated ancestor walks for the same key. */\nconst resolvedCache = new WeakMap<object, Map<InjectionKey<unknown>, unknown>>();\n\nconst walkAndFind = <T>(element: HTMLElement, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n const chain = buildAncestorChain(element);\n\n for (const node of chain) {\n const map = contextRegistry.get(node);\n\n if (map?.has(key)) return map.get(key) as T;\n }\n\n return NOT_FOUND_SENTINEL;\n};\n\n/** Cached ancestor-walk lookup shared by `inject()` and `injectStrict()`. */\nconst lookup = <T>(ctx: RuntimeContext, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n let cache = resolvedCache.get(ctx);\n\n if (!cache) {\n cache = new Map();\n resolvedCache.set(ctx, cache);\n }\n\n const cacheKey = key as InjectionKey<unknown>;\n\n if (!cache.has(cacheKey)) cache.set(cacheKey, walkAndFind(ctx.element, key));\n\n return cache.get(cacheKey) as T | typeof NOT_FOUND_SENTINEL;\n};\n\nexport function inject<T>(key: InjectionKey<T>): T | undefined;\nexport function inject<T>(key: InjectionKey<T>, fallback: T): T;\nexport function inject<T>(key: InjectionKey<T>, ...rest: [T?]): T | undefined {\n const found = lookup(requireSetupContext('inject'), key);\n\n if (found === NOT_FOUND_SENTINEL) return rest.length > 0 ? rest[0] : undefined;\n\n return found;\n}\n\nexport const injectStrict = <T>(key: InjectionKey<T>): T => {\n const ctx = requireSetupContext('injectStrict');\n const found = lookup(ctx, key);\n\n if (found !== NOT_FOUND_SENTINEL) return found;\n\n throw new OreApiError(ORE_ERRORS.injectStrictFailed(String(key), ctx.element.localName));\n};\n\n/** Create a unique typed context key with a diagnostic description. */\nexport function createContext<T>(description: string): InjectionKey<T> {\n return Symbol(description) as InjectionKey<T>;\n}\n"],"mappings":"8IAeA,IAAM,EAAkB,IAAI,QAWtB,EAAsB,GAAsC,CAChE,IAAM,EAAuB,CAAC,EAC1B,EAAoB,EAExB,KAAO,GACD,aAAgB,aAAa,EAAM,KAAK,CAAI,EAGhD,EAAO,EAAK,aAAe,aAAgB,WAAa,EAAK,KAAO,MAGtE,OAAO,CACT,EAMM,GAAuB,EAAiB,EAAsB,IAAmB,CACrF,IAAM,EAAM,EAAgB,IAAI,CAAE,GAAK,IAAI,IAMvC,EAAI,IAAI,CAAG,GAEX,GAAuC,EAAG,UAA1C,EAIJ,EAAI,IAAI,EAAK,CAAK,EAClB,EAAgB,IAAI,EAAI,CAAG,CAC7B,EAUa,GAAc,EAAsB,IAAmB,CAClE,IAAM,EAAK,EAAoB,SAAS,CAAC,CAAC,QAE1C,EAAiB,EAAI,EAAK,CAAK,EAE/B,MAAgB,CACd,IAAM,EAAM,EAAgB,IAAI,CAAE,EAE7B,IAEL,EAAI,OAAO,CAAG,EAEV,EAAI,OAAS,GAAG,EAAgB,OAAO,CAAE,EAC/C,CAAC,CACH,EAEM,EAAqB,OAAO,kBAAkB,EAG9C,EAAgB,IAAI,QAEpB,GAAkB,EAAsB,IAAwD,CACpG,IAAM,EAAQ,EAAmB,CAAO,EAExC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAM,EAAgB,IAAI,CAAI,EAEpC,GAAI,GAAK,IAAI,CAAG,EAAG,OAAO,EAAI,IAAI,CAAG,CACvC,CAEA,OAAO,CACT,EAGM,GAAa,EAAqB,IAAwD,CAC9F,IAAI,EAAQ,EAAc,IAAI,CAAG,EAE5B,IACH,EAAQ,IAAI,IACZ,EAAc,IAAI,EAAK,CAAK,GAG9B,IAAM,EAAW,EAIjB,OAFK,EAAM,IAAI,CAAQ,GAAG,EAAM,IAAI,EAAU,EAAY,EAAI,QAAS,CAAG,CAAC,EAEpE,EAAM,IAAI,CAAQ,CAC3B,EAIA,SAAgB,EAAU,EAAsB,GAAG,EAA2B,CAC5E,IAAM,EAAQ,EAAO,EAAoB,QAAQ,EAAG,CAAG,EAIvD,OAFI,IAAU,EAA2B,EAAK,OAAS,EAAI,EAAK,GAAK,IAAA,GAE9D,CACT,CAEA,IAAa,EAAmB,GAA4B,CAC1D,IAAM,EAAM,EAAoB,cAAc,EACxC,EAAQ,EAAO,EAAK,CAAG,EAE7B,GAAI,IAAU,EAAoB,OAAO,EAEzC,MAAM,IAAI,EAAY,EAAW,mBAAmB,OAAO,CAAG,EAAG,EAAI,QAAQ,SAAS,CAAC,CACzF,EAGA,SAAgB,EAAiB,EAAsC,CACrE,OAAO,OAAO,CAAW,CAC3B"}
1
+ {"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["/**\n * Component context injection API: `inject` / `injectStrict` / `provide` / `createContext`.\n *\n * Context values are stored on the providing element via a WeakMap registry and\n * resolved by walking up the DOM tree (including through shadow boundaries).\n * Providing is done via `provide(key, value)` inside `setup()`.\n *\n * Each `createContext()` call creates a unique key. Descriptions are diagnostic\n * labels only and never determine identity.\n */\n\nimport { warn } from './_dev';\nimport { ORE_ERRORS, OreApiError } from './errors';\nimport { onCleanup, type RuntimeContext, requireSetupContext } from './runtime';\n\nconst contextRegistry = new WeakMap<HTMLElement, Map<InjectionKey<unknown>, unknown>>();\n\nexport type InjectionKey<T> = symbol & {\n readonly __ore_injection_key?: T;\n};\n\n/**\n * Build a linear ancestor chain (including shadow host boundaries).\n * Walks `parentNode` (not `parentElement`) so non-HTML intermediate parents\n * (e.g. an `SVGElement` between child and provider) don't break the chain.\n */\nconst buildAncestorChain = (start: HTMLElement): HTMLElement[] => {\n const chain: HTMLElement[] = [];\n let node: Node | null = start;\n\n while (node) {\n if (node instanceof HTMLElement) chain.push(node);\n\n // A ShadowRoot's parentNode is null: hop to its host to keep walking.\n node = node.parentNode ?? (node instanceof ShadowRoot ? node.host : null);\n }\n\n return chain;\n};\n\n/**\n * Register a context value on a specific element.\n * @internal Backs the public `provide()`: do not call directly.\n */\nconst provideOnElement = <T>(el: HTMLElement, key: InjectionKey<T>, value: T): void => {\n const map = contextRegistry.get(el) ?? new Map<InjectionKey<unknown>, unknown>();\n\n // `inject()` memoizes its result per consumer (see resolvedCache below), so a\n // provider swapping the raw value after a descendant already read it would be\n // silently ignored downstream. Provide a `Readable` (signal/computed) instead\n // of a raw value so descendants observe updates through the value itself.\n if (map.has(key)) {\n warn(`provide(): key already provided on <${el.localName}>: overwriting. Provide a Readable to update it instead.`);\n }\n\n map.set(key, value);\n contextRegistry.set(el, map);\n};\n\n/**\n * Register a context value on the current component's host element, making it\n * available to descendant components via `inject(key)`.\n *\n * Provide a `Readable` (signal/computed) rather than a raw value if descendants\n * need to observe later changes: `inject()` resolves and caches the value once\n * per consumer, so re-calling `provide()` with a new raw value later is not seen.\n */\nexport const provide = <T>(key: InjectionKey<T>, value: T): void => {\n const el = requireSetupContext('provide').element;\n\n provideOnElement(el, key, value);\n\n onCleanup(() => {\n const map = contextRegistry.get(el);\n\n if (!map) return;\n\n map.delete(key);\n\n if (map.size === 0) contextRegistry.delete(el);\n });\n};\n\nconst NOT_FOUND_SENTINEL = Symbol('inject.not_found');\n\n/** Per-setup-context cache: avoids repeated ancestor walks for the same key. */\nconst resolvedCache = new WeakMap<object, Map<InjectionKey<unknown>, unknown>>();\n\nconst walkAndFind = <T>(element: HTMLElement, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n const chain = buildAncestorChain(element);\n\n for (const node of chain) {\n const map = contextRegistry.get(node);\n\n if (map?.has(key)) return map.get(key) as T;\n }\n\n return NOT_FOUND_SENTINEL;\n};\n\n/** Cached ancestor-walk lookup shared by `inject()` and `injectStrict()`. */\nconst lookup = <T>(ctx: RuntimeContext, key: InjectionKey<T>): T | typeof NOT_FOUND_SENTINEL => {\n let cache = resolvedCache.get(ctx);\n\n if (!cache) {\n cache = new Map();\n resolvedCache.set(ctx, cache);\n }\n\n const cacheKey = key as InjectionKey<unknown>;\n\n if (!cache.has(cacheKey)) cache.set(cacheKey, walkAndFind(ctx.element, key));\n\n return cache.get(cacheKey) as T | typeof NOT_FOUND_SENTINEL;\n};\n\nexport function inject<T>(key: InjectionKey<T>): T | undefined;\nexport function inject<T>(key: InjectionKey<T>, fallback: T): T;\nexport function inject<T>(key: InjectionKey<T>, ...rest: [T?]): T | undefined {\n const found = lookup(requireSetupContext('inject'), key);\n\n if (found === NOT_FOUND_SENTINEL) return rest.length > 0 ? rest[0] : undefined;\n\n return found;\n}\n\nexport const injectStrict = <T>(key: InjectionKey<T>): T => {\n const ctx = requireSetupContext('injectStrict');\n const found = lookup(ctx, key);\n\n if (found !== NOT_FOUND_SENTINEL) return found;\n\n throw new OreApiError(ORE_ERRORS.injectStrictFailed(String(key), ctx.element.localName));\n};\n\n/** Create a unique typed context key with a diagnostic description. */\nexport function createContext<T>(description: string): InjectionKey<T> {\n return Symbol(description) as InjectionKey<T>;\n}\n"],"mappings":"8IAeA,IAAM,EAAkB,IAAI,QAWtB,EAAsB,GAAsC,CAChE,IAAM,EAAuB,CAAC,EAC1B,EAAoB,EAExB,KAAO,GACD,aAAgB,aAAa,EAAM,KAAK,CAAI,EAGhD,EAAO,EAAK,aAAe,aAAgB,WAAa,EAAK,KAAO,MAGtE,OAAO,CACT,EAMM,GAAuB,EAAiB,EAAsB,IAAmB,CACrF,IAAM,EAAM,EAAgB,IAAI,CAAE,GAAK,IAAI,IAMvC,EAAI,IAAI,CAAG,GACR,GAAuC,EAAG,UAA1C,EAGP,EAAI,IAAI,EAAK,CAAK,EAClB,EAAgB,IAAI,EAAI,CAAG,CAC7B,EAUa,GAAc,EAAsB,IAAmB,CAClE,IAAM,EAAK,EAAoB,SAAS,CAAC,CAAC,QAE1C,EAAiB,EAAI,EAAK,CAAK,EAE/B,MAAgB,CACd,IAAM,EAAM,EAAgB,IAAI,CAAE,EAE7B,IAEL,EAAI,OAAO,CAAG,EAEV,EAAI,OAAS,GAAG,EAAgB,OAAO,CAAE,EAC/C,CAAC,CACH,EAEM,EAAqB,OAAO,kBAAkB,EAG9C,EAAgB,IAAI,QAEpB,GAAkB,EAAsB,IAAwD,CACpG,IAAM,EAAQ,EAAmB,CAAO,EAExC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAM,EAAgB,IAAI,CAAI,EAEpC,GAAI,GAAK,IAAI,CAAG,EAAG,OAAO,EAAI,IAAI,CAAG,CACvC,CAEA,OAAO,CACT,EAGM,GAAa,EAAqB,IAAwD,CAC9F,IAAI,EAAQ,EAAc,IAAI,CAAG,EAE5B,IACH,EAAQ,IAAI,IACZ,EAAc,IAAI,EAAK,CAAK,GAG9B,IAAM,EAAW,EAIjB,OAFK,EAAM,IAAI,CAAQ,GAAG,EAAM,IAAI,EAAU,EAAY,EAAI,QAAS,CAAG,CAAC,EAEpE,EAAM,IAAI,CAAQ,CAC3B,EAIA,SAAgB,EAAU,EAAsB,GAAG,EAA2B,CAC5E,IAAM,EAAQ,EAAO,EAAoB,QAAQ,EAAG,CAAG,EAIvD,OAFI,IAAU,EAA2B,EAAK,OAAS,EAAI,EAAK,GAAK,IAAA,GAE9D,CACT,CAEA,IAAa,EAAmB,GAA4B,CAC1D,IAAM,EAAM,EAAoB,cAAc,EACxC,EAAQ,EAAO,EAAK,CAAG,EAE7B,GAAI,IAAU,EAAoB,OAAO,EAEzC,MAAM,IAAI,EAAY,EAAW,mBAAmB,OAAO,CAAG,EAAG,EAAI,QAAQ,SAAS,CAAC,CACzF,EAGA,SAAgB,EAAiB,EAAsC,CACrE,OAAO,OAAO,CAAW,CAC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"define.cjs","names":[],"sources":["../src/define.ts"],"sourcesContent":["import { createComponentClass } from './_component-class';\nimport type { ComponentDefinition } from './component-types';\nimport { ORE_ERRORS, OreApiError } from './errors';\n\n/**\n * Create and register a web component.\n *\n * The `setup` function runs for each connection and returns an `HTMLResult`.\n * Disconnecting disposes its state; reconnecting rebuilds it. All reactive\n * behaviour within a connection is expressed through directives inside the\n * template — not by re-evaluating setup itself.\n *\n * Everything besides `props` — lifecycle hooks, host bindings, context, slots,\n * emit — is a free function imported from `@vielzeug/ore` and called directly\n * from inside `setup()` (or from a composable it calls):\n *\n * ```ts\n * import { define, html, prop, onMounted, useEmit, useSlots } from '@vielzeug/ore';\n *\n * define<{ count?: number }>('my-counter', {\n * props: { count: prop.number(0) },\n * setup(props) {\n * const emit = useEmit<{ increment: number }>();\n * const slots = useSlots<'header' | 'footer'>();\n *\n * onMounted(() => console.log('mounted'));\n *\n * return html`<button @click=${() => emit('increment', props.count.value + 1)}>${props.count}</button>`;\n * },\n * });\n * ```\n *\n * Returns the registered class, so callers keep a handle for `instanceof` or\n * subclassing without a follow-up `customElements.get(tag)` cast.\n */\nexport function define<Props extends Record<string, unknown> = Record<never, never>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): CustomElementConstructor {\n if (!tag) throw new OreApiError(ORE_ERRORS.defineRequiresTag);\n if (customElements.get(tag)) throw new OreApiError(ORE_ERRORS.defineDuplicate(tag));\n\n const ComponentClass = createComponentClass(tag, definition);\n\n // Registration is intentionally the sole global side effect of define().\n Object.defineProperty(ComponentClass, 'name', { value: tag });\n customElements.define(tag, ComponentClass);\n\n return ComponentClass;\n}\n"],"mappings":"oEAmCA,SAAgB,EACd,EACA,EAC0B,CAC1B,GAAI,CAAC,EAAK,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,iBAAiB,EAC5D,GAAI,eAAe,IAAI,CAAG,EAAG,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,gBAAgB,CAAG,CAAC,EAElF,IAAM,EAAiB,EAAA,qBAAqB,EAAK,CAAU,EAM3D,OAHA,OAAO,eAAe,EAAgB,OAAQ,CAAE,MAAO,CAAI,CAAC,EAC5D,eAAe,OAAO,EAAK,CAAc,EAElC,CACT"}
1
+ {"version":3,"file":"define.cjs","names":[],"sources":["../src/define.ts"],"sourcesContent":["import { createComponentClass } from './_component-class';\nimport type { ComponentDefinition } from './component-types';\nimport { ORE_ERRORS, OreApiError } from './errors';\n\n/**\n * Create and register a web component.\n *\n * The `setup` function runs for each connection and returns an `HTMLResult`.\n * Disconnecting disposes its state; reconnecting rebuilds it. All reactive\n * behaviour within a connection is expressed through directives inside the\n * template: not by re-evaluating setup itself.\n *\n * Everything besides `props`: lifecycle hooks, host bindings, context, slots,\n * emit: is a free function imported from `@vielzeug/ore` and called directly\n * from inside `setup()` (or from a composable it calls):\n *\n * ```ts\n * import { define, html, prop, onMounted, useEmit, useSlots } from '@vielzeug/ore';\n *\n * define<{ count?: number }>('my-counter', {\n * props: { count: prop.number(0) },\n * setup(props) {\n * const emit = useEmit<{ increment: number }>();\n * const slots = useSlots<'header' | 'footer'>();\n *\n * onMounted(() => console.log('mounted'));\n *\n * return html`<button @click=${() => emit('increment', props.count.value + 1)}>${props.count}</button>`;\n * },\n * });\n * ```\n *\n * Returns the registered class, so callers keep a handle for `instanceof` or\n * subclassing without a follow-up `customElements.get(tag)` cast.\n */\nexport function define<Props extends Record<string, unknown> = Record<never, never>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): CustomElementConstructor {\n if (!tag) throw new OreApiError(ORE_ERRORS.defineRequiresTag);\n if (customElements.get(tag)) throw new OreApiError(ORE_ERRORS.defineDuplicate(tag));\n\n const ComponentClass = createComponentClass(tag, definition);\n\n // Registration is intentionally the sole global side effect of define().\n Object.defineProperty(ComponentClass, 'name', { value: tag });\n customElements.define(tag, ComponentClass);\n\n return ComponentClass;\n}\n"],"mappings":"oEAmCA,SAAgB,EACd,EACA,EAC0B,CAC1B,GAAI,CAAC,EAAK,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,iBAAiB,EAC5D,GAAI,eAAe,IAAI,CAAG,EAAG,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,gBAAgB,CAAG,CAAC,EAElF,IAAM,EAAiB,EAAA,qBAAqB,EAAK,CAAU,EAM3D,OAHA,OAAO,eAAe,EAAgB,OAAQ,CAAE,MAAO,CAAI,CAAC,EAC5D,eAAe,OAAO,EAAK,CAAc,EAElC,CACT"}
package/dist/define.d.ts CHANGED
@@ -5,10 +5,10 @@ import type { ComponentDefinition } from './component-types';
5
5
  * The `setup` function runs for each connection and returns an `HTMLResult`.
6
6
  * Disconnecting disposes its state; reconnecting rebuilds it. All reactive
7
7
  * behaviour within a connection is expressed through directives inside the
8
- * template — not by re-evaluating setup itself.
8
+ * template: not by re-evaluating setup itself.
9
9
  *
10
- * Everything besides `props` — lifecycle hooks, host bindings, context, slots,
11
- * emit — is a free function imported from `@vielzeug/ore` and called directly
10
+ * Everything besides `props`: lifecycle hooks, host bindings, context, slots,
11
+ * emit: is a free function imported from `@vielzeug/ore` and called directly
12
12
  * from inside `setup()` (or from a composable it calls):
13
13
  *
14
14
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"define.js","names":[],"sources":["../src/define.ts"],"sourcesContent":["import { createComponentClass } from './_component-class';\nimport type { ComponentDefinition } from './component-types';\nimport { ORE_ERRORS, OreApiError } from './errors';\n\n/**\n * Create and register a web component.\n *\n * The `setup` function runs for each connection and returns an `HTMLResult`.\n * Disconnecting disposes its state; reconnecting rebuilds it. All reactive\n * behaviour within a connection is expressed through directives inside the\n * template — not by re-evaluating setup itself.\n *\n * Everything besides `props` — lifecycle hooks, host bindings, context, slots,\n * emit — is a free function imported from `@vielzeug/ore` and called directly\n * from inside `setup()` (or from a composable it calls):\n *\n * ```ts\n * import { define, html, prop, onMounted, useEmit, useSlots } from '@vielzeug/ore';\n *\n * define<{ count?: number }>('my-counter', {\n * props: { count: prop.number(0) },\n * setup(props) {\n * const emit = useEmit<{ increment: number }>();\n * const slots = useSlots<'header' | 'footer'>();\n *\n * onMounted(() => console.log('mounted'));\n *\n * return html`<button @click=${() => emit('increment', props.count.value + 1)}>${props.count}</button>`;\n * },\n * });\n * ```\n *\n * Returns the registered class, so callers keep a handle for `instanceof` or\n * subclassing without a follow-up `customElements.get(tag)` cast.\n */\nexport function define<Props extends Record<string, unknown> = Record<never, never>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): CustomElementConstructor {\n if (!tag) throw new OreApiError(ORE_ERRORS.defineRequiresTag);\n if (customElements.get(tag)) throw new OreApiError(ORE_ERRORS.defineDuplicate(tag));\n\n const ComponentClass = createComponentClass(tag, definition);\n\n // Registration is intentionally the sole global side effect of define().\n Object.defineProperty(ComponentClass, 'name', { value: tag });\n customElements.define(tag, ComponentClass);\n\n return ComponentClass;\n}\n"],"mappings":"uHAmCA,SAAgB,EACd,EACA,EAC0B,CAC1B,GAAI,CAAC,EAAK,MAAM,IAAI,EAAY,EAAW,iBAAiB,EAC5D,GAAI,eAAe,IAAI,CAAG,EAAG,MAAM,IAAI,EAAY,EAAW,gBAAgB,CAAG,CAAC,EAElF,IAAM,EAAiB,EAAqB,EAAK,CAAU,EAM3D,OAHA,OAAO,eAAe,EAAgB,OAAQ,CAAE,MAAO,CAAI,CAAC,EAC5D,eAAe,OAAO,EAAK,CAAc,EAElC,CACT"}
1
+ {"version":3,"file":"define.js","names":[],"sources":["../src/define.ts"],"sourcesContent":["import { createComponentClass } from './_component-class';\nimport type { ComponentDefinition } from './component-types';\nimport { ORE_ERRORS, OreApiError } from './errors';\n\n/**\n * Create and register a web component.\n *\n * The `setup` function runs for each connection and returns an `HTMLResult`.\n * Disconnecting disposes its state; reconnecting rebuilds it. All reactive\n * behaviour within a connection is expressed through directives inside the\n * template: not by re-evaluating setup itself.\n *\n * Everything besides `props`: lifecycle hooks, host bindings, context, slots,\n * emit: is a free function imported from `@vielzeug/ore` and called directly\n * from inside `setup()` (or from a composable it calls):\n *\n * ```ts\n * import { define, html, prop, onMounted, useEmit, useSlots } from '@vielzeug/ore';\n *\n * define<{ count?: number }>('my-counter', {\n * props: { count: prop.number(0) },\n * setup(props) {\n * const emit = useEmit<{ increment: number }>();\n * const slots = useSlots<'header' | 'footer'>();\n *\n * onMounted(() => console.log('mounted'));\n *\n * return html`<button @click=${() => emit('increment', props.count.value + 1)}>${props.count}</button>`;\n * },\n * });\n * ```\n *\n * Returns the registered class, so callers keep a handle for `instanceof` or\n * subclassing without a follow-up `customElements.get(tag)` cast.\n */\nexport function define<Props extends Record<string, unknown> = Record<never, never>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): CustomElementConstructor {\n if (!tag) throw new OreApiError(ORE_ERRORS.defineRequiresTag);\n if (customElements.get(tag)) throw new OreApiError(ORE_ERRORS.defineDuplicate(tag));\n\n const ComponentClass = createComponentClass(tag, definition);\n\n // Registration is intentionally the sole global side effect of define().\n Object.defineProperty(ComponentClass, 'name', { value: tag });\n customElements.define(tag, ComponentClass);\n\n return ComponentClass;\n}\n"],"mappings":"uHAmCA,SAAgB,EACd,EACA,EAC0B,CAC1B,GAAI,CAAC,EAAK,MAAM,IAAI,EAAY,EAAW,iBAAiB,EAC5D,GAAI,eAAe,IAAI,CAAG,EAAG,MAAM,IAAI,EAAY,EAAW,gBAAgB,CAAG,CAAC,EAElF,IAAM,EAAiB,EAAqB,EAAK,CAAU,EAM3D,OAHA,OAAO,eAAe,EAAgB,OAAQ,CAAE,MAAO,CAAI,CAAC,EAC5D,eAAe,OAAO,EAAK,CAAc,EAElC,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"classMap.cjs","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { type MaybeReactive, resolveMaybeReactive } from '../utils/dom';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, MaybeReactive<boolean>>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => resolveMaybeReactive(v))\n // Strip whitespace from each key — spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"sEAoBA,IAAa,EAAY,IACvB,EAAO,EAAA,SAAA,KACL,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAO,EAAA,qBAAqB,CAAC,CAAC,CAAC,CAE1C,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
1
+ {"version":3,"file":"classMap.cjs","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { type MaybeReactive, resolveMaybeReactive } from '../utils/dom';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, MaybeReactive<boolean>>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => resolveMaybeReactive(v))\n // Strip whitespace from each key: spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"sEAoBA,IAAa,EAAY,IACvB,EAAO,EAAA,SAAA,KACL,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAO,EAAA,qBAAqB,CAAC,CAAC,CAAC,CAE1C,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
@@ -1 +1 @@
1
- {"version":3,"file":"classMap.js","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { type MaybeReactive, resolveMaybeReactive } from '../utils/dom';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, MaybeReactive<boolean>>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => resolveMaybeReactive(v))\n // Strip whitespace from each key — spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"mGAoBA,IAAa,EAAY,GAChB,MACL,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAO,EAAqB,CAAC,CAAC,CAAC,CAE1C,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
1
+ {"version":3,"file":"classMap.js","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { type MaybeReactive, resolveMaybeReactive } from '../utils/dom';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, MaybeReactive<boolean>>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => resolveMaybeReactive(v))\n // Strip whitespace from each key: spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"mGAoBA,IAAa,EAAY,GAChB,MACL,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAO,EAAqB,CAAC,CAAC,CAAC,CAE1C,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
@@ -1 +1 @@
1
- {"version":3,"file":"each.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { type MaybeReactive, removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = MaybeReactive<T[]>;\ntype ItemKey = number | string;\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n const cleanups: (() => void)[] = [];\n let nodes: Node[] = [];\n\n scope.run(() => {\n const result = render(dataSignal, indexSignal);\n\n nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n });\n\n return { cleanups, data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeySet = new Set(nextKeys);\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass — move any item not already adjacent to cursor.\n // An item is in place when its last node directly precedes the cursor; items may\n // span several nodes (whitespace text around an element), so compare the last one.\n // Moving an already-placed item would disconnect and reconnect its custom elements.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const lastNode = entry.nodes[entry.nodes.length - 1];\n\n if (firstNode && lastNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index — an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function — an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log — listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker));\n } catch (error) {\n // Rendering may already have changed the live map, so clear it to avoid retaining partial DOM.\n reportEachError(error, anchor);\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"each.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { type MaybeReactive, removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = MaybeReactive<T[]>;\ntype ItemKey = number | string;\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n const cleanups: (() => void)[] = [];\n let nodes: Node[] = [];\n\n scope.run(() => {\n const result = render(dataSignal, indexSignal);\n\n nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n });\n\n return { cleanups, data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeySet = new Set(nextKeys);\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass: move any item not already adjacent to cursor.\n // An item is in place when its last node directly precedes the cursor; items may\n // span several nodes (whitespace text around an element), so compare the last one.\n // Moving an already-placed item would disconnect and reconnect its custom elements.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const lastNode = entry.nodes[entry.nodes.length - 1];\n\n if (firstNode && lastNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index: an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function: an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log: listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker));\n } catch (error) {\n // Rendering may already have changed the live map, so clear it to avoid retaining partial DOM.\n reportEachError(error, anchor);\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
@@ -22,16 +22,16 @@ type MaybeReactiveArray<T> = MaybeReactive<T[]>;
22
22
  * **Optional fallback:** the fourth argument renders when the list is empty.
23
23
  *
24
24
  * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the
25
- * array index — an index-based key reassigns to a different item whenever the
25
+ * array index: an index-based key reassigns to a different item whenever the
26
26
  * list is reordered or an item is inserted/removed before it, causing full
27
27
  * item teardown/recreation instead of the in-place update `each()` is built
28
28
  * for.
29
29
  *
30
30
  * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)
31
- * does not throw past this function — an uncaught exception inside the reactive effect that
31
+ * does not throw past this function: an uncaught exception inside the reactive effect that
32
32
  * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.
33
33
  * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event
34
- * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log — listen for
34
+ * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log: listen for
35
35
  * `ore:error` on `document`/`window` to observe this in every build, including production.
36
36
  */
37
37
  export declare function each<T>(list: MaybeReactiveArray<T>, keyFn: (item: T, index: number) => string | number, render: (item: Readable<T>, index: Readable<number>) => HTMLResult, fallback?: () => HTMLResult): DirectiveResult;
@@ -1 +1 @@
1
- {"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { type MaybeReactive, removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = MaybeReactive<T[]>;\ntype ItemKey = number | string;\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n const cleanups: (() => void)[] = [];\n let nodes: Node[] = [];\n\n scope.run(() => {\n const result = render(dataSignal, indexSignal);\n\n nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n });\n\n return { cleanups, data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeySet = new Set(nextKeys);\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass — move any item not already adjacent to cursor.\n // An item is in place when its last node directly precedes the cursor; items may\n // span several nodes (whitespace text around an element), so compare the last one.\n // Moving an already-placed item would disconnect and reconnect its custom elements.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const lastNode = entry.nodes[entry.nodes.length - 1];\n\n if (firstNode && lastNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index — an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function — an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log — listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker));\n } catch (error) {\n // Rendering may already have changed the live map, so clear it to avoid retaining partial DOM.\n reportEachError(error, anchor);\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"8VA+BA,IAAM,GACJ,EACA,EACA,EACA,EACA,IACiB,CACjB,IAAM,EAAwB,EAAO,CAAI,EACnC,EAA8B,EAAO,CAAK,EAC1C,EAAQ,EAAY,EACpB,EAA2B,CAAC,EAC9B,EAAgB,CAAC,EAQrB,OANA,EAAM,QAAU,CAGd,EAFe,EAAO,EAAY,CAE1B,CAAA,CAAO,MAAM,EAAQ,EAAe,GAAO,EAAS,KAAK,CAAE,CAAC,CACtE,CAAC,EAEM,CAAE,WAAU,KAAM,EAAY,MAAO,EAAa,QAAO,OAAM,CACxE,EAEM,EAAiB,GAA8B,CACnD,EAAM,MAAM,QAAQ,EACpB,EAAO,EAAM,QAAQ,EACrB,EAAY,EAAM,KAAK,CACzB,EAUM,GACJ,EACA,EACA,EACA,EACA,EACA,IACmB,CACnB,IAAM,EAAa,IAAI,IAAI,CAAQ,EAGnC,IAAK,GAAM,CAAC,EAAK,KAAU,EACpB,EAAW,IAAI,CAAG,IACrB,EAAW,CAAK,EAChB,EAAS,OAAO,CAAG,GAKvB,IAAM,EAA8B,CAAC,EAErC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAM,EAAS,GACf,EAAW,EAAS,IAAI,CAAG,EAEjC,GAAI,EACF,MAAY,CACV,EAAS,KAAK,MAAQ,EAAK,GAC3B,EAAS,MAAM,MAAQ,CACzB,CAAC,EACD,EAAY,KAAK,CAAQ,MACpB,CACL,IAAM,EAAQ,MAAc,EAAW,EAAK,GAAI,EAAG,EAAQ,EAAQ,CAAS,CAAC,EAE7E,EAAS,IAAI,EAAK,CAAK,EACvB,EAAY,KAAK,CAAK,CACxB,CACF,CAQA,IAAI,EAAe,EAEnB,IAAK,IAAI,EAAI,EAAY,OAAS,EAAG,GAAK,EAAG,IAAK,CAChD,IAAM,EAAQ,EAAY,GACpB,EAAY,EAAM,MAAM,GACxB,EAAW,EAAM,MAAM,EAAM,MAAM,OAAS,GAElD,GAAI,GAAa,IAAa,EAAO,gBACnC,IAAK,IAAM,KAAQ,EAAM,MAAO,EAAO,aAAa,EAAM,CAAM,EAGlE,EAAS,GAAa,CACxB,CAEA,OAAO,CACT,EAEM,GAAkB,EAAW,IAA0D,CAC3F,IAAM,EAAkB,CAAC,EACnB,EAAO,IAAI,IAEjB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAM,EAAM,EAAK,GAAI,CAAC,EAE5B,GAAI,EAAK,IAAI,CAAG,EAAG,MAAM,IAAI,EAAY,EAAW,iBAAiB,OAAO,CAAG,EAAG,CAAC,CAAC,EAEpF,EAAK,IAAI,CAAG,EACZ,EAAK,KAAK,CAAG,CACf,CAEA,OAAO,CACT,EAEM,GAAmB,EAAgB,IAA0B,CACjE,IAAM,EAAQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEtE,EACE,IAAI,EAAkB,6CAA6C,EAAM,UAAW,CAClF,QACA,UAAW,SACX,MAAO,gBACT,CAAC,EACD,CACF,CACF,EAoCA,SAAgB,EACd,EACA,EACA,EACA,EACiB,CACjB,IAAM,EAAa,MAAM,QAAQ,CAAI,EACjC,EAAO,CAAW,EAClB,OAAO,GAAS,WACd,EAAS,CAAiB,EAC1B,EAEN,OAAO,GAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAO,WAEtB,EAAU,EAAQ,0CAA0C,EAE5D,IAAM,EAAY,SAAS,cAAc,UAAU,EAEnD,EAAO,aAAa,EAAW,EAAO,WAAW,EAEjD,IAAI,EAAW,IAAI,IACf,EAA+B,CAAC,EAChC,EAA+B,KAC/B,EAAmC,CAAC,EAElC,MAA4B,CAC3B,IAIL,EAFe,EAEC,CAAA,CAAO,MAAM,EAAQ,EAAY,GAAO,EAAiB,KAAK,CAAE,CAAC,EACnF,EAEM,MAA4B,CAC5B,IACF,EAAO,CAAgB,EACvB,EAAY,CAAa,EACzB,EAAgB,KAChB,EAAmB,CAAC,EAExB,EAEM,EAAM,MAAgB,CAC1B,IAAM,EAAW,EAAW,OAAS,CAAC,EAEtC,GAAI,EAAS,SAAW,EAAG,CACzB,IAAK,IAAM,KAAS,MAAc,CAAY,EAAG,EAAW,CAAK,EACjE,EAAW,IAAI,IACf,EAAe,CAAC,EAEX,GAAe,EAAQ,CAAa,EAEzC,MACF,CAEA,IAAI,EAEJ,GAAI,CACF,EAAW,MAAc,EAAY,EAAU,CAAK,CAAC,CACvD,OAAS,EAAO,CACd,EAAgB,EAAO,CAAM,EAC7B,MACF,CAEA,EAAc,EAEd,GAAI,CACF,EAAe,MAAc,EAAe,EAAU,EAAU,EAAU,EAAQ,EAAQ,CAAS,CAAC,CACtG,OAAS,EAAO,CAEd,EAAgB,EAAO,CAAM,EAC7B,IAAK,IAAM,KAAS,EAAS,OAAO,EAAG,EAAW,CAAK,EACvD,EAAW,IAAI,IACf,EAAe,CAAC,CAClB,CACF,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EACnC,MAAsB,CACpB,EAAc,EACd,IAAK,IAAM,KAAS,EAAc,EAAW,CAAK,EAClD,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH"}
1
+ {"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { type MaybeReactive, removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = MaybeReactive<T[]>;\ntype ItemKey = number | string;\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n const cleanups: (() => void)[] = [];\n let nodes: Node[] = [];\n\n scope.run(() => {\n const result = render(dataSignal, indexSignal);\n\n nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n });\n\n return { cleanups, data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeySet = new Set(nextKeys);\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass: move any item not already adjacent to cursor.\n // An item is in place when its last node directly precedes the cursor; items may\n // span several nodes (whitespace text around an element), so compare the last one.\n // Moving an already-placed item would disconnect and reconnect its custom elements.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const lastNode = entry.nodes[entry.nodes.length - 1];\n\n if (firstNode && lastNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index: an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function: an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log: listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker));\n } catch (error) {\n // Rendering may already have changed the live map, so clear it to avoid retaining partial DOM.\n reportEachError(error, anchor);\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal — the\n * \"live\" flag belongs to this one binding site, never to the signal itself,\n * so other bindings of the same signal are unaffected.\n *\n * @example\n * html`<input value=\"${live(model)}\" />`\n */\nexport type LiveBinding<T> = { readonly source: Readable<T> };\n\nconst liveBrand = makeBrand<LiveBinding<unknown>>('ore:live');\n\n/**\n * Marks one attribute binding as \"live\" so stale app-state writes never clobber\n * in-progress user input at that binding site.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveBinding<T> => liveBrand.stamp({ source }) as LiveBinding<T>;\n\nexport const isLiveBinding = liveBrand.is;\n"],"mappings":"AAcA,IAAM,8BAAY,CAAA,CAAA,UAAgC,UAAU,EAU/C,EAAW,GAAwC,EAAU,MAAM,CAAE,QAAO,CAAC,EAE7E,EAAgB,EAAU"}
1
+ {"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal: the\n * \"live\" flag belongs to this one binding site, never to the signal itself,\n * so other bindings of the same signal are unaffected.\n *\n * @example\n * html`<input value=\"${live(model)}\" />`\n */\nexport type LiveBinding<T> = { readonly source: Readable<T> };\n\nconst liveBrand = makeBrand<LiveBinding<unknown>>('ore:live');\n\n/**\n * Marks one attribute binding as \"live\" so stale app-state writes never clobber\n * in-progress user input at that binding site.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveBinding<T> => liveBrand.stamp({ source }) as LiveBinding<T>;\n\nexport const isLiveBinding = liveBrand.is;\n"],"mappings":"AAcA,IAAM,8BAAY,CAAA,CAAA,UAAgC,UAAU,EAU/C,EAAW,GAAwC,EAAU,MAAM,CAAE,QAAO,CAAC,EAE7E,EAAgB,EAAU"}
@@ -1,6 +1,6 @@
1
1
  import type { Readable } from '@vielzeug/ripple';
2
2
  /**
3
- * A per-binding marker produced by `live()`. Wraps the source signal — the
3
+ * A per-binding marker produced by `live()`. Wraps the source signal: the
4
4
  * "live" flag belongs to this one binding site, never to the signal itself,
5
5
  * so other bindings of the same signal are unaffected.
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal — the\n * \"live\" flag belongs to this one binding site, never to the signal itself,\n * so other bindings of the same signal are unaffected.\n *\n * @example\n * html`<input value=\"${live(model)}\" />`\n */\nexport type LiveBinding<T> = { readonly source: Readable<T> };\n\nconst liveBrand = makeBrand<LiveBinding<unknown>>('ore:live');\n\n/**\n * Marks one attribute binding as \"live\" so stale app-state writes never clobber\n * in-progress user input at that binding site.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveBinding<T> => liveBrand.stamp({ source }) as LiveBinding<T>;\n\nexport const isLiveBinding = liveBrand.is;\n"],"mappings":"8CAcA,IAAM,EAAY,EAAgC,UAAU,EAU/C,EAAW,GAAwC,EAAU,MAAM,CAAE,QAAO,CAAC,EAE7E,EAAgB,EAAU"}
1
+ {"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal: the\n * \"live\" flag belongs to this one binding site, never to the signal itself,\n * so other bindings of the same signal are unaffected.\n *\n * @example\n * html`<input value=\"${live(model)}\" />`\n */\nexport type LiveBinding<T> = { readonly source: Readable<T> };\n\nconst liveBrand = makeBrand<LiveBinding<unknown>>('ore:live');\n\n/**\n * Marks one attribute binding as \"live\" so stale app-state writes never clobber\n * in-progress user input at that binding site.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveBinding<T> => liveBrand.stamp({ source }) as LiveBinding<T>;\n\nexport const isLiveBinding = liveBrand.is;\n"],"mappings":"8CAcA,IAAM,EAAY,EAAgC,UAAU,EAU/C,EAAW,GAAwC,EAAU,MAAM,CAAE,QAAO,CAAC,EAE7E,EAAgB,EAAU"}
package/dist/errors.cjs CHANGED
@@ -1,3 +1,3 @@
1
- require("./_dev.cjs");var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice — custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifiersUnsupported:e=>`@${e}: event modifiers are unsupported; call native event methods in the handler instead`,injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}" — listener not attached`,propInvalidReflect:`Structured props cannot use reflect:true — use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
1
+ require("./_dev.cjs");var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice: custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifiersUnsupported:e=>`@${e}: event modifiers are unsupported; call native event methods in the handler instead`,injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}": listener not attached`,propInvalidReflect:`Structured props cannot use reflect:true: use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
2
2
  `)}`};function o(e,t){if(!e)throw new n(a.invariantViolated(t))}exports.ORE_ERRORS=a,exports.OreApiError=t,exports.OreError=e,exports.OreInternalError=n,exports.OreLifecycleError=r,exports.invariant=o,exports.reportRuntimeError=i;
3
3
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package — decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails — e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'` — synchronous setup() threw\n * - `'mounted'` — an onMounted callback threw\n * - `'form-reset'` — an onFormReset callback threw\n * - `'each-reconcile'` — `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`) — component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not** — it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice — custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifiersUnsupported: (eventName: string): string =>\n `@${eventName}: event modifiers are unsupported; call native event methods in the handler instead`,\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\" — listener not attached`,\n propInvalidReflect: 'Structured props cannot use reflect:true — use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold — e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first — `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"sBAoBA,IAAa,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAiC,CAAS,CAAC,EAO9B,EAAb,cAAsC,CAAS,CAAC,EAenC,EAAb,cAAuC,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAM,EAAS,CAAE,MAAO,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAY,EAAQ,UACzB,KAAK,MAAQ,EAAQ,KACvB,CACF,EAiBA,SAAgB,EAAmB,EAA0B,EAA2B,CAC7E,GAAI,EAAM,UAAV,EAAgD,EAAM,MAAtD,EAAiE,EAAM,MAEhF,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQ,CACV,CAAC,CACH,CACF,CAIA,IAAa,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAW,EAAI,qDACzD,kCAAoC,GAClC,+BAA+B,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkC,EAAI,aAAa,IAC7G,0BAA4B,GAC1B,IAAI,EAAU,qFAChB,oBAAqB,EAAa,IAAwB,yCAAyC,EAAI,QAAQ,EAAI,GACnH,kBAAoB,GAA4B,uBAAuB,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2D,EAAU,2BACvE,mBAAoB,gFACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqC,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0B,EAAI,MAAM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgB,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAAC,EAAW,MAAM,IAAI,EAAiB,EAAW,kBAAkB,CAAO,CAAC,CAClF"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package: decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails: e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'`: synchronous setup() threw\n * - `'mounted'`: an onMounted callback threw\n * - `'form-reset'`: an onFormReset callback threw\n * - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice: custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifiersUnsupported: (eventName: string): string =>\n `@${eventName}: event modifiers are unsupported; call native event methods in the handler instead`,\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\": listener not attached`,\n propInvalidReflect: 'Structured props cannot use reflect:true: use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold: e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"sBAoBA,IAAa,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAiC,CAAS,CAAC,EAO9B,EAAb,cAAsC,CAAS,CAAC,EAenC,EAAb,cAAuC,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAM,EAAS,CAAE,MAAO,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAY,EAAQ,UACzB,KAAK,MAAQ,EAAQ,KACvB,CACF,EAiBA,SAAgB,EAAmB,EAA0B,EAA2B,CAC7E,GAAI,EAAM,UAAV,EAAgD,EAAM,MAAtD,EAAiE,EAAM,MAEhF,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQ,CACV,CAAC,CACH,CACF,CAIA,IAAa,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAW,EAAI,oDACzD,kCAAoC,GAClC,+BAA+B,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkC,EAAI,aAAa,IAC7G,0BAA4B,GAC1B,IAAI,EAAU,qFAChB,oBAAqB,EAAa,IAAwB,yCAAyC,EAAI,QAAQ,EAAI,GACnH,kBAAoB,GAA4B,uBAAuB,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2D,EAAU,0BACvE,mBAAoB,+EACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqC,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0B,EAAI,MAAM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgB,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAAC,EAAW,MAAM,IAAI,EAAiB,EAAW,kBAAkB,CAAO,CAAC,CAClF"}