@vielzeug/ore 1.2.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -8
- package/dist/_component-class.cjs +2 -0
- package/dist/_component-class.cjs.map +1 -0
- package/dist/_component-class.d.ts +2 -0
- package/dist/_component-class.d.ts.map +1 -0
- package/dist/_component-class.js +2 -0
- package/dist/_component-class.js.map +1 -0
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +1 -1
- package/dist/_dev.js.map +1 -1
- package/dist/base-element.cjs +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.d.ts +9 -15
- package/dist/base-element.d.ts.map +1 -1
- package/dist/base-element.js +1 -1
- package/dist/base-element.js.map +1 -1
- package/dist/component-types.d.ts +3 -14
- package/dist/component-types.d.ts.map +1 -1
- package/dist/context.cjs +1 -1
- package/dist/context.cjs.map +1 -1
- package/dist/context.d.ts +13 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/define.cjs +1 -1
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.ts +6 -5
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +1 -1
- package/dist/define.js.map +1 -1
- package/dist/directives/classMap.cjs +1 -1
- package/dist/directives/classMap.cjs.map +1 -1
- package/dist/directives/classMap.d.ts.map +1 -1
- package/dist/directives/classMap.js +1 -1
- package/dist/directives/classMap.js.map +1 -1
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +9 -2
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/live.cjs +1 -1
- package/dist/directives/live.cjs.map +1 -1
- package/dist/directives/live.d.ts +10 -12
- package/dist/directives/live.d.ts.map +1 -1
- package/dist/directives/live.js +1 -1
- package/dist/directives/live.js.map +1 -1
- package/dist/directives/styleMap.cjs +1 -1
- package/dist/directives/styleMap.cjs.map +1 -1
- package/dist/directives/styleMap.d.ts +1 -1
- package/dist/directives/styleMap.d.ts.map +1 -1
- package/dist/directives/styleMap.js +1 -1
- package/dist/directives/styleMap.js.map +1 -1
- package/dist/directives/unsafe-html.cjs +2 -0
- package/dist/directives/unsafe-html.cjs.map +1 -0
- package/dist/directives/unsafe-html.d.ts +14 -0
- package/dist/directives/unsafe-html.d.ts.map +1 -0
- package/dist/directives/unsafe-html.js +2 -0
- package/dist/directives/unsafe-html.js.map +1 -0
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts +1 -1
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs +2 -2
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +21 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/forms/field.cjs +1 -1
- package/dist/forms/field.cjs.map +1 -1
- package/dist/forms/field.d.ts +5 -1
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +1 -1
- package/dist/forms/field.js.map +1 -1
- package/dist/host-bind.cjs +1 -1
- package/dist/host-bind.cjs.map +1 -1
- package/dist/host-bind.d.ts +6 -0
- package/dist/host-bind.d.ts.map +1 -1
- package/dist/host-bind.js +1 -1
- package/dist/host-bind.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/observers/intersection-observe.cjs +1 -1
- package/dist/observers/intersection-observe.cjs.map +1 -1
- package/dist/observers/intersection-observe.d.ts.map +1 -1
- package/dist/observers/intersection-observe.js +1 -1
- package/dist/observers/intersection-observe.js.map +1 -1
- package/dist/observers/media-observe.cjs +1 -1
- package/dist/observers/media-observe.cjs.map +1 -1
- package/dist/observers/media-observe.d.ts.map +1 -1
- package/dist/observers/media-observe.js +1 -1
- package/dist/observers/media-observe.js.map +1 -1
- package/dist/observers/mutation-observe.cjs +1 -1
- package/dist/observers/mutation-observe.cjs.map +1 -1
- package/dist/observers/mutation-observe.d.ts.map +1 -1
- package/dist/observers/mutation-observe.js +1 -1
- package/dist/observers/mutation-observe.js.map +1 -1
- package/dist/observers/resize-observe.cjs +1 -1
- package/dist/observers/resize-observe.cjs.map +1 -1
- package/dist/observers/resize-observe.d.ts.map +1 -1
- package/dist/observers/resize-observe.js +1 -1
- package/dist/observers/resize-observe.js.map +1 -1
- package/dist/ore.cjs +2 -2
- package/dist/ore.cjs.map +1 -1
- package/dist/ore.iife.js +2 -2
- package/dist/ore.iife.js.map +1 -1
- package/dist/ore.js +2 -2
- package/dist/ore.js.map +1 -1
- package/dist/props.cjs +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts +1 -6
- package/dist/props.d.ts.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +7 -10
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/slots.cjs +1 -1
- package/dist/slots.cjs.map +1 -1
- package/dist/slots.d.ts.map +1 -1
- package/dist/slots.js +1 -1
- package/dist/slots.js.map +1 -1
- package/dist/template/binding-types.cjs +1 -0
- package/dist/template/binding-types.d.ts +59 -0
- package/dist/template/binding-types.d.ts.map +1 -0
- package/dist/template/binding-types.js +1 -0
- package/dist/template/bindings.cjs +1 -1
- package/dist/template/bindings.cjs.map +1 -1
- package/dist/template/bindings.d.ts +18 -3
- package/dist/template/bindings.d.ts.map +1 -1
- package/dist/template/bindings.js +1 -1
- package/dist/template/bindings.js.map +1 -1
- package/dist/template/compiler.cjs +1 -1
- package/dist/template/compiler.cjs.map +1 -1
- package/dist/template/compiler.d.ts +0 -4
- package/dist/template/compiler.d.ts.map +1 -1
- package/dist/template/compiler.js +1 -1
- package/dist/template/compiler.js.map +1 -1
- package/dist/template/instantiator.cjs +1 -1
- package/dist/template/instantiator.cjs.map +1 -1
- package/dist/template/instantiator.d.ts +6 -1
- package/dist/template/instantiator.d.ts.map +1 -1
- package/dist/template/instantiator.js +1 -1
- package/dist/template/instantiator.js.map +1 -1
- package/dist/template/result.cjs +2 -0
- package/dist/template/result.cjs.map +1 -0
- package/dist/template/result.d.ts +42 -0
- package/dist/template/result.d.ts.map +1 -0
- package/dist/template/result.js +2 -0
- package/dist/template/result.js.map +1 -0
- package/dist/testing/flush.cjs +1 -1
- package/dist/testing/flush.cjs.map +1 -1
- package/dist/testing/flush.d.ts +30 -26
- package/dist/testing/flush.d.ts.map +1 -1
- package/dist/testing/flush.js +1 -1
- package/dist/testing/flush.js.map +1 -1
- package/dist/testing/form-internals-polyfill.cjs +1 -1
- package/dist/testing/form-internals-polyfill.cjs.map +1 -1
- package/dist/testing/form-internals-polyfill.d.ts +9 -7
- package/dist/testing/form-internals-polyfill.d.ts.map +1 -1
- package/dist/testing/form-internals-polyfill.js +1 -1
- package/dist/testing/form-internals-polyfill.js.map +1 -1
- package/dist/testing/index.d.ts +35 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/mount.cjs +1 -1
- package/dist/testing/mount.cjs.map +1 -1
- package/dist/testing/mount.d.ts +10 -19
- package/dist/testing/mount.d.ts.map +1 -1
- package/dist/testing/mount.js +1 -1
- package/dist/testing/mount.js.map +1 -1
- package/dist/testing/render-hook.cjs +1 -1
- package/dist/testing/render-hook.cjs.map +1 -1
- package/dist/testing/render-hook.js +1 -1
- package/dist/testing/render-hook.js.map +1 -1
- package/dist/testing/reset.cjs +2 -0
- package/dist/testing/reset.cjs.map +1 -0
- package/dist/testing/reset.d.ts +12 -0
- package/dist/testing/reset.d.ts.map +1 -0
- package/dist/testing/reset.js +2 -0
- package/dist/testing/reset.js.map +1 -0
- package/dist/testing.cjs +2 -1
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.js +2 -1
- package/dist/testing.js.map +1 -0
- package/dist/utils/aria.cjs.map +1 -1
- package/dist/utils/aria.d.ts +1 -1
- package/dist/utils/aria.js.map +1 -1
- package/dist/utils/brand.cjs +2 -0
- package/dist/utils/brand.cjs.map +1 -0
- package/dist/utils/brand.d.ts +16 -0
- package/dist/utils/brand.d.ts.map +1 -0
- package/dist/utils/brand.js +2 -0
- package/dist/utils/brand.js.map +1 -0
- package/dist/utils/css.cjs +1 -1
- package/dist/utils/css.cjs.map +1 -1
- package/dist/utils/css.d.ts.map +1 -1
- package/dist/utils/css.js +1 -1
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/dom.cjs +1 -1
- package/dist/utils/dom.cjs.map +1 -1
- package/dist/utils/dom.d.ts +34 -0
- package/dist/utils/dom.d.ts.map +1 -1
- package/dist/utils/dom.js +1 -1
- package/dist/utils/dom.js.map +1 -1
- package/dist/utils/emit.cjs.map +1 -1
- package/dist/utils/emit.d.ts +10 -3
- package/dist/utils/emit.d.ts.map +1 -1
- package/dist/utils/emit.js.map +1 -1
- package/dist/utils/id.cjs +1 -1
- package/dist/utils/id.cjs.map +1 -1
- package/dist/utils/id.d.ts +11 -2
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +1 -1
- package/dist/utils/id.js.map +1 -1
- package/package.json +15 -30
- package/dist/aria.cjs +0 -2
- package/dist/aria.cjs.map +0 -1
- package/dist/aria.d.ts +0 -20
- package/dist/aria.d.ts.map +0 -1
- package/dist/aria.js +0 -2
- package/dist/aria.js.map +0 -1
- package/dist/devtools.cjs +0 -2
- package/dist/devtools.cjs.map +0 -1
- package/dist/devtools.d.ts +0 -32
- package/dist/devtools.d.ts.map +0 -1
- package/dist/devtools.js +0 -2
- package/dist/devtools.js.map +0 -1
- package/dist/directives/index.d.ts +0 -8
- package/dist/directives/index.d.ts.map +0 -1
- package/dist/directives/model.cjs +0 -2
- package/dist/directives/model.cjs.map +0 -1
- package/dist/directives/model.d.ts +0 -28
- package/dist/directives/model.d.ts.map +0 -1
- package/dist/directives/model.js +0 -2
- package/dist/directives/model.js.map +0 -1
- package/dist/directives/raw.cjs +0 -2
- package/dist/directives/raw.cjs.map +0 -1
- package/dist/directives/raw.d.ts +0 -16
- package/dist/directives/raw.d.ts.map +0 -1
- package/dist/directives/raw.js +0 -2
- package/dist/directives/raw.js.map +0 -1
- package/dist/directives.cjs +0 -1
- package/dist/directives.js +0 -1
- package/dist/forms/context.cjs +0 -2
- package/dist/forms/context.cjs.map +0 -1
- package/dist/forms/context.d.ts +0 -56
- package/dist/forms/context.d.ts.map +0 -1
- package/dist/forms/context.js +0 -2
- package/dist/forms/context.js.map +0 -1
- package/dist/forms/index.d.ts +0 -11
- package/dist/forms/index.d.ts.map +0 -1
- package/dist/forms.cjs +0 -1
- package/dist/forms.js +0 -1
- package/dist/observers/index.d.ts +0 -5
- package/dist/observers/index.d.ts.map +0 -1
- package/dist/observers.cjs +0 -1
- package/dist/observers.js +0 -1
- package/dist/template.cjs +0 -1
- package/dist/template.d.ts +0 -4
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -1
- package/dist/testing/events.cjs +0 -2
- package/dist/testing/events.cjs.map +0 -1
- package/dist/testing/events.d.ts +0 -37
- package/dist/testing/events.d.ts.map +0 -1
- package/dist/testing/events.js +0 -2
- package/dist/testing/events.js.map +0 -1
- package/dist/testing/interactions.cjs +0 -2
- package/dist/testing/interactions.cjs.map +0 -1
- package/dist/testing/interactions.d.ts +0 -27
- package/dist/testing/interactions.d.ts.map +0 -1
- package/dist/testing/interactions.js +0 -2
- package/dist/testing/interactions.js.map +0 -1
- package/dist/testing/query.cjs +0 -2
- package/dist/testing/query.cjs.map +0 -1
- package/dist/testing/query.d.ts +0 -24
- package/dist/testing/query.d.ts.map +0 -1
- package/dist/testing/query.js +0 -2
- package/dist/testing/query.js.map +0 -1
- package/dist/testing/testing.cjs +0 -2
- package/dist/testing/testing.cjs.map +0 -1
- package/dist/testing/testing.d.ts +0 -32
- package/dist/testing/testing.d.ts.map +0 -1
- package/dist/testing/testing.js +0 -2
- package/dist/testing/testing.js.map +0 -1
- package/dist/testing/wait.cjs +0 -2
- package/dist/testing/wait.cjs.map +0 -1
- package/dist/testing/wait.d.ts +0 -35
- package/dist/testing/wait.d.ts.map +0 -1
- package/dist/testing/wait.js +0 -2
- package/dist/testing/wait.js.map +0 -1
- package/dist/types/bindings.cjs +0 -2
- package/dist/types/bindings.cjs.map +0 -1
- package/dist/types/bindings.d.ts +0 -125
- package/dist/types/bindings.d.ts.map +0 -1
- package/dist/types/bindings.js +0 -2
- package/dist/types/bindings.js.map +0 -1
- package/dist/types.cjs +0 -2
- package/dist/types.cjs.map +0 -1
- package/dist/types.d.ts +0 -19
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/utils/event-modifiers.cjs +0 -2
- package/dist/utils/event-modifiers.cjs.map +0 -1
- package/dist/utils/event-modifiers.d.ts +0 -11
- package/dist/utils/event-modifiers.d.ts.map +0 -1
- package/dist/utils/event-modifiers.js +0 -2
- package/dist/utils/event-modifiers.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
**Package:** `@vielzeug/ore` · **Category:** UI Primitives
|
|
11
11
|
|
|
12
|
-
**Key exports:** `define`, `html`, `css`, `prop`, `ref`, `onMounted`, `onCleanup`, `onEvent`, `onElement`, `
|
|
12
|
+
**Key exports:** `define`, `html`, `css`, `prop`, `ref`, `onMounted`, `onCleanup`, `onEvent`, `onElement`, `watchEffect`, `bind`, `provide`, `inject`, `useEmit`, `useSlots`, `getHost`, `each`, `when`, `live`, `unsafeHtml`, `useField`
|
|
13
13
|
|
|
14
14
|
**When to use:** Functional custom-element authoring with typed props, reactive templates, lifecycle helpers, observers, and testing utilities.
|
|
15
15
|
|
|
@@ -17,14 +17,15 @@
|
|
|
17
17
|
|
|
18
18
|
</details>
|
|
19
19
|
|
|
20
|
-
`@vielzeug/ore` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
|
|
20
|
+
`@vielzeug/ore` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output. Install
|
|
21
|
+
`@vielzeug/ripple` alongside it: Ore uses the application's Ripple runtime as a peer dependency.
|
|
21
22
|
|
|
22
23
|
## Installation
|
|
23
24
|
|
|
24
25
|
```sh
|
|
25
|
-
pnpm add @vielzeug/ore
|
|
26
|
-
npm install @vielzeug/ore
|
|
27
|
-
yarn add @vielzeug/ore
|
|
26
|
+
pnpm add @vielzeug/ore @vielzeug/ripple
|
|
27
|
+
npm install @vielzeug/ore @vielzeug/ripple
|
|
28
|
+
yarn add @vielzeug/ore @vielzeug/ripple
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
## Quick Start
|
|
@@ -62,7 +63,7 @@ define('my-counter', {
|
|
|
62
63
|
});
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
`setup()` takes only `props`. Everything else — lifecycle hooks (`onMounted`, `onCleanup`, `onEvent`, `onElement`, `
|
|
66
|
+
`setup()` takes only `props`. Everything else — lifecycle hooks (`onMounted`, `onCleanup`, `onEvent`, `onElement`, `watchEffect`), host bindings (`bind`), context (`inject`/`injectStrict`/`provide`), and per-instance factories (`useEmit<Emits>()`, `useSlots<SlotNames>()`) — is imported from `@vielzeug/ore` and resolved while `setup()` (or a composable it calls) is running. Setup state is connection-scoped: disconnect disposes it and reconnect runs setup again.
|
|
66
67
|
|
|
67
68
|
## Testing
|
|
68
69
|
|
|
@@ -89,10 +90,12 @@ Register `cleanup` once so mounted elements don't leak between tests:
|
|
|
89
90
|
import { afterEach } from 'vitest';
|
|
90
91
|
import { install } from '@vielzeug/ore/testing';
|
|
91
92
|
|
|
92
|
-
install(afterEach);
|
|
93
|
+
install(afterEach); // add { formInternals: true } when testing form-associated (useField) components
|
|
93
94
|
```
|
|
94
95
|
|
|
95
|
-
Also available from `@vielzeug/ore/testing`: `renderHook` (test a composable in isolation, without a full template),
|
|
96
|
+
Also available from `@vielzeug/ore/testing`: `renderHook` (test a composable in isolation, without a full template),
|
|
97
|
+
`flush`, and form-internals support. Import generic DOM interactions, queries, and waiting helpers from
|
|
98
|
+
`@vielzeug/assay`. See the [Usage Guide](https://vielzeug.dev/ore/usage) for the full API.
|
|
96
99
|
|
|
97
100
|
## Documentation
|
|
98
101
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./utils/dom.cjs"),n=require("./props.cjs"),r=require("./base-element.cjs");function i(i,a){let{props:o}=a,s=(()=>{if(!o)return;let t=n.validatePropDefs(o);if(t.length>0)throw new e.OreApiError(e.ORE_ERRORS.validationFailed(i,t));let r={};for(let[e,t]of Object.entries(o))r[e]=n.normalizePropDefinition(t,e);return r})(),c=s?Object.keys(s).map(t.toKebab):[];return class extends r.BaseElement{static _definition=a;static _normalizedPropDefs=s;static formAssociated=a.formAssociated??!1;static observedAttributes=c}}exports.createComponentClass=i;
|
|
2
|
+
//# sourceMappingURL=_component-class.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_component-class.cjs","names":[],"sources":["../src/_component-class.ts"],"sourcesContent":["import type { ComponentDefinition } from './component-types';\n\nimport { BaseElement } from './base-element';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { normalizePropDefinition, type PropInputDefs, type PropsDef, validatePropDefs } from './props';\nimport { toKebab } from './utils/dom';\n\n/**\n * Builds the element class without touching the browser registry. Keeping\n * declaration work separate from registration makes the latter the only\n * irreversible global side effect in the component-definition path.\n *\n * @internal\n */\nexport function createComponentClass<Props extends Record<string, unknown>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): CustomElementConstructor {\n const { props: propDefs } = definition;\n\n const normalizedPropDefs: PropsDef<Props> | undefined = (() => {\n if (!propDefs) return undefined;\n\n const errors = validatePropDefs(propDefs as Record<string, unknown>);\n\n if (errors.length > 0) throw new OreApiError(ORE_ERRORS.validationFailed(tag, errors));\n\n const normalized: PropInputDefs = {};\n\n for (const [key, def] of Object.entries(propDefs)) {\n normalized[key] = normalizePropDefinition(def, key);\n }\n\n return normalized as PropsDef<Props>;\n })();\n\n const observedAttrs = normalizedPropDefs ? Object.keys(normalizedPropDefs).map(toKebab) : [];\n\n const ComponentClass = class extends BaseElement {\n static override _definition = definition as unknown as ComponentDefinition;\n static override _normalizedPropDefs = normalizedPropDefs as PropsDef<Record<never, never>> | undefined;\n static override formAssociated = definition.formAssociated ?? false;\n static override observedAttributes = observedAttrs;\n };\n\n return ComponentClass;\n}\n"],"mappings":"sHAcA,SAAgB,EACd,EACA,EAC0B,CAC1B,GAAM,CAAE,MAAO,GAAa,EAEtB,OAAyD,CAC7D,GAAI,CAAC,EAAU,OAEf,IAAM,EAAS,EAAA,iBAAiB,CAAmC,EAEnE,GAAI,EAAO,OAAS,EAAG,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,iBAAiB,EAAK,CAAM,CAAC,EAErF,IAAM,EAA4B,CAAC,EAEnC,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,CAAQ,EAC9C,EAAW,GAAO,EAAA,wBAAwB,EAAK,CAAG,EAGpD,OAAO,CACT,EAAA,CAAG,EAEG,EAAgB,EAAqB,OAAO,KAAK,CAAkB,CAAC,CAAC,IAAI,EAAA,OAAO,EAAI,CAAC,EAS3F,OAAO,cAP8B,EAAA,WAAY,CAC/C,OAAgB,YAAc,EAC9B,OAAgB,oBAAsB,EACtC,OAAgB,eAAiB,EAAW,gBAAkB,GAC9D,OAAgB,mBAAqB,CACvC,CAGF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_component-class.d.ts","sourceRoot":"","sources":["../src/_component-class.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{ORE_ERRORS as e,OreApiError as t}from"./errors.js";import{toKebab as n}from"./utils/dom.js";import{normalizePropDefinition as r,validatePropDefs as i}from"./props.js";import{BaseElement as a}from"./base-element.js";function o(o,s){let{props:c}=s,l=(()=>{if(!c)return;let n=i(c);if(n.length>0)throw new t(e.validationFailed(o,n));let a={};for(let[e,t]of Object.entries(c))a[e]=r(t,e);return a})(),u=l?Object.keys(l).map(n):[];return class extends a{static _definition=s;static _normalizedPropDefs=l;static formAssociated=s.formAssociated??!1;static observedAttributes=u}}export{o as createComponentClass};
|
|
2
|
+
//# sourceMappingURL=_component-class.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_component-class.js","names":[],"sources":["../src/_component-class.ts"],"sourcesContent":["import type { ComponentDefinition } from './component-types';\n\nimport { BaseElement } from './base-element';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { normalizePropDefinition, type PropInputDefs, type PropsDef, validatePropDefs } from './props';\nimport { toKebab } from './utils/dom';\n\n/**\n * Builds the element class without touching the browser registry. Keeping\n * declaration work separate from registration makes the latter the only\n * irreversible global side effect in the component-definition path.\n *\n * @internal\n */\nexport function createComponentClass<Props extends Record<string, unknown>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): CustomElementConstructor {\n const { props: propDefs } = definition;\n\n const normalizedPropDefs: PropsDef<Props> | undefined = (() => {\n if (!propDefs) return undefined;\n\n const errors = validatePropDefs(propDefs as Record<string, unknown>);\n\n if (errors.length > 0) throw new OreApiError(ORE_ERRORS.validationFailed(tag, errors));\n\n const normalized: PropInputDefs = {};\n\n for (const [key, def] of Object.entries(propDefs)) {\n normalized[key] = normalizePropDefinition(def, key);\n }\n\n return normalized as PropsDef<Props>;\n })();\n\n const observedAttrs = normalizedPropDefs ? Object.keys(normalizedPropDefs).map(toKebab) : [];\n\n const ComponentClass = class extends BaseElement {\n static override _definition = definition as unknown as ComponentDefinition;\n static override _normalizedPropDefs = normalizedPropDefs as PropsDef<Record<never, never>> | undefined;\n static override formAssociated = definition.formAssociated ?? false;\n static override observedAttributes = observedAttrs;\n };\n\n return ComponentClass;\n}\n"],"mappings":"8NAcA,SAAgB,EACd,EACA,EAC0B,CAC1B,GAAM,CAAE,MAAO,GAAa,EAEtB,OAAyD,CAC7D,GAAI,CAAC,EAAU,OAEf,IAAM,EAAS,EAAiB,CAAmC,EAEnE,GAAI,EAAO,OAAS,EAAG,MAAM,IAAI,EAAY,EAAW,iBAAiB,EAAK,CAAM,CAAC,EAErF,IAAM,EAA4B,CAAC,EAEnC,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,CAAQ,EAC9C,EAAW,GAAO,EAAwB,EAAK,CAAG,EAGpD,OAAO,CACT,EAAA,CAAG,EAEG,EAAgB,EAAqB,OAAO,KAAK,CAAkB,CAAC,CAAC,IAAI,CAAO,EAAI,CAAC,EAS3F,OAAO,cAP8B,CAAY,CAC/C,OAAgB,YAAc,EAC9B,OAAgB,oBAAsB,EACtC,OAAgB,eAAiB,EAAW,gBAAkB,GAC9D,OAAgB,mBAAqB,CACvC,CAGF"}
|
package/dist/_dev.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
function e(e){}function t(e,...t){}exports.error=t,exports.warn=e;
|
|
2
2
|
//# sourceMappingURL=_dev.cjs.map
|
package/dist/_dev.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ORE_PROD__?: boolean }).__ORE_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/ore] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/ore] ${msg}`, ...args);\n}\n
|
|
1
|
+
{"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ORE_PROD__?: boolean }).__ORE_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/ore] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/ore] ${msg}`, ...args);\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC,CAGA,SAAgB,EAAM,EAAa,GAAG,EAAuB,CAE7D"}
|
package/dist/_dev.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
function e(e){}function t(e,...t){}export{t as error,e as warn};
|
|
2
2
|
//# sourceMappingURL=_dev.js.map
|
package/dist/_dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ORE_PROD__?: boolean }).__ORE_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/ore] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/ore] ${msg}`, ...args);\n}\n
|
|
1
|
+
{"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ORE_PROD__?: boolean }).__ORE_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/ore] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/ore] ${msg}`, ...args);\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC,CAGA,SAAgB,EAAM,EAAa,GAAG,EAAuB,CAE7D"}
|
package/dist/base-element.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./runtime.cjs"),n=require("./props.cjs");require("./template/result.cjs");const r=require("./utils/css.cjs");let i=require("@vielzeug/ripple");var a={SETUP_DONE:`setup_done`,SETUP_RUNNING:`setup_running`,UNINITIALIZED:`uninitialized`,UNMOUNTED:`unmounted`},o={CONNECT:`ore:connect`,DISCONNECT:`ore:disconnect`},s=()=>({formResetCallbacks:[],generation:0,mountCallbacks:[],phase:a.UNINITIALIZED,scope:(0,i.createScope)(),templateResult:null}),c=e=>(typeof e==`object`||typeof e==`function`)&&e!==null&&`then`in e&&typeof e.then==`function`,l=class extends HTMLElement{static _definition;static _normalizedPropDefs;static formAssociated=!1;static observedAttributes=[];_component;constructor(){super();let e=this.constructor._definition;e?.shadow!==!1&&this.attachShadow({mode:`open`,...e?.shadow}),this._component=s()}connectedCallback(){(0,i.untrack)(()=>{this._component.phase===a.UNINITIALIZED&&this._runSetup(),this._init()}),this.dispatchEvent(new CustomEvent(o.CONNECT,{bubbles:!1,composed:!1}))}attributeChangedCallback(e,t,r){if(t===r)return;let a=n.getPropMeta(this,e);if(!a)return;let o=a.parse(r);Object.is((0,i.untrack)(()=>a.signal.value),o)||(a.signal.value=o)}disconnectedCallback(){this._component.generation++,this._component.phase=a.UNMOUNTED,this.dispatchEvent(new CustomEvent(o.DISCONNECT,{bubbles:!1,composed:!1})),this._resetSetupState()}_resetSetupState(){this._component.scope.dispose(),this._component.formResetCallbacks=[],this._component.mountCallbacks=[],this._component.phase=a.UNINITIALIZED,this._component.scope=(0,i.createScope)(),this._component.templateResult=null}formResetCallback(){for(let e of this._component.formResetCallbacks)try{e()}catch(e){this._reportLifecycleError(e,`form-reset`)}}_reportLifecycleError(t,n){let r=t instanceof Error?t:Error(String(t)),i=new e.OreLifecycleError(`<${this.localName}> failed during ${this._component.phase} (${n})`,{cause:r,component:this.localName,phase:n});e.reportRuntimeError(i,this)}_runSetup(){this._component.phase=a.SETUP_RUNNING;let r=this.constructor._definition,i=this.constructor._normalizedPropDefs,o=t.createRuntimeContext(this);try{let s;if(this._component.scope.run(()=>{s=t.runWithContext(o,()=>{let e=i?n.createProps(this,i):{};return r.setup(e)})}),this._component.mountCallbacks.push(...o.mountCallbacks),this._component.formResetCallbacks.push(...o.formResetCallbacks),c(s))throw new e.OreApiError(e.ORE_ERRORS.asyncSetupUnsupported);this._component.templateResult=s??null,this._component.phase=a.SETUP_DONE}catch(e){throw this._reportLifecycleError(e,`setup`),this._resetSetupState(),e}}_isStale(e){return this._component.generation!==e||!this.isConnected}_applyResult(e){if(!e)return;let n=this.shadowRoot??this,r=t.createRuntimeContext(this);n.replaceChildren(),this._component.scope.run(()=>{t.runWithContext(r,()=>{e.mount(n,null,t.onCleanup)})})}_init(){this._applyStyles(),this._mountTemplate(),this._component.phase===a.SETUP_DONE&&this._scheduleMountCallbacks()}_applyStyles(){let e=this.constructor._definition;this.shadowRoot&&e?.styles?.length&&(this.shadowRoot.adoptedStyleSheets=e.styles.map(r.loadStylesheet))}_mountTemplate(){let e=this._component.templateResult;e&&this._applyResult(e)}_scheduleMountCallbacks(){if(this._component.mountCallbacks.length===0)return;let e=this._component.generation,n=t.beginPendingWork();queueMicrotask(()=>{try{if(this._isStale(e))return;let n=this._component.mountCallbacks.splice(0);for(let e of n)try{let n=t.createRuntimeContext(this);this._component.scope.run(()=>{t.runWithContext(n,()=>{let n=e();typeof n==`function`&&t.onCleanup(n)})}),n.mountCallbacks.length>0&&this._component.mountCallbacks.push(...n.mountCallbacks),n.formResetCallbacks.length>0&&this._component.formResetCallbacks.push(...n.formResetCallbacks)}catch(e){this._reportLifecycleError(e,`mounted`)}this._component.mountCallbacks.length>0&&this._scheduleMountCallbacks()}finally{n()}})}};exports.BaseElement=l;
|
|
2
2
|
//# sourceMappingURL=base-element.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-element.cjs","names":[],"sources":["../src/base-element.ts"],"sourcesContent":["import { scope as _scope, type Scope, untrack } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from './component-types';\n\nimport { warn } from './_dev';\nimport { type OreErrorPhase, OreLifecycleError, reportRuntimeError } from './errors';\nimport { createProps, getPropMeta, type InferProps, type PropInputDefs, type PropsDef } from './props';\nimport {\n type OnFormResetCallback,\n type OnMountedCallback,\n onCleanup,\n type RuntimeContext,\n runWithContext,\n} from './runtime';\nimport { ComponentPhase, LIFECYCLE_EVENTS } from './types';\nimport { type HTMLResult } from './types/bindings';\nimport { loadStylesheet } from './utils/css';\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 — guards both mount callbacks and async setup results. */\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: _scope(),\n templateResult: null,\n});\n\n// ─── BaseElement ──────────────────────────────────────────────────────────────\n\n/**\n * Phase transitions:\n *\n * ```\n * UNINITIALIZED ──_runSetup()──┬─(sync result)──► SETUP_DONE\n * └─(promise)──────► LOADING ──(resolves)──► SETUP_DONE\n * SETUP_DONE / LOADING ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED\n * ```\n *\n * `generation` increments on every disconnect and is captured before an async\n * `setup()` is awaited; if it no longer matches when the promise settles, the\n * element was disconnected (and possibly reconnected) in the meantime and the\n * result is discarded (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<any>;\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 this.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.CONNECT, { bubbles: false, composed: false }));\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.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.DISCONNECT, { bubbles: false, composed: false }));\n this._component.scope.dispose();\n // Reset mutable fields for next connect, 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 = _scope();\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._handleSetupError(error, 'form-reset');\n }\n }\n }\n\n private _handleSetupError(error: unknown, phase: OreErrorPhase = 'setup'): HTMLResult | 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 const def = (this.constructor as typeof BaseElement)._definition;\n\n if (def?.onError) {\n try {\n return def.onError(oreError, this);\n } catch {\n /* fall through */\n }\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: RuntimeContext = { element: this, formResetCallbacks: [], mountCallbacks: [] };\n\n try {\n let setupResult: HTMLResult | null | Promise<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 (setupResult != null && typeof (setupResult as Promise<HTMLResult | null>).then === 'function') {\n // Async setup: show loading template immediately, swap when resolved.\n // Store captured mount callbacks; they will be scheduled after the real template mounts.\n const pendingCallbacks = this._component.mountCallbacks.splice(0);\n\n this._component.phase = ComponentPhase.LOADING;\n\n if (def.loading) {\n this._component.templateResult = def.loading();\n }\n\n // Capture the current generation so the async handler can detect staleness:\n // if the element disconnects+reconnects before the promise resolves, generation is\n // incremented and the old promise result must be discarded.\n void this._runSetupAsync(\n setupResult as Promise<HTMLResult | null>,\n pendingCallbacks,\n this._component.generation,\n );\n } else {\n this._component.templateResult = (setupResult as HTMLResult | null) ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n }\n } catch (error) {\n const recoveryTemplate = this._handleSetupError(error);\n\n if (recoveryTemplate) {\n this._component.templateResult = recoveryTemplate;\n this._component.phase = ComponentPhase.SETUP_DONE;\n } else {\n this._component.phase = ComponentPhase.UNINITIALIZED;\n throw error;\n }\n }\n }\n\n /**\n * True once the element has disconnected (and possibly reconnected) since\n * `capturedGeneration` was recorded — an in-flight async `setup()` result\n * arriving after that point is for an instance state that no longer exists.\n */\n private _isStale(capturedGeneration: number): boolean {\n return this._component.generation !== capturedGeneration || !this.isConnected;\n }\n\n private async _runSetupAsync(\n promise: Promise<HTMLResult | null>,\n pendingCallbacks: OnMountedCallback[],\n capturedGeneration: number,\n ): Promise<void> {\n try {\n const result = await promise;\n\n if (this._isStale(capturedGeneration)) {\n warn(`<${this.localName}> async setup result discarded — element disconnected before setup resolved.`);\n\n return;\n }\n\n this._component.templateResult = result ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n this._component.mountCallbacks.push(...pendingCallbacks);\n\n if (result) this._applyResult(result);\n\n this._scheduleMountCallbacks();\n } catch (error) {\n if (this._isStale(capturedGeneration)) {\n warn(`<${this.localName}> async setup error discarded — element disconnected before setup resolved.`);\n\n return;\n }\n\n const recovery = this._handleSetupError(error, 'async-setup');\n\n if (recovery) {\n this._component.templateResult = recovery;\n this._component.phase = ComponentPhase.SETUP_DONE;\n this._applyResult(recovery);\n } else {\n this._component.phase = ComponentPhase.UNINITIALIZED;\n }\n }\n }\n\n private _applyResult(result: HTMLResult | null): void {\n if (!result) return;\n\n const host: Element | ShadowRoot = this.shadowRoot ?? this;\n\n host.replaceChildren(result.fragment);\n this._component.scope.run(() => {\n result.apply(onCleanup);\n });\n }\n\n private _init(): void {\n this._applyStyles();\n this._mountTemplate();\n\n // Only schedule mount callbacks once setup is fully complete (not in LOADING state)\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\n queueMicrotask(() => {\n if (this._isStale(capturedGeneration)) return;\n\n // Snapshot callbacks so in-loop registrations don't extend this iteration.\n const batch = this._component.mountCallbacks.splice(0);\n\n for (const callback of batch) {\n try {\n const nestedCtx = {\n element: this,\n formResetCallbacks: [] as typeof this._component.formResetCallbacks,\n mountCallbacks: [] as typeof this._component.mountCallbacks,\n };\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 this._component.mountCallbacks.push(...nestedCtx.mountCallbacks);\n }\n\n if (nestedCtx.formResetCallbacks.length > 0) {\n this._component.formResetCallbacks.push(...nestedCtx.formResetCallbacks);\n }\n } catch (error) {\n this._handleSetupError(error, 'mounted');\n }\n }\n\n // If nested onMounted calls registered new callbacks, schedule them with\n // a fresh token check in the next microtask.\n if (this._component.mountCallbacks.length > 0) {\n this._scheduleMountCallbacks();\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 { OreApiError, type OreErrorPhase, OreLifecycleError, ORE_ERRORS, 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 & lifecycle events ──────────────────────────────────────\n// Internal to BaseElement — the only state machine and event dispatcher 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\nconst LIFECYCLE_EVENTS = {\n CONNECT: 'ore:connect',\n DISCONNECT: 'ore:disconnect',\n} as const;\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 this.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.CONNECT, { bubbles: false, composed: false }));\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.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.DISCONNECT, { bubbles: false, composed: false }));\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 const batch = this._component.mountCallbacks.splice(0);\n\n for (const callback of batch) {\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 this._component.mountCallbacks.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\n // If nested onMounted calls registered new callbacks, schedule them with\n // a fresh token check in the next microtask. This happens *before* endWork()\n // below runs so the counter never dips to zero between the two schedules.\n if (this._component.mountCallbacks.length > 0) {\n this._scheduleMountCallbacks();\n }\n } finally {\n endWork();\n }\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/base-element.d.ts
CHANGED
|
@@ -4,15 +4,13 @@ import { type PropsDef } from './props';
|
|
|
4
4
|
* Phase transitions:
|
|
5
5
|
*
|
|
6
6
|
* ```
|
|
7
|
-
* UNINITIALIZED ──_runSetup()
|
|
8
|
-
*
|
|
9
|
-
* SETUP_DONE / LOADING ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED
|
|
7
|
+
* UNINITIALIZED ──_runSetup()──► SETUP_DONE
|
|
8
|
+
* SETUP_DONE ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED
|
|
10
9
|
* ```
|
|
11
10
|
*
|
|
12
|
-
* `generation` increments on every disconnect
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* result is discarded (see `_isStale`).
|
|
11
|
+
* `generation` increments on every disconnect. Scheduled mount callbacks capture
|
|
12
|
+
* it so callbacks belonging to a disconnected instance cannot run after a
|
|
13
|
+
* reconnect (see `_isStale`).
|
|
16
14
|
*
|
|
17
15
|
* Why this lives on the class instead of a standalone pure reducer: every
|
|
18
16
|
* transition here is triggered by running actual user code (`def.setup()`,
|
|
@@ -24,7 +22,7 @@ import { type PropsDef } from './props';
|
|
|
24
22
|
* procedural steps instead of a reducer + effect interpreter.
|
|
25
23
|
*/
|
|
26
24
|
export declare class BaseElement extends HTMLElement {
|
|
27
|
-
static _definition: ComponentDefinition
|
|
25
|
+
static _definition: ComponentDefinition;
|
|
28
26
|
static _normalizedPropDefs: PropsDef<Record<never, never>> | undefined;
|
|
29
27
|
static formAssociated: boolean;
|
|
30
28
|
static observedAttributes: string[];
|
|
@@ -33,21 +31,17 @@ export declare class BaseElement extends HTMLElement {
|
|
|
33
31
|
connectedCallback(): void;
|
|
34
32
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
35
33
|
disconnectedCallback(): void;
|
|
34
|
+
/** Dispose one connection's resources before its state can be rebuilt. */
|
|
35
|
+
private _resetSetupState;
|
|
36
36
|
/**
|
|
37
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
|
*/
|
|
41
41
|
formResetCallback(): void;
|
|
42
|
-
private
|
|
42
|
+
private _reportLifecycleError;
|
|
43
43
|
private _runSetup;
|
|
44
|
-
/**
|
|
45
|
-
* True once the element has disconnected (and possibly reconnected) since
|
|
46
|
-
* `capturedGeneration` was recorded — an in-flight async `setup()` result
|
|
47
|
-
* arriving after that point is for an instance state that no longer exists.
|
|
48
|
-
*/
|
|
49
44
|
private _isStale;
|
|
50
|
-
private _runSetupAsync;
|
|
51
45
|
private _applyResult;
|
|
52
46
|
private _init;
|
|
53
47
|
private _applyStyles;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-element.d.ts","sourceRoot":"","sources":["../src/base-element.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"base-element.d.ts","sourceRoot":"","sources":["../src/base-element.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAAiE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AA2DvG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,WAAY,SAAQ,WAAW;IAC1C,MAAM,CAAC,WAAW,EAAE,mBAAmB,CAAC;IACxC,MAAM,CAAC,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;IACvE,MAAM,CAAC,cAAc,UAAS;IAC9B,MAAM,CAAC,kBAAkB,EAAE,MAAM,EAAE,CAAM;IAEzC,OAAO,CAAC,UAAU,CAAiB;;IAcnC,iBAAiB,IAAI,IAAI;IASzB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAkB9F,oBAAoB,IAAI,IAAI;IAO5B,0EAA0E;IAC1E,OAAO,CAAC,gBAAgB;IAUxB;;;;OAIG;IACH,iBAAiB,IAAI,IAAI;IAUzB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,SAAS;IAmCjB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,uBAAuB;CAmDhC"}
|
package/dist/base-element.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ORE_ERRORS as e,OreApiError as t,OreLifecycleError as n,reportRuntimeError as r}from"./errors.js";import{beginPendingWork as i,createRuntimeContext as a,onCleanup as o,runWithContext as s}from"./runtime.js";import{createProps as c,getPropMeta as l}from"./props.js";import"./template/result.js";import{loadStylesheet as u}from"./utils/css.js";import{createScope as d,untrack as f}from"@vielzeug/ripple";var p={SETUP_DONE:`setup_done`,SETUP_RUNNING:`setup_running`,UNINITIALIZED:`uninitialized`,UNMOUNTED:`unmounted`},m={CONNECT:`ore:connect`,DISCONNECT:`ore:disconnect`},h=()=>({formResetCallbacks:[],generation:0,mountCallbacks:[],phase:p.UNINITIALIZED,scope:d(),templateResult:null}),g=e=>(typeof e==`object`||typeof e==`function`)&&e!==null&&`then`in e&&typeof e.then==`function`,_=class extends HTMLElement{static _definition;static _normalizedPropDefs;static formAssociated=!1;static observedAttributes=[];_component;constructor(){super();let e=this.constructor._definition;e?.shadow!==!1&&this.attachShadow({mode:`open`,...e?.shadow}),this._component=h()}connectedCallback(){f(()=>{this._component.phase===p.UNINITIALIZED&&this._runSetup(),this._init()}),this.dispatchEvent(new CustomEvent(m.CONNECT,{bubbles:!1,composed:!1}))}attributeChangedCallback(e,t,n){if(t===n)return;let r=l(this,e);if(!r)return;let i=r.parse(n);Object.is(f(()=>r.signal.value),i)||(r.signal.value=i)}disconnectedCallback(){this._component.generation++,this._component.phase=p.UNMOUNTED,this.dispatchEvent(new CustomEvent(m.DISCONNECT,{bubbles:!1,composed:!1})),this._resetSetupState()}_resetSetupState(){this._component.scope.dispose(),this._component.formResetCallbacks=[],this._component.mountCallbacks=[],this._component.phase=p.UNINITIALIZED,this._component.scope=d(),this._component.templateResult=null}formResetCallback(){for(let e of this._component.formResetCallbacks)try{e()}catch(e){this._reportLifecycleError(e,`form-reset`)}}_reportLifecycleError(e,t){let i=e instanceof Error?e:Error(String(e)),a=new n(`<${this.localName}> failed during ${this._component.phase} (${t})`,{cause:i,component:this.localName,phase:t});r(a,this)}_runSetup(){this._component.phase=p.SETUP_RUNNING;let n=this.constructor._definition,r=this.constructor._normalizedPropDefs,i=a(this);try{let a;if(this._component.scope.run(()=>{a=s(i,()=>{let e=r?c(this,r):{};return n.setup(e)})}),this._component.mountCallbacks.push(...i.mountCallbacks),this._component.formResetCallbacks.push(...i.formResetCallbacks),g(a))throw new t(e.asyncSetupUnsupported);this._component.templateResult=a??null,this._component.phase=p.SETUP_DONE}catch(e){throw this._reportLifecycleError(e,`setup`),this._resetSetupState(),e}}_isStale(e){return this._component.generation!==e||!this.isConnected}_applyResult(e){if(!e)return;let t=this.shadowRoot??this,n=a(this);t.replaceChildren(),this._component.scope.run(()=>{s(n,()=>{e.mount(t,null,o)})})}_init(){this._applyStyles(),this._mountTemplate(),this._component.phase===p.SETUP_DONE&&this._scheduleMountCallbacks()}_applyStyles(){let e=this.constructor._definition;this.shadowRoot&&e?.styles?.length&&(this.shadowRoot.adoptedStyleSheets=e.styles.map(u))}_mountTemplate(){let e=this._component.templateResult;e&&this._applyResult(e)}_scheduleMountCallbacks(){if(this._component.mountCallbacks.length===0)return;let e=this._component.generation,t=i();queueMicrotask(()=>{try{if(this._isStale(e))return;let t=this._component.mountCallbacks.splice(0);for(let e of t)try{let t=a(this);this._component.scope.run(()=>{s(t,()=>{let t=e();typeof t==`function`&&o(t)})}),t.mountCallbacks.length>0&&this._component.mountCallbacks.push(...t.mountCallbacks),t.formResetCallbacks.length>0&&this._component.formResetCallbacks.push(...t.formResetCallbacks)}catch(e){this._reportLifecycleError(e,`mounted`)}this._component.mountCallbacks.length>0&&this._scheduleMountCallbacks()}finally{t()}})}};export{_ as BaseElement};
|
|
2
2
|
//# sourceMappingURL=base-element.js.map
|
package/dist/base-element.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-element.js","names":[],"sources":["../src/base-element.ts"],"sourcesContent":["import { scope as _scope, type Scope, untrack } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from './component-types';\n\nimport { warn } from './_dev';\nimport { type OreErrorPhase, OreLifecycleError, reportRuntimeError } from './errors';\nimport { createProps, getPropMeta, type InferProps, type PropInputDefs, type PropsDef } from './props';\nimport {\n type OnFormResetCallback,\n type OnMountedCallback,\n onCleanup,\n type RuntimeContext,\n runWithContext,\n} from './runtime';\nimport { ComponentPhase, LIFECYCLE_EVENTS } from './types';\nimport { type HTMLResult } from './types/bindings';\nimport { loadStylesheet } from './utils/css';\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 — guards both mount callbacks and async setup results. */\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: _scope(),\n templateResult: null,\n});\n\n// ─── BaseElement ──────────────────────────────────────────────────────────────\n\n/**\n * Phase transitions:\n *\n * ```\n * UNINITIALIZED ──_runSetup()──┬─(sync result)──► SETUP_DONE\n * └─(promise)──────► LOADING ──(resolves)──► SETUP_DONE\n * SETUP_DONE / LOADING ──disconnectedCallback()──► UNMOUNTED ──(reset)──► UNINITIALIZED\n * ```\n *\n * `generation` increments on every disconnect and is captured before an async\n * `setup()` is awaited; if it no longer matches when the promise settles, the\n * element was disconnected (and possibly reconnected) in the meantime and the\n * result is discarded (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<any>;\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 this.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.CONNECT, { bubbles: false, composed: false }));\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.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.DISCONNECT, { bubbles: false, composed: false }));\n this._component.scope.dispose();\n // Reset mutable fields for next connect, 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 = _scope();\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._handleSetupError(error, 'form-reset');\n }\n }\n }\n\n private _handleSetupError(error: unknown, phase: OreErrorPhase = 'setup'): HTMLResult | 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 const def = (this.constructor as typeof BaseElement)._definition;\n\n if (def?.onError) {\n try {\n return def.onError(oreError, this);\n } catch {\n /* fall through */\n }\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: RuntimeContext = { element: this, formResetCallbacks: [], mountCallbacks: [] };\n\n try {\n let setupResult: HTMLResult | null | Promise<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 (setupResult != null && typeof (setupResult as Promise<HTMLResult | null>).then === 'function') {\n // Async setup: show loading template immediately, swap when resolved.\n // Store captured mount callbacks; they will be scheduled after the real template mounts.\n const pendingCallbacks = this._component.mountCallbacks.splice(0);\n\n this._component.phase = ComponentPhase.LOADING;\n\n if (def.loading) {\n this._component.templateResult = def.loading();\n }\n\n // Capture the current generation so the async handler can detect staleness:\n // if the element disconnects+reconnects before the promise resolves, generation is\n // incremented and the old promise result must be discarded.\n void this._runSetupAsync(\n setupResult as Promise<HTMLResult | null>,\n pendingCallbacks,\n this._component.generation,\n );\n } else {\n this._component.templateResult = (setupResult as HTMLResult | null) ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n }\n } catch (error) {\n const recoveryTemplate = this._handleSetupError(error);\n\n if (recoveryTemplate) {\n this._component.templateResult = recoveryTemplate;\n this._component.phase = ComponentPhase.SETUP_DONE;\n } else {\n this._component.phase = ComponentPhase.UNINITIALIZED;\n throw error;\n }\n }\n }\n\n /**\n * True once the element has disconnected (and possibly reconnected) since\n * `capturedGeneration` was recorded — an in-flight async `setup()` result\n * arriving after that point is for an instance state that no longer exists.\n */\n private _isStale(capturedGeneration: number): boolean {\n return this._component.generation !== capturedGeneration || !this.isConnected;\n }\n\n private async _runSetupAsync(\n promise: Promise<HTMLResult | null>,\n pendingCallbacks: OnMountedCallback[],\n capturedGeneration: number,\n ): Promise<void> {\n try {\n const result = await promise;\n\n if (this._isStale(capturedGeneration)) {\n warn(`<${this.localName}> async setup result discarded — element disconnected before setup resolved.`);\n\n return;\n }\n\n this._component.templateResult = result ?? null;\n this._component.phase = ComponentPhase.SETUP_DONE;\n this._component.mountCallbacks.push(...pendingCallbacks);\n\n if (result) this._applyResult(result);\n\n this._scheduleMountCallbacks();\n } catch (error) {\n if (this._isStale(capturedGeneration)) {\n warn(`<${this.localName}> async setup error discarded — element disconnected before setup resolved.`);\n\n return;\n }\n\n const recovery = this._handleSetupError(error, 'async-setup');\n\n if (recovery) {\n this._component.templateResult = recovery;\n this._component.phase = ComponentPhase.SETUP_DONE;\n this._applyResult(recovery);\n } else {\n this._component.phase = ComponentPhase.UNINITIALIZED;\n }\n }\n }\n\n private _applyResult(result: HTMLResult | null): void {\n if (!result) return;\n\n const host: Element | ShadowRoot = this.shadowRoot ?? this;\n\n host.replaceChildren(result.fragment);\n this._component.scope.run(() => {\n result.apply(onCleanup);\n });\n }\n\n private _init(): void {\n this._applyStyles();\n this._mountTemplate();\n\n // Only schedule mount callbacks once setup is fully complete (not in LOADING state)\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\n queueMicrotask(() => {\n if (this._isStale(capturedGeneration)) return;\n\n // Snapshot callbacks so in-loop registrations don't extend this iteration.\n const batch = this._component.mountCallbacks.splice(0);\n\n for (const callback of batch) {\n try {\n const nestedCtx = {\n element: this,\n formResetCallbacks: [] as typeof this._component.formResetCallbacks,\n mountCallbacks: [] as typeof this._component.mountCallbacks,\n };\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 this._component.mountCallbacks.push(...nestedCtx.mountCallbacks);\n }\n\n if (nestedCtx.formResetCallbacks.length > 0) {\n this._component.formResetCallbacks.push(...nestedCtx.formResetCallbacks);\n }\n } catch (error) {\n this._handleSetupError(error, 'mounted');\n }\n }\n\n // If nested onMounted calls registered new callbacks, schedule them with\n // a fresh token check in the next microtask.\n if (this._component.mountCallbacks.length > 0) {\n this._scheduleMountCallbacks();\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 { OreApiError, type OreErrorPhase, OreLifecycleError, ORE_ERRORS, 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 & lifecycle events ──────────────────────────────────────\n// Internal to BaseElement — the only state machine and event dispatcher 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\nconst LIFECYCLE_EVENTS = {\n CONNECT: 'ore:connect',\n DISCONNECT: 'ore:disconnect',\n} as const;\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 this.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.CONNECT, { bubbles: false, composed: false }));\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.dispatchEvent(new CustomEvent(LIFECYCLE_EVENTS.DISCONNECT, { bubbles: false, composed: false }));\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 const batch = this._component.mountCallbacks.splice(0);\n\n for (const callback of batch) {\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 this._component.mountCallbacks.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\n // If nested onMounted calls registered new callbacks, schedule them with\n // a fresh token check in the next microtask. This happens *before* endWork()\n // below runs so the counter never dips to zero between the two schedules.\n if (this._component.mountCallbacks.length > 0) {\n this._scheduleMountCallbacks();\n }\n } finally {\n endWork();\n }\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
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` — 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"
|
|
@@ -13,24 +13,13 @@
|
|
|
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.
|
|
15
15
|
*/
|
|
16
|
-
import type { OreLifecycleError } from './errors';
|
|
17
16
|
import type { InferProps, PropsDef } from './props';
|
|
18
|
-
import type { HTMLResult } from './
|
|
17
|
+
import type { HTMLResult } from './template/result';
|
|
19
18
|
import type { CSSResult } from './utils/css';
|
|
20
19
|
export type ComponentDefinition<Props extends Record<string, unknown> = Record<never, never>> = {
|
|
21
20
|
formAssociated?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Rendered while async `setup()` is still pending.
|
|
24
|
-
* Only used when `setup` returns a `Promise`.
|
|
25
|
-
*/
|
|
26
|
-
loading?: () => HTMLResult;
|
|
27
|
-
/**
|
|
28
|
-
* Error handler called when setup throws.
|
|
29
|
-
* If a recovery HTMLResult is returned, it replaces the failed template.
|
|
30
|
-
*/
|
|
31
|
-
onError?: (error: OreLifecycleError, element: HTMLElement) => HTMLResult | void;
|
|
32
21
|
props?: PropsDef<Props>;
|
|
33
|
-
setup: (props: InferProps<PropsDef<Props>>) => HTMLResult | null
|
|
22
|
+
setup: (props: InferProps<PropsDef<Props>>) => HTMLResult | null;
|
|
34
23
|
/**
|
|
35
24
|
* Shadow DOM configuration. `mode` defaults to `'open'`.
|
|
36
25
|
* Set to `false` to opt out of shadow DOM entirely (light DOM rendering).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-types.d.ts","sourceRoot":"","sources":["../src/component-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"component-types.d.ts","sourceRoot":"","sources":["../src/component-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,mBAAmB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI;IAC9F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxB,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,UAAU,GAAG,IAAI,CAAC;IACjE;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC;IACzC,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC,EAAE,CAAC;CACjD,CAAC"}
|
package/dist/context.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
require("./_dev.cjs");const e=require("./errors.cjs"),t=require("./runtime.cjs");var n=new WeakMap,r=e=>{let t=[],n=e;for(;n;)n instanceof HTMLElement&&t.push(n),n=n.parentNode??(n instanceof ShadowRoot?n.host:null);return t},i=(e,t,r)=>{let i=n.get(e)??new Map;i.has(t)&&`${e.localName}`,i.set(t,r),n.set(e,i)},a=(e,n)=>{i(t.requireSetupContext(`provide`).element,e,n)},o=Symbol(`inject.not_found`),s=new WeakMap,c=(e,t)=>{let i=r(e);for(let e of i){let r=n.get(e);if(r?.has(t))return r.get(t)}return o},l=(e,t)=>{let n=s.get(e);n||(n=new Map,s.set(e,n));let r=t;return n.has(r)||n.set(r,c(e.element,t)),n.get(r)};function u(e,...n){let r=l(t.requireSetupContext(`inject`),e);return r===o?n.length>0?n[0]:void 0:r}var d=n=>{let r=t.requireSetupContext(`injectStrict`),i=l(r,n);if(i!==o)return i;throw new e.OreApiError(e.ORE_ERRORS.injectStrictFailed(String(n),r.element.localName))},f=0;function p(e){return Symbol.for(`ore:context:${e??`anonymous-${++f}`}`)}exports.createContext=p,exports.inject=u,exports.injectStrict=d,exports.provide=a;
|
|
2
2
|
//# sourceMappingURL=context.cjs.map
|
package/dist/context.cjs.map
CHANGED
|
@@ -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\nimport { warn } from './_dev';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport {
|
|
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 * Keys are `Symbol.for`-based (see `createContext`) so provide/inject still match\n * across a duplicated module graph — the same cross-copy survival rule as the\n * object brands in `utils/brand.ts`.\n */\n\nimport { warn } from './_dev';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { requireSetupContext, type RuntimeContext } 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 provideOnElement(requireSetupContext('provide').element, key, value);\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\nlet anonymousKeyCounter = 0;\n\n/**\n * Create a typed context key. `Symbol.for`-keyed (`ore:context:<description>`) so\n * a provider and an injector loaded from two bundled copies of ore still match\n * (see module header). Two `createContext('theme')` calls intentionally produce\n * the same key — use distinct descriptions for distinct contexts.\n *\n * Always pass a description: anonymous keys are minted from a per-graph counter,\n * so they do NOT survive duplicated module graphs (each copy numbers its own).\n */\nexport function createContext<T>(description?: string): InjectionKey<T> {\n if (description === undefined) {\n warn(\n 'createContext() called without a description — anonymous context keys do not survive duplicated module graphs.',\n );\n }\n\n return Symbol.for(`ore:context:${description ?? `anonymous-${++anonymousKeyCounter}`}`) as InjectionKey<T>;\n}\n"],"mappings":"iFAgBA,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,EAAiB,EAAA,oBAAoB,SAAS,CAAC,CAAC,QAAS,EAAK,CAAK,CACrE,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,EAEI,EAAsB,EAW1B,SAAgB,EAAiB,EAAuC,CAOtE,OAAO,OAAO,IAAI,eAAe,GAAe,aAAa,EAAE,KAAuB,CACxF"}
|
package/dist/context.d.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
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).
|
|
6
6
|
* Providing is done via `provide(key, value)` inside `setup()`.
|
|
7
|
+
*
|
|
8
|
+
* Keys are `Symbol.for`-based (see `createContext`) so provide/inject still match
|
|
9
|
+
* across a duplicated module graph — the same cross-copy survival rule as the
|
|
10
|
+
* object brands in `utils/brand.ts`.
|
|
7
11
|
*/
|
|
8
12
|
export type InjectionKey<T> = symbol & {
|
|
9
13
|
readonly __ore_injection_key?: T;
|
|
@@ -20,5 +24,14 @@ export declare const provide: <T>(key: InjectionKey<T>, value: T) => void;
|
|
|
20
24
|
export declare function inject<T>(key: InjectionKey<T>): T | undefined;
|
|
21
25
|
export declare function inject<T>(key: InjectionKey<T>, fallback: T): T;
|
|
22
26
|
export declare const injectStrict: <T>(key: InjectionKey<T>) => T;
|
|
27
|
+
/**
|
|
28
|
+
* Create a typed context key. `Symbol.for`-keyed (`ore:context:<description>`) so
|
|
29
|
+
* a provider and an injector loaded from two bundled copies of ore still match
|
|
30
|
+
* (see module header). Two `createContext('theme')` calls intentionally produce
|
|
31
|
+
* the same key — use distinct descriptions for distinct contexts.
|
|
32
|
+
*
|
|
33
|
+
* Always pass a description: anonymous keys are minted from a per-graph counter,
|
|
34
|
+
* so they do NOT survive duplicated module graphs (each copy numbers its own).
|
|
35
|
+
*/
|
|
23
36
|
export declare function createContext<T>(description?: string): InjectionKey<T>;
|
|
24
37
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;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,IAE3D,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;AAIF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
|
package/dist/context.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import"./_dev.js";import{ORE_ERRORS as e,OreApiError as t}from"./errors.js";import{requireSetupContext as n}from"./runtime.js";var r=new WeakMap,i=e=>{let t=[],n=e;for(;n;)n instanceof HTMLElement&&t.push(n),n=n.parentNode??(n instanceof ShadowRoot?n.host:null);return t},a=(e,t,n)=>{let i=r.get(e)??new Map;i.has(t)&&`${e.localName}`,i.set(t,n),r.set(e,i)},o=(e,t)=>{a(n(`provide`).element,e,t)},s=Symbol(`inject.not_found`),c=new WeakMap,l=(e,t)=>{let n=i(e);for(let e of n){let n=r.get(e);if(n?.has(t))return n.get(t)}return s},u=(e,t)=>{let n=c.get(e);n||(n=new Map,c.set(e,n));let r=t;return n.has(r)||n.set(r,l(e.element,t)),n.get(r)};function d(e,...t){let r=u(n(`inject`),e);return r===s?t.length>0?t[0]:void 0:r}var f=r=>{let i=n(`injectStrict`),a=u(i,r);if(a!==s)return a;throw new t(e.injectStrictFailed(String(r),i.element.localName))},p=0;function m(e){return Symbol.for(`ore:context:${e??`anonymous-${++p}`}`)}export{m as createContext,d as inject,f as injectStrict,o as provide};
|
|
2
2
|
//# sourceMappingURL=context.js.map
|
package/dist/context.js.map
CHANGED
|
@@ -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\nimport { warn } from './_dev';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport {
|
|
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 * Keys are `Symbol.for`-based (see `createContext`) so provide/inject still match\n * across a duplicated module graph — the same cross-copy survival rule as the\n * object brands in `utils/brand.ts`.\n */\n\nimport { warn } from './_dev';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { requireSetupContext, type RuntimeContext } 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 provideOnElement(requireSetupContext('provide').element, key, value);\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\nlet anonymousKeyCounter = 0;\n\n/**\n * Create a typed context key. `Symbol.for`-keyed (`ore:context:<description>`) so\n * a provider and an injector loaded from two bundled copies of ore still match\n * (see module header). Two `createContext('theme')` calls intentionally produce\n * the same key — use distinct descriptions for distinct contexts.\n *\n * Always pass a description: anonymous keys are minted from a per-graph counter,\n * so they do NOT survive duplicated module graphs (each copy numbers its own).\n */\nexport function createContext<T>(description?: string): InjectionKey<T> {\n if (description === undefined) {\n warn(\n 'createContext() called without a description — anonymous context keys do not survive duplicated module graphs.',\n );\n }\n\n return Symbol.for(`ore:context:${description ?? `anonymous-${++anonymousKeyCounter}`}`) as InjectionKey<T>;\n}\n"],"mappings":"+HAgBA,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,EAAiB,EAAoB,SAAS,CAAC,CAAC,QAAS,EAAK,CAAK,CACrE,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,EAEI,EAAsB,EAW1B,SAAgB,EAAiB,EAAuC,CAOtE,OAAO,OAAO,IAAI,eAAe,GAAe,aAAa,EAAE,KAAuB,CACxF"}
|