@vielzeug/ore 26.11.9 → 26.11.11
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/dist/base-element.cjs +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.d.ts +12 -39
- 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/context.cjs.map +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.map +1 -1
- package/dist/define.js +1 -1
- package/dist/define.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.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/model.cjs.map +1 -1
- package/dist/directives/model.d.ts +2 -3
- package/dist/directives/model.d.ts.map +1 -1
- package/dist/directives/model.js.map +1 -1
- package/dist/directives/unsafe-html.cjs +1 -1
- package/dist/directives/unsafe-html.cjs.map +1 -1
- package/dist/directives/unsafe-html.d.ts.map +1 -1
- package/dist/directives/unsafe-html.js +1 -1
- package/dist/directives/unsafe-html.js.map +1 -1
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +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 +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +2 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.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 +2 -2
- 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 +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +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 +411 -424
- 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 +6 -10
- 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 +22 -9
- 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.map +1 -1
- package/dist/slots.d.ts.map +1 -1
- package/dist/slots.js.map +1 -1
- package/dist/template/binding-types.d.ts +3 -9
- package/dist/template/binding-types.d.ts.map +1 -1
- package/dist/template/bindings.cjs +1 -1
- package/dist/template/bindings.cjs.map +1 -1
- package/dist/template/bindings.d.ts +8 -9
- 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/instantiator.cjs +1 -1
- package/dist/template/instantiator.cjs.map +1 -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 +1 -1
- package/dist/template/result.cjs.map +1 -1
- package/dist/template/result.d.ts +10 -16
- package/dist/template/result.d.ts.map +1 -1
- package/dist/template/result.js +1 -1
- package/dist/template/result.js.map +1 -1
- package/dist/testing/index.d.ts +1 -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.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.d.ts +16 -13
- package/dist/testing/render-hook.d.ts.map +1 -1
- package/dist/testing/render-hook.js +1 -1
- package/dist/testing/render-hook.js.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/utils/brand.cjs.map +1 -1
- package/dist/utils/brand.d.ts +1 -1
- package/dist/utils/brand.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 +12 -10
- 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/package.json +5 -5
- package/dist/directives/live.cjs +0 -2
- package/dist/directives/live.cjs.map +0 -1
- package/dist/directives/live.d.ts +0 -23
- package/dist/directives/live.d.ts.map +0 -1
- package/dist/directives/live.js +0 -2
- package/dist/directives/live.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.cjs","names":["signal","makeBrand","require_brand","_vielzeug_ripple","ref","directiveBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","createHtmlResult","applyFn","nodes","fragment","parent","anchor"
|
|
1
|
+
{"version":3,"file":"result.cjs","names":["signal","makeBrand","OreApiError","ORE_ERRORS","require_errors","require_brand","_vielzeug_ripple","ref","directiveBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","asCompiled","result","createHtmlResult","claimed","claim","applyFn","nodes","fragment","parent","anchor"],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts: Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`): see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function: only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\ndeclare const htmlResultTag: unique symbol;\n\n/**\n * The output of an `html` tagged template call: an opaque, single-use value.\n *\n * Each `html` call produces an independent fragment bound to its own interpolated\n * values, so it can be rendered exactly once: return it from `setup()`, interpolate\n * it into another template, or return it from a directive callback. Rendering the\n * same result twice throws; call the template function again for a second copy.\n */\nexport interface HTMLResult {\n readonly [htmlResultTag]: true;\n}\n\n/**\n * @internal The engine-facing shape behind every `HTMLResult`. `fragment` and `apply`\n * are the two-phase insertion protocol (static-embed merging in the instantiator,\n * `insertHtmlValues` in the binding layer); `mount` does both in one step. Every\n * consumer calls `claim()` first, which enforces single use.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(): void;\n /** Mark this result as consumed; throws `OreApiError` when it already was. */\n claim(): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n /**\n * Claim, insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null), and wire up all reactive effects. Returns the inserted nodes.\n * Listeners and nested effects register themselves on the active ripple owner.\n */\n mount(parent: ParentNode, anchor: Node | null): Node[];\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\n/** @internal Narrow a public `HTMLResult` to the engine shape, rejecting anything else. */\nexport const asCompiled = (result: HTMLResult): CompiledHTMLResult => {\n if (!isHtmlResult(result)) throw new OreApiError(ORE_ERRORS.notAnHtmlResult);\n\n return result;\n};\n\nexport function createHtmlResult(fragment: DocumentFragment, applyFn: () => void): CompiledHTMLResult {\n let claimed = false;\n\n const claim = (): void => {\n if (claimed) throw new OreApiError(ORE_ERRORS.htmlResultReused);\n\n claimed = true;\n };\n\n const mount = (parent: ParentNode, anchor: Node | null): Node[] => {\n claim();\n\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn();\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, claim, fragment, mount } as unknown as CompiledHTMLResult);\n}\n"],"mappings":"A,mGAiBA,SAAgBO,GAAiC,CAC/C,OAAA,EAAOP,EAAAA,OAAAA,CAAiB,IAAI,CAC9B,CAUA,IAAMQ,EAAiBP,EAAAA,UAA2B,eAAe,EAMpDQ,EAAyB,GACpCD,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnBE,EAAoBF,EAAe,GAuC1CG,EAAkBV,EAAAA,UAA8B,iBAAiB,EAE1DW,EAAeD,EAAgB,GAG/BE,EAAc,GAA2C,CACpE,GAAI,CAACD,EAAaE,CAAM,EAAG,MAAM,IAAIZ,EAAAA,YAAYC,EAAAA,WAAW,eAAe,EAE3E,OAAOW,CACT,EAEA,SAAgBC,EAAiB,EAA4B,EAAyC,CACpG,IAAIC,EAAU,GAERC,MAAoB,CACxB,GAAID,EAAS,MAAM,IAAId,EAAAA,YAAYC,EAAAA,WAAW,gBAAgB,EAE9D,EAAU,EACZ,EAaA,OAAOQ,EAAgB,MAAM,CAAE,MAAOO,EAAS,QAAO,WAAU,OAXjD,EAAoB,IAAgC,CACjED,EAAM,EAEN,IAAME,EAAQ,MAAM,KAAKC,EAAS,UAAU,EAK5C,OAHAC,EAAO,aAAaD,EAAUE,CAAM,EACpCJ,EAAQ,EAEDC,CACT,CAEsE,CAAkC,CAC1G,C"}
|
|
@@ -10,7 +10,7 @@ export type Ref<T extends Element> = Signal<T | null>;
|
|
|
10
10
|
export declare function ref<T extends Element>(): Ref<T>;
|
|
11
11
|
export type RefCallback<T extends Element> = (el: T | null) => void;
|
|
12
12
|
export type DirectiveResult = {
|
|
13
|
-
mount: (anchor: Comment
|
|
13
|
+
mount: (anchor: Comment) => void;
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
16
|
* Creates a registered DirectiveResult. All directive factories must use this
|
|
@@ -18,25 +18,19 @@ export type DirectiveResult = {
|
|
|
18
18
|
*/
|
|
19
19
|
export declare const createDirectiveResult: (mount: DirectiveResult["mount"]) => DirectiveResult;
|
|
20
20
|
export declare const isDirectiveResult: (value: unknown) => value is DirectiveResult;
|
|
21
|
+
declare const htmlResultTag: unique symbol;
|
|
21
22
|
/**
|
|
22
|
-
* The output of an `html` tagged template call.
|
|
23
|
+
* The output of an `html` tagged template call: an opaque, single-use value.
|
|
23
24
|
*
|
|
24
|
-
* Each `html` call produces an independent fragment
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* `mount()` is the entire public surface: insertion and reactive wiring in one
|
|
29
|
-
* step, so the two can never get out of sync (the old public `fragment` + `apply`
|
|
30
|
-
* pair made it possible to insert without wiring, or wire without inserting).
|
|
25
|
+
* Each `html` call produces an independent fragment bound to its own interpolated
|
|
26
|
+
* values, so it can be rendered exactly once: return it from `setup()`, interpolate
|
|
27
|
+
* it into another template, or return it from a directive callback. Rendering the
|
|
28
|
+
* same result twice throws; call the template function again for a second copy.
|
|
31
29
|
*/
|
|
32
30
|
export interface HTMLResult {
|
|
33
|
-
|
|
34
|
-
* Insert the template's nodes before `anchor` (or append to `parent` when
|
|
35
|
-
* `anchor` is null) and wire up all reactive effects. Returns the inserted
|
|
36
|
-
* nodes so callers can remove them later.
|
|
37
|
-
*/
|
|
38
|
-
mount(parent: ParentNode, anchor: Node | null, registerCleanup: (fn: () => void) => void): Node[];
|
|
31
|
+
readonly [htmlResultTag]: true;
|
|
39
32
|
}
|
|
40
33
|
export declare const isHtmlResult: (value: unknown) => value is CompiledHTMLResult;
|
|
41
|
-
export declare function createHtmlResult(fragment: DocumentFragment, applyFn: (
|
|
34
|
+
export declare function createHtmlResult(fragment: DocumentFragment, applyFn: () => void): CompiledHTMLResult;
|
|
35
|
+
export {};
|
|
42
36
|
//# sourceMappingURL=result.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/template/result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/template/result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;AAOvD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,IAAI,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAEtD,wBAAgB,GAAG,CAAC,CAAC,SAAS,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAE/C;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;AAIpE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAClC,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,eAAe,CAAC,OAAO,CAAC,KAAG,eACvC,CAAC;AAElC,eAAO,MAAM,iBAAiB,8CAAoB,CAAC;AAInD,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;CAChC;AAyBD,eAAO,MAAM,YAAY,iDAAqB,CAAC;AAS/C,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,kBAAkB,CAqBpG"}
|
package/dist/template/result.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ORE_ERRORS as e,OreApiError as t}from"../errors.js";import{makeBrand as n}from"../utils/brand.js";import{signal as r}from"@vielzeug/ripple";function i(){return r(null)}var a=n(`ore:directive`),o=e=>a.stamp({mount:e}),s=a.is,c=n(`ore:html-result`),l=c.is,u=n=>{if(!l(n))throw new t(e.notAnHtmlResult);return n};function d(n,r){let i=!1,a=()=>{if(i)throw new t(e.htmlResultReused);i=!0};return c.stamp({apply:r,claim:a,fragment:n,mount:(e,t)=>{a();let i=Array.from(n.childNodes);return e.insertBefore(n,t),r(),i}})}export{u as asCompiled,o as createDirectiveResult,d as createHtmlResult,s as isDirectiveResult,l as isHtmlResult,i as ref};
|
|
2
2
|
//# sourceMappingURL=result.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.js","names":["ref","signal","directiveBrand","makeBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","createHtmlResult","applyFn","nodes","fragment","parent","anchor"
|
|
1
|
+
{"version":3,"file":"result.js","names":["ref","signal","directiveBrand","makeBrand","createDirectiveResult","isDirectiveResult","htmlResultBrand","isHtmlResult","asCompiled","result","OreApiError","ORE_ERRORS","createHtmlResult","claimed","claim","applyFn","nodes","fragment","parent","anchor"],"sources":["../../src/template/result.ts"],"sourcesContent":["/**\n * template/result.ts: Branded result objects produced by the template authoring APIs.\n *\n * Runtime code (factories, brand guards) lives here next to the template engine that\n * consumes it; pure binding-shape types live in `binding-types.ts`. All branding goes\n * through `utils/brand.ts` (`Symbol.for`): see that module for why.\n */\n\nimport { type Signal, signal } from '@vielzeug/ripple';\n\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { makeBrand } from '../utils/brand';\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\nexport type Ref<T extends Element> = Signal<T | null>;\n\nexport function ref<T extends Element>(): Ref<T> {\n return signal<T | null>(null);\n}\n\nexport type RefCallback<T extends Element> = (el: T | null) => void;\n\n// ─── Directive result ─────────────────────────────────────────────────────────\n\nexport type DirectiveResult = {\n mount: (anchor: Comment) => void;\n};\n\nconst directiveBrand = makeBrand<DirectiveResult>('ore:directive');\n\n/**\n * Creates a registered DirectiveResult. All directive factories must use this\n * function: only objects created here pass `isDirectiveResult()`.\n */\nexport const createDirectiveResult = (mount: DirectiveResult['mount']): DirectiveResult =>\n directiveBrand.stamp({ mount });\n\nexport const isDirectiveResult = directiveBrand.is;\n\n// ─── HTML result ──────────────────────────────────────────────────────────────\n\ndeclare const htmlResultTag: unique symbol;\n\n/**\n * The output of an `html` tagged template call: an opaque, single-use value.\n *\n * Each `html` call produces an independent fragment bound to its own interpolated\n * values, so it can be rendered exactly once: return it from `setup()`, interpolate\n * it into another template, or return it from a directive callback. Rendering the\n * same result twice throws; call the template function again for a second copy.\n */\nexport interface HTMLResult {\n readonly [htmlResultTag]: true;\n}\n\n/**\n * @internal The engine-facing shape behind every `HTMLResult`. `fragment` and `apply`\n * are the two-phase insertion protocol (static-embed merging in the instantiator,\n * `insertHtmlValues` in the binding layer); `mount` does both in one step. Every\n * consumer calls `claim()` first, which enforces single use.\n */\nexport interface CompiledHTMLResult extends HTMLResult {\n /** Wire up reactive effects to the fragment's nodes. Call after insertion. */\n apply(): void;\n /** Mark this result as consumed; throws `OreApiError` when it already was. */\n claim(): void;\n /** The DOM fragment ready to insert into the document. Consumed on insertion. */\n readonly fragment: DocumentFragment;\n /**\n * Claim, insert the template's nodes before `anchor` (or append to `parent` when\n * `anchor` is null), and wire up all reactive effects. Returns the inserted nodes.\n * Listeners and nested effects register themselves on the active ripple owner.\n */\n mount(parent: ParentNode, anchor: Node | null): Node[];\n}\n\nconst htmlResultBrand = makeBrand<CompiledHTMLResult>('ore:html-result');\n\nexport const isHtmlResult = htmlResultBrand.is;\n\n/** @internal Narrow a public `HTMLResult` to the engine shape, rejecting anything else. */\nexport const asCompiled = (result: HTMLResult): CompiledHTMLResult => {\n if (!isHtmlResult(result)) throw new OreApiError(ORE_ERRORS.notAnHtmlResult);\n\n return result;\n};\n\nexport function createHtmlResult(fragment: DocumentFragment, applyFn: () => void): CompiledHTMLResult {\n let claimed = false;\n\n const claim = (): void => {\n if (claimed) throw new OreApiError(ORE_ERRORS.htmlResultReused);\n\n claimed = true;\n };\n\n const mount = (parent: ParentNode, anchor: Node | null): Node[] => {\n claim();\n\n const nodes = Array.from(fragment.childNodes);\n\n parent.insertBefore(fragment, anchor);\n applyFn();\n\n return nodes;\n };\n\n return htmlResultBrand.stamp({ apply: applyFn, claim, fragment, mount } as unknown as CompiledHTMLResult);\n}\n"],"mappings":"A,mJAiBA,SAAgBA,GAAiC,CAC/C,OAAOC,EAAiB,IAAI,CAC9B,CAUA,IAAMC,EAAiBC,EAA2B,eAAe,EAMpDC,EAAyB,GACpCF,EAAe,MAAM,CAAE,OAAM,CAAC,EAEnBG,EAAoBH,EAAe,GAuC1CI,EAAkBH,EAA8B,iBAAiB,EAE1DI,EAAeD,EAAgB,GAG/BE,EAAc,GAA2C,CACpE,GAAI,CAACD,EAAaE,CAAM,EAAG,MAAM,IAAIC,EAAYC,EAAW,eAAe,EAE3E,OAAOF,CACT,EAEA,SAAgBG,EAAiB,EAA4B,EAAyC,CACpG,IAAIC,EAAU,GAERC,MAAoB,CACxB,GAAID,EAAS,MAAM,IAAIH,EAAYC,EAAW,gBAAgB,EAE9D,EAAU,EACZ,EAaA,OAAOL,EAAgB,MAAM,CAAE,MAAOS,EAAS,QAAO,WAAU,OAXjD,EAAoB,IAAgC,CACjED,EAAM,EAEN,IAAME,EAAQ,MAAM,KAAKC,EAAS,UAAU,EAK5C,OAHAC,EAAO,aAAaD,EAAUE,CAAM,EACpCJ,EAAQ,EAEDC,CACT,CAEsE,CAAkC,CAC1G,C"}
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { type FlushOptions, flush, OreTimeoutError } from './flush';
|
|
9
9
|
export { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';
|
|
10
|
-
export { type HookFixture, renderHook } from './render-hook';
|
|
10
|
+
export { type HookFixture, type RenderHookOptions, renderHook } from './render-hook';
|
|
11
11
|
/**
|
|
12
12
|
* Register auto-cleanup after each test. Call once in your test setup file.
|
|
13
13
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAErF;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,IAAI,CAErE"}
|
package/dist/testing/mount.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../define.cjs"),t=require("../utils/dom.cjs"),n=require("./flush.cjs"),r=require("./
|
|
1
|
+
const e=require("../define.cjs"),t=require("../utils/dom.cjs"),n=require("./flush.cjs"),r=require("./reset.cjs");let i=require("@vielzeug/assay");var a=[],o=0;function s(e,n,r){t.setAttr(e,n,r)}var c=e=>e instanceof Error?e:Error(String(e)),l=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=c(e.error??e.message),e.preventDefault()},r=e=>{t=c(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function u(t,r={}){let{attrs:c={},componentOptions:u,container:d=document.body,html:f,props:p={}}=r,m,h;typeof t==`string`?m=t:(m=`trial-${++o}`,h={...u??{},setup:t}),h&&e.define(m,h);let g=document.createElement(m);f&&(g.innerHTML=f),Object.keys(p).length&&Object.assign(g,p);for(let[e,t]of Object.entries(c))s(g,e,t);try{await l(async()=>{d.appendChild(g),a.push(g),await n.flush()})}catch(e){g.remove();let t=a.indexOf(g);throw t!==-1&&a.splice(t,1),e}let _=!1;function v(){if(_)return;_=!0,g.remove();let e=a.indexOf(g);e!==-1&&a.splice(e,1)}return{...(0,i.within)(g.shadowRoot??g),async act(e){await e(),await n.flush()},async attr(e,t){s(g,e,t),await n.flush()},async attrs(e){for(let[t,n]of Object.entries(e))s(g,t,n);await n.flush()},dispose:v,get disposed(){return _},element:g,flush:n.flush,get shadow(){return g.shadowRoot},[Symbol.dispose](){v()}}}function d(){for(let e of a)e.remove();a.length=0,r.resetOreForTests()}exports._mountedElements=a,exports.cleanup=d,exports.mount=u;
|
|
2
2
|
//# sourceMappingURL=mount.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.cjs","names":["flush","within","require_define","require_dom","require_flush","require_render_hook","require_reset","_vielzeug_assay","_mountedElements","_componentTagCounter","applyAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","html","props","attrs","container","err","i","isDisposed","dispose","fn","record","cleanup","el"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mount.cjs","names":["flush","within","require_define","require_dom","require_flush","require_reset","_vielzeug_assay","_mountedElements","_componentTagCounter","applyAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","html","props","attrs","container","err","i","isDisposed","dispose","fn","record","cleanup","el"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/testing/mount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,SAAS,CAAC;AAOnD;;;GAGG;AACH,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,CAAE,SAAQ,UAAU;IAC9E,0FAA0F;IAC1F,GAAG,CAAC,EAAE,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,+DAA+D;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,yCAAyC;IACzC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,qDAAqD;IACrD,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4BAA4B;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,yDAAyD;IACzD,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IACnC,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/E,gDAAgD;IAChD,SAAS,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC;IACrC,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,KAAK,UAAU,GAAG;IAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;AAG9D,MAAM,MAAM,UAAU,GAAG;IACvB,cAAc,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,GAAG,IAAI,CAAC;CAC1D,CAAC,gBAAgB,CAAC,CAAC;AAIpB,eAAO,MAAM,gBAAgB,EAAE,WAAW,EAAO,CAAC;AAiDlD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC7D,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACvB,wBAAsB,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC7D,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAqGvB;;;;;;GAMG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAI9B"}
|
package/dist/testing/mount.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{define as e}from"../define.js";import{setAttr as t}from"../utils/dom.js";import{flush as n}from"./flush.js";import{
|
|
1
|
+
import{define as e}from"../define.js";import{setAttr as t}from"../utils/dom.js";import{flush as n}from"./flush.js";import{resetOreForTests as r}from"./reset.js";import{within as i}from"@vielzeug/assay";var a=[],o=0;function s(e,n,r){t(e,n,r)}var c=e=>e instanceof Error?e:Error(String(e)),l=async e=>{if(typeof window>`u`)return e();let t=null,n=e=>{t=c(e.error??e.message),e.preventDefault()},r=e=>{t=c(e.reason),e.preventDefault()};window.addEventListener(`error`,n),window.addEventListener(`unhandledrejection`,r);try{let n=await e();if(t)throw t;return n}finally{window.removeEventListener(`error`,n),window.removeEventListener(`unhandledrejection`,r)}};async function u(t,r={}){let{attrs:c={},componentOptions:u,container:d=document.body,html:f,props:p={}}=r,m,h;typeof t==`string`?m=t:(m=`trial-${++o}`,h={...u??{},setup:t}),h&&e(m,h);let g=document.createElement(m);f&&(g.innerHTML=f),Object.keys(p).length&&Object.assign(g,p);for(let[e,t]of Object.entries(c))s(g,e,t);try{await l(async()=>{d.appendChild(g),a.push(g),await n()})}catch(e){g.remove();let t=a.indexOf(g);throw t!==-1&&a.splice(t,1),e}let _=!1;function v(){if(_)return;_=!0,g.remove();let e=a.indexOf(g);e!==-1&&a.splice(e,1)}return{...i(g.shadowRoot??g),async act(e){await e(),await n()},async attr(e,t){s(g,e,t),await n()},async attrs(e){for(let[t,n]of Object.entries(e))s(g,t,n);await n()},dispose:v,get disposed(){return _},element:g,flush:n,get shadow(){return g.shadowRoot},[Symbol.dispose](){v()}}}function d(){for(let e of a)e.remove();a.length=0,r()}export{a as _mountedElements,d as cleanup,u as mount};
|
|
2
2
|
//# sourceMappingURL=mount.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.js","names":["_mountedElements","_componentTagCounter","applyAttr","setAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","define","html","props","attrs","container","flush","err","i","isDisposed","dispose","within","fn","record","cleanup","el","_disposeRenderHooks","resetOreForTests"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { _disposeRenderHooks } from './render-hook';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n _disposeRenderHooks();\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"A,iQAkEA,IAAaA,EAAkC,CAAC,EAK5CC,EAAuB,EAI3B,SAASC,EAAU,EAAkB,EAAc,EAAwC,CAGzFC,EAAQC,EAASC,EAAMC,CAAK,CAC9B,CAEA,IAAMC,EAAW,GACRD,aAAiB,MAAQA,EAAY,MAAM,OAAOA,CAAK,CAAC,EAG3DE,EAAyB,KAAU,IAAyC,CAChF,GAAI,OAAO,OAAW,IAAa,OAAOC,EAAO,EAEjD,IAAIC,EAAyB,KACvBC,EAAW,GAAsB,CACrC,EAAWJ,EAAQK,EAAM,OAASA,EAAM,OAAO,EAC/CA,EAAM,eAAe,CACvB,EACMC,EAAwB,GAAiC,CAC7D,EAAWN,EAAQK,EAAM,MAAM,EAC/BA,EAAM,eAAe,CACvB,EAEA,OAAO,iBAAiB,QAASD,CAAO,EACxC,OAAO,iBAAiB,qBAAsBE,CAAoB,EAElE,GAAI,CACF,IAAMC,EAAS,MAAML,EAAO,EAE5B,GAAIC,EAAU,MAAMA,EAEpB,OAAOI,CACT,QAAU,CACR,OAAO,oBAAoB,QAASH,CAAO,EAC3C,OAAO,oBAAoB,qBAAsBE,CAAoB,CACvE,CACF,EA2BA,eAAsBE,EACpB,EACA,EAAwB,CAAC,EACJ,CACrB,GAAM,CAAE,QAAQ,CAAC,EAAG,mBAAkB,YAAY,SAAS,KAAM,OAAM,QAAQ,CAAC,GAAMC,EAElFC,EACAC,EAEA,OAAOC,GAAe,SACxB,EAAUA,GAEV,EAAU,SAAS,EAAElB,IACrB,EAAmB,CACjB,GAAImB,GAAoB,CAAC,EACzB,MAAOD,CACT,GAGED,GACFG,EAAOJ,EAASC,CAAgB,EAGlC,IAAMd,EAAU,SAAS,cAAca,CAAO,EAE1CK,IAAM,EAAQ,UAAYA,GAE1B,OAAO,KAAKC,CAAK,CAAC,CAAC,QAAQ,OAAO,OAAOnB,EAASmB,CAAK,EAE3D,IAAK,GAAM,CAAClB,EAAMC,KAAU,OAAO,QAAQkB,CAAK,EAAGtB,EAAUE,EAASC,EAAMC,CAAK,EAEjF,GAAI,CACF,MAAME,EAAuB,SAAY,CACvCiB,EAAU,YAAYrB,CAAO,EAC7BJ,EAAiB,KAAKI,CAAO,EAC7B,MAAMsB,EAAM,CACd,CAAC,CACH,OAASC,EAAK,CACZvB,EAAQ,OAAO,EAEf,IAAMwB,EAAI5B,EAAiB,QAAQI,CAAO,EAI1C,MAFIwB,IAAM,IAAI5B,EAAiB,OAAO4B,EAAG,CAAC,EAEpCD,CACR,CAEA,IAAIE,EAAa,GAEjB,SAASC,GAAU,CACjB,GAAID,EAAY,OAEhB,EAAa,GACbzB,EAAQ,OAAO,EAEf,IAAMwB,EAAI5B,EAAiB,QAAQI,CAAO,EAEtCwB,IAAM,IAAI5B,EAAiB,OAAO4B,EAAG,CAAC,CAC5C,CAIA,MAAO,CACL,GAHYG,EAAQ3B,EAAQ,YAAcA,CAGvC,EAEH,MAAM,IAAI,EAAI,CACZ,MAAM4B,EAAG,EACT,MAAMN,EAAM,CACd,EAEA,MAAM,KAAK,EAAM,EAAO,CACtBxB,EAAUE,EAASC,EAAMC,CAAK,EAC9B,MAAMoB,EAAM,CACd,EAEA,MAAM,MAAM,EAAQ,CAClB,IAAK,GAAM,CAACrB,EAAMC,KAAU,OAAO,QAAQ2B,CAAM,EAAG/B,EAAUE,EAASC,EAAMC,CAAK,EAClF,MAAMoB,EAAM,CACd,EAEA,UAEA,IAAI,UAAoB,CACtB,OAAOG,CACT,EAEA,UAEA,QAEA,IAAI,QAA4B,CAC9B,OAAOzB,EAAQ,UACjB,EAEA,CAAC,OAAO,UAAW,CACjB0B,EAAQ,CACV,CACF,CACF,CASA,SAAgBI,GAAgB,CAC9B,IAAK,IAAMC,KAAMnC,EAAkBmC,EAAG,OAAO,EAC7C,EAAiB,OAAS,EAC1BC,EAAoB,EACpBC,EAAiB,CACnB,C"}
|
|
1
|
+
{"version":3,"file":"mount.js","names":["_mountedElements","_componentTagCounter","applyAttr","setAttr","element","name","value","toError","withWindowErrorCapture","action","captured","onError","event","onUnhandledRejection","result","mount","options","tagName","inlineDefinition","tagOrSetup","componentOptions","define","html","props","attrs","container","flush","err","i","isDisposed","dispose","within","fn","record","cleanup","el","resetOreForTests"],"sources":["../../src/testing/mount.ts"],"sourcesContent":["/**\n * Component mounting utilities for test environments.\n */\n\nimport { type QueryScope, within } from '@vielzeug/assay';\nimport type { Readable } from '@vielzeug/ripple';\n\nimport type { ComponentDefinition } from '../component-types';\nimport { define } from '../define';\nimport type { HTMLResult } from '../template/result';\nimport { setAttr } from '../utils/dom';\nimport { type FlushOptions, flush } from './flush';\nimport { resetOreForTests } from './reset';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n// Fixture inherits Assay's complete QueryScope. Keeping that relationship structural means new\n// scoped query capabilities are immediately available to fixtures without a duplicate contract.\n/**\n * A mounted component ready for assertions. All inherited `QueryScope` methods are scoped to\n * `element.shadowRoot`, falling back to `element` for light-DOM components (`shadow: false`).\n */\nexport interface Fixture<T extends HTMLElement = HTMLElement> extends QueryScope {\n /** Run a callback then flush: the standard way to trigger and assert a reactive update */\n act(fn: () => unknown): Promise<void>;\n /** Set an attribute (boolean `false` removes it) then flush */\n attr(name: string, value: string | number | boolean): Promise<void>;\n /** Set multiple attributes then flush */\n attrs(record: Record<string, string | number | boolean>): Promise<void>;\n /** Remove the component from the DOM. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The component element */\n element: T;\n /** Wait for all reactive updates and animation frames */\n flush(options?: FlushOptions): Promise<void>;\n /** The component's shadow root (null for light-DOM components) */\n readonly shadow: ShadowRoot | null;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface MountOptions {\n /** HTML attributes to set on the element */\n attrs?: Record<string, string | number | boolean>;\n /** Extra component options when passing an inline setup function */\n componentOptions?: Omit<ComponentDefinition<Record<string, unknown>>, 'setup'>;\n /** Parent container (default: document.body) */\n container?: HTMLElement | ShadowRoot;\n /** Inner HTML for slot content */\n html?: string;\n /** Properties assigned directly onto the element */\n props?: Record<string, unknown>;\n}\n\ntype MountProps = { readonly [x: string]: Readable<unknown> };\n\n// Bivariant callback type keeps inline test callbacks ergonomic across varying prop specializations.\nexport type MountSetup = {\n bivarianceHack: (props: MountProps) => HTMLResult | null;\n}['bivarianceHack'];\n\n// ─── Test environment state ───────────────────────────────────────────────────\n\nexport const _mountedElements: HTMLElement[] = [];\n\n// Monotonic across the whole test run: never reset: custom element registrations\n// are permanent, so a re-used tag name would throw on re-define. Deterministic\n// within a run (trial-1, trial-2, ...) without a random suffix.\nlet _componentTagCounter = 0;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction applyAttr(element: Element, name: string, value: string | number | boolean): void {\n // Same write path as the runtime's attribute bindings, so tests never set up\n // states the runtime itself can't produce (e.g. boolean true → \"true\").\n setAttr(element, name, value);\n}\n\nconst toError = (value: unknown): Error => {\n return value instanceof Error ? value : new Error(String(value));\n};\n\nconst withWindowErrorCapture = async <T>(action: () => Promise<T>): Promise<T> => {\n if (typeof window === 'undefined') return action();\n\n let captured: Error | null = null;\n const onError = (event: ErrorEvent) => {\n captured = toError(event.error ?? event.message);\n event.preventDefault();\n };\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n captured = toError(event.reason);\n event.preventDefault();\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n try {\n const result = await action();\n\n if (captured) throw captured;\n\n return result;\n } finally {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n }\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Mount a component into the DOM and return a test fixture.\n *\n * Accepts a registered tag name, an inline setup function, or a component\n * options object. Setup functions are auto-registered with generated tag names.\n *\n * @example: inline setup function\n * const { query } = await mount(() => {\n * const count = signal(0);\n * return html`<button @click=${() => count.value++}>${count}</button>`;\n * });\n *\n * @example: registered tag name\n * const { query } = await mount('my-counter');\n */\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: MountSetup,\n options?: MountOptions,\n): Promise<Fixture<T>>;\nexport async function mount<T extends HTMLElement = HTMLElement>(\n tagOrSetup: string | MountSetup,\n options: MountOptions = {},\n): Promise<Fixture<T>> {\n const { attrs = {}, componentOptions, container = document.body, html, props = {} } = options;\n\n let tagName: string;\n let inlineDefinition: ComponentDefinition<Record<string, unknown>> | undefined;\n\n if (typeof tagOrSetup === 'string') {\n tagName = tagOrSetup;\n } else {\n tagName = `trial-${++_componentTagCounter}`;\n inlineDefinition = {\n ...(componentOptions ?? {}),\n setup: tagOrSetup,\n };\n }\n\n if (inlineDefinition) {\n define(tagName, inlineDefinition);\n }\n\n const element = document.createElement(tagName) as T;\n\n if (html) element.innerHTML = html;\n\n if (Object.keys(props).length) Object.assign(element, props);\n\n for (const [name, value] of Object.entries(attrs)) applyAttr(element, name, value);\n\n try {\n await withWindowErrorCapture(async () => {\n container.appendChild(element);\n _mountedElements.push(element);\n await flush();\n });\n } catch (err) {\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n\n throw err;\n }\n\n let isDisposed = false;\n\n function dispose() {\n if (isDisposed) return;\n\n isDisposed = true;\n element.remove();\n\n const i = _mountedElements.indexOf(element);\n\n if (i !== -1) _mountedElements.splice(i, 1);\n }\n\n const scope = within((element.shadowRoot ?? element) as Element);\n\n return {\n ...scope,\n\n async act(fn) {\n await fn();\n await flush();\n },\n\n async attr(name, value) {\n applyAttr(element, name, value);\n await flush();\n },\n\n async attrs(record) {\n for (const [name, value] of Object.entries(record)) applyAttr(element, name, value);\n await flush();\n },\n\n dispose,\n\n get disposed(): boolean {\n return isDisposed;\n },\n\n element,\n\n flush,\n\n get shadow(): ShadowRoot | null {\n return element.shadowRoot;\n },\n\n [Symbol.dispose]() {\n dispose();\n },\n };\n}\n\n/**\n * Remove all elements mounted via `mount()`.\n * Call in `afterEach` to keep tests isolated.\n *\n * @example\n * afterEach(() => cleanup());\n */\nexport function cleanup(): void {\n for (const el of _mountedElements) el.remove();\n _mountedElements.length = 0;\n resetOreForTests();\n}\n\n/** @internal re-export for within() */\nexport type { QueryScope };\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("
|
|
1
|
+
const e=require("./mount.cjs");async function t(t,n,r){let i=typeof t!=`function`,a=i?n:t,o=i?r:n,s,c=await e.mount(e=>(s=a(e),null),{componentOptions:{formAssociated:o?.formAssociated,props:i?t:void 0}});return{dispose:c.dispose,get disposed(){return c.disposed},element:c.element,flush:()=>c.flush(),result:s,[Symbol.dispose]:c.dispose}}exports.renderHook=t;
|
|
2
2
|
//# sourceMappingURL=render-hook.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-hook.cjs","names":["
|
|
1
|
+
{"version":3,"file":"render-hook.cjs","names":["mount","require_mount","renderHook","hasPropDefs","setupOrDefs","setup","setupOrOptions","options","maybeOptions","result","fixture","props"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without writing a component template.\n */\n\nimport type { InferProps, PropInputDefs } from '../props';\nimport { mount } from './mount';\n\nexport interface HookFixture<T> {\n /** Remove the host component; its cleanups run one microtask later, like `mount().dispose()`. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The generated host component running the hook. */\n readonly element: HTMLElement;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface RenderHookOptions {\n /** Register the host with `formAssociated: true`, for composables that call `useField()`. */\n formAssociated?: boolean;\n}\n\n/**\n * Run a setup function inside a real, generated host component and return its result.\n * Every lifecycle hook (`onMounted`, `onCleanup`, `onFormReset`, `useField`, ...) runs\n * exactly as it does in `define()`: the host is an ordinary Ore component.\n *\n * @example No props\n * ```ts\n * const { result } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook({ label: prop.string('hello') }, (props) => props.label);\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(\n setup: (_props: Record<never, never>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n setupOrOptions?: ((props: InferProps<D>) => T) | RenderHookOptions,\n maybeOptions?: RenderHookOptions,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const setup = (hasPropDefs ? setupOrOptions : setupOrDefs) as (props: unknown) => T;\n const options = (hasPropDefs ? maybeOptions : setupOrOptions) as RenderHookOptions | undefined;\n\n let result!: T;\n\n const fixture = await mount(\n (props) => {\n result = setup(props);\n\n return null;\n },\n {\n componentOptions: {\n formAssociated: options?.formAssociated,\n props: hasPropDefs ? (setupOrDefs as D) : undefined,\n },\n },\n );\n\n return {\n dispose: fixture.dispose,\n get disposed() {\n return fixture.disposed;\n },\n element: fixture.element,\n flush: () => fixture.flush(),\n result,\n [Symbol.dispose]: fixture.dispose,\n };\n}\n"],"mappings":"A,+BA0DA,eAAsBE,EACpB,EACA,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAASF,EAAcG,EAAiBF,EACxCG,EAAWJ,EAAcK,EAAeF,EAE1CG,EAEEC,EAAU,MAAMV,EAAAA,MACnB,IACC,EAASK,EAAMM,CAAK,EAEb,MAET,CACE,iBAAkB,CAChB,eAAgBJ,GAAS,eACzB,MAAOJ,EAAeC,EAAoB,IAAA,EAC5C,CACF,CACF,EAEA,MAAO,CACL,QAASM,EAAQ,QACjB,IAAI,UAAW,CACb,OAAOA,EAAQ,QACjB,EACA,QAASA,EAAQ,QACjB,UAAaA,EAAQ,MAAM,EAC3B,UACC,OAAO,SAAUA,EAAQ,OAC5B,CACF,C"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation
|
|
3
|
-
* without
|
|
3
|
+
* without writing a component template.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import type { InferProps, PropInputDefs } from '../props';
|
|
6
6
|
export interface HookFixture<T> {
|
|
7
|
-
/**
|
|
7
|
+
/** Remove the host component; its cleanups run one microtask later, like `mount().dispose()`. Idempotent. */
|
|
8
8
|
dispose(): void;
|
|
9
9
|
/** `true` after `dispose()` has been called. */
|
|
10
10
|
readonly disposed: boolean;
|
|
11
|
+
/** The generated host component running the hook. */
|
|
12
|
+
readonly element: HTMLElement;
|
|
11
13
|
/** Flush pending reactive updates */
|
|
12
14
|
flush(): Promise<void>;
|
|
13
15
|
/** The value returned by the setup function */
|
|
@@ -15,14 +17,18 @@ export interface HookFixture<T> {
|
|
|
15
17
|
/** Delegates to `dispose()`. Enables `using` declarations. */
|
|
16
18
|
[Symbol.dispose](): void;
|
|
17
19
|
}
|
|
20
|
+
export interface RenderHookOptions {
|
|
21
|
+
/** Register the host with `formAssociated: true`, for composables that call `useField()`. */
|
|
22
|
+
formAssociated?: boolean;
|
|
23
|
+
}
|
|
18
24
|
/**
|
|
19
|
-
* Run a setup function
|
|
20
|
-
*
|
|
21
|
-
* `
|
|
25
|
+
* Run a setup function inside a real, generated host component and return its result.
|
|
26
|
+
* Every lifecycle hook (`onMounted`, `onCleanup`, `onFormReset`, `useField`, ...) runs
|
|
27
|
+
* exactly as it does in `define()`: the host is an ordinary Ore component.
|
|
22
28
|
*
|
|
23
29
|
* @example No props
|
|
24
30
|
* ```ts
|
|
25
|
-
* const { result
|
|
31
|
+
* const { result } = await renderHook(() => {
|
|
26
32
|
* const count = signal(0);
|
|
27
33
|
* onMounted(() => { count.value = 1; });
|
|
28
34
|
* return count;
|
|
@@ -32,13 +38,10 @@ export interface HookFixture<T> {
|
|
|
32
38
|
*
|
|
33
39
|
* @example With props
|
|
34
40
|
* ```ts
|
|
35
|
-
* const { result } = await renderHook(
|
|
36
|
-
* { label: prop.string('hello') },
|
|
37
|
-
* (props) => props.label,
|
|
38
|
-
* );
|
|
41
|
+
* const { result } = await renderHook({ label: prop.string('hello') }, (props) => props.label);
|
|
39
42
|
* expect(result.value).toBe('hello');
|
|
40
43
|
* ```
|
|
41
44
|
*/
|
|
42
|
-
export declare function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;
|
|
43
|
-
export declare function renderHook<D extends PropInputDefs, T>(propDefs: D, setup: (props: InferProps<D>) => T): Promise<HookFixture<T>>;
|
|
45
|
+
export declare function renderHook<T>(setup: (_props: Record<never, never>) => T, options?: RenderHookOptions): Promise<HookFixture<T>>;
|
|
46
|
+
export declare function renderHook<D extends PropInputDefs, T>(propDefs: D, setup: (props: InferProps<D>) => T, options?: RenderHookOptions): Promise<HookFixture<T>>;
|
|
44
47
|
//# sourceMappingURL=render-hook.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-hook.d.ts","sourceRoot":"","sources":["../../src/testing/render-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"render-hook.d.ts","sourceRoot":"","sources":["../../src/testing/render-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG1D,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,6GAA6G;IAC7G,OAAO,IAAI,IAAI,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,qCAAqC;IACrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+CAA+C;IAC/C,MAAM,EAAE,CAAC,CAAC;IACV,8DAA8D;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3B,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EAAE,CAAC,EACzD,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,EAClC,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{mount as e}from"./mount.js";async function t(t,n,r){let i=typeof t!=`function`,a=i?n:t,o=i?r:n,s,c=await e(e=>(s=a(e),null),{componentOptions:{formAssociated:o?.formAssociated,props:i?t:void 0}});return{dispose:c.dispose,get disposed(){return c.disposed},element:c.element,flush:()=>c.flush(),result:s,[Symbol.dispose]:c.dispose}}export{t as renderHook};
|
|
2
2
|
//# sourceMappingURL=render-hook.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-hook.js","names":["
|
|
1
|
+
{"version":3,"file":"render-hook.js","names":["renderHook","hasPropDefs","setupOrDefs","setup","setupOrOptions","options","maybeOptions","result","fixture","mount","props"],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook: test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without writing a component template.\n */\n\nimport type { InferProps, PropInputDefs } from '../props';\nimport { mount } from './mount';\n\nexport interface HookFixture<T> {\n /** Remove the host component; its cleanups run one microtask later, like `mount().dispose()`. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()` has been called. */\n readonly disposed: boolean;\n /** The generated host component running the hook. */\n readonly element: HTMLElement;\n /** Flush pending reactive updates */\n flush(): Promise<void>;\n /** The value returned by the setup function */\n result: T;\n /** Delegates to `dispose()`. Enables `using` declarations. */\n [Symbol.dispose](): void;\n}\n\nexport interface RenderHookOptions {\n /** Register the host with `formAssociated: true`, for composables that call `useField()`. */\n formAssociated?: boolean;\n}\n\n/**\n * Run a setup function inside a real, generated host component and return its result.\n * Every lifecycle hook (`onMounted`, `onCleanup`, `onFormReset`, `useField`, ...) runs\n * exactly as it does in `define()`: the host is an ordinary Ore component.\n *\n * @example No props\n * ```ts\n * const { result } = await renderHook(() => {\n * const count = signal(0);\n * onMounted(() => { count.value = 1; });\n * return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook({ label: prop.string('hello') }, (props) => props.label);\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(\n setup: (_props: Record<never, never>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n propDefs: D,\n setup: (props: InferProps<D>) => T,\n options?: RenderHookOptions,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n setupOrDefs: ((_props: Record<never, never>) => T) | D,\n setupOrOptions?: ((props: InferProps<D>) => T) | RenderHookOptions,\n maybeOptions?: RenderHookOptions,\n): Promise<HookFixture<T>> {\n const hasPropDefs = typeof setupOrDefs !== 'function';\n const setup = (hasPropDefs ? setupOrOptions : setupOrDefs) as (props: unknown) => T;\n const options = (hasPropDefs ? maybeOptions : setupOrOptions) as RenderHookOptions | undefined;\n\n let result!: T;\n\n const fixture = await mount(\n (props) => {\n result = setup(props);\n\n return null;\n },\n {\n componentOptions: {\n formAssociated: options?.formAssociated,\n props: hasPropDefs ? (setupOrDefs as D) : undefined,\n },\n },\n );\n\n return {\n dispose: fixture.dispose,\n get disposed() {\n return fixture.disposed;\n },\n element: fixture.element,\n flush: () => fixture.flush(),\n result,\n [Symbol.dispose]: fixture.dispose,\n };\n}\n"],"mappings":"A,mCA0DA,eAAsBA,EACpB,EACA,EACA,EACyB,CACzB,IAAMC,EAAc,OAAOC,GAAgB,WACrCC,EAASF,EAAcG,EAAiBF,EACxCG,EAAWJ,EAAcK,EAAeF,EAE1CG,EAEEC,EAAU,MAAMC,EACnB,IACC,EAASN,EAAMO,CAAK,EAEb,MAET,CACE,iBAAkB,CAChB,eAAgBL,GAAS,eACzB,MAAOJ,EAAeC,EAAoB,IAAA,EAC5C,CACF,CACF,EAEA,MAAO,CACL,QAASM,EAAQ,QACjB,IAAI,UAAW,CACb,OAAOA,EAAQ,QACjB,EACA,QAASA,EAAQ,QACjB,UAAaA,EAAQ,MAAM,EAC3B,UACC,OAAO,SAAUA,EAAQ,OAC5B,CACF,C"}
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.cjs","names":["cleanup","require_mount","require_flush","require_render_hook","install","afterEachHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,kLA6BA,SAAgBI,EAAQ,EAA+C,CACrEC,EAAcL,EAAAA,OAAO,CACvB,C"}
|
|
1
|
+
{"version":3,"file":"testing.cjs","names":["cleanup","require_mount","require_flush","require_render_hook","install","afterEachHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, type RenderHookOptions, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,kLA6BA,SAAgBI,EAAQ,EAA+C,CACrEC,EAAcL,EAAAA,OAAO,CACvB,C"}
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","names":["install","afterEachHook","cleanup","OreTimeoutError","flush","mount","renderHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,8KA6BA,SAAgBA,EAAQ,EAA+C,CACrEC,EAAcC,CAAO,CACvB,C"}
|
|
1
|
+
{"version":3,"file":"testing.js","names":["install","afterEachHook","cleanup","OreTimeoutError","flush","mount","renderHook"],"sources":["../src/testing/index.ts"],"sourcesContent":["/**\n * Testing utilities for Ore components\n *\n * ⚠️ Requires DOM environment (browser / jsdom / happy-dom)\n *\n * This is the package's only public testing entry point.\n */\n\nimport { cleanup } from './mount';\n\n// Testing-only error: flush() is its only thrower in this package (a pending-work timeout :\n// see flush.ts). Exported here (not from the main `.` entry) so components with no interest\n// in the testing sub-path don't carry a testing-only error class in their public type surface.\n// Generic DOM interaction helpers belong to @vielzeug/assay, not this Ore-specific test API:\n// including the jsdom ElementInternals/FormData polyfill, which form-associated-component\n// suites install directly from assay (`installFormInternalsPolyfill`).\nexport { type FlushOptions, flush, OreTimeoutError } from './flush';\nexport { cleanup, type Fixture, type MountOptions, type MountSetup, mount } from './mount';\nexport { type HookFixture, type RenderHookOptions, renderHook } from './render-hook';\n\n/**\n * Register auto-cleanup after each test. Call once in your test setup file.\n *\n * @example\n * // vitest.setup.ts\n * import { afterEach } from 'vitest';\n * import { install } from '@vielzeug/ore/testing';\n * install(afterEach);\n */\nexport function install(afterEachHook: (fn: () => void) => void): void {\n afterEachHook(cleanup);\n}\n"],"mappings":"A,8KA6BA,SAAgBA,EAAQ,EAA+C,CACrEC,EAAcC,CAAO,CACvB,C"}
|
package/dist/utils/brand.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand.cjs","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `
|
|
1
|
+
{"version":3,"file":"brand.cjs","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `ModelSource`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
|
package/dist/utils/brand.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail
|
|
8
8
|
* across those copies: an object stamped by one graph is invisible to the other.
|
|
9
9
|
* Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,
|
|
10
|
-
* `
|
|
10
|
+
* `ModelSource`, `CSSResult`) goes through this helper: do not introduce a second mechanism.
|
|
11
11
|
*/
|
|
12
12
|
export type Brand<T extends object> = {
|
|
13
13
|
is: (value: unknown) => value is T;
|
package/dist/utils/brand.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand.js","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `
|
|
1
|
+
{"version":3,"file":"brand.js","names":["makeBrand","BRAND","key","value","obj"],"sources":["../../src/utils/brand.ts"],"sourcesContent":["/**\n * The package's single object-branding mechanism: a `Symbol.for`-keyed property stamp.\n *\n * Why `Symbol.for` and not identity checks (`WeakSet`, `instanceof`): `Symbol.for` keys are\n * process-global, so brands survive a duplicated module graph (two copies of ore bundled into\n * one page: possible whenever a consumer and a dependency each bundle their own ore, or an\n * IIFE `window.Ore` runtime coexists with an npm import). Identity-based checks silently fail\n * across those copies: an object stamped by one graph is invisible to the other.\n * Every branded runtime object in ore (`HTMLResult`, `DirectiveResult`,\n * `ModelSource`, `CSSResult`) goes through this helper: do not introduce a second mechanism.\n */\n\nexport type Brand<T extends object> = {\n is: (value: unknown) => value is T;\n stamp: (obj: T) => T;\n};\n\nexport const makeBrand = <T extends object>(key: string): Brand<T> => {\n const BRAND = Symbol.for(key);\n const stamp = (obj: T): T => Object.assign(obj, { [BRAND]: true });\n const is = (value: unknown): value is T => typeof value === 'object' && value !== null && BRAND in (value as object);\n\n return { is, stamp };\n};\n"],"mappings":"AAiBA,IAAaA,EAA+B,GAA0B,CACpE,IAAMC,EAAQ,OAAO,IAAIC,CAAG,EAI5B,MAAO,CAAE,GAFG,GAA+B,OAAOC,GAAU,YAAYA,GAAkBF,KAAUE,EAEvF,MAHE,GAAc,OAAO,OAAOC,EAAK,EAAGH,GAAQ,EAAK,CAAC,CAG9C,CACrB,E"}
|
package/dist/utils/dom.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require("../_dev.cjs");const e=require("../errors.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t
|
|
1
|
+
require("../_dev.cjs");const e=require("../errors.cjs");let t=require("@vielzeug/ripple");var n=e=>typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e,r=/[;{}]/g,i=e=>e.replace(r,``),a=e=>{for(let t of e)t.remove()},o=()=>{let e=[],n=null,r=()=>{n?.dispose(),n=null,a(e),e=[]};return{clear:r,get nodes(){return e},render(e){r();let i=(0,t.createScope)();n=i;try{return i.run(e)}catch(e){throw i.dispose(),n=null,e}},setNodes(t){e=t}}},s=new Set([`action`,`cite`,`codebase`,`data`,`formaction`,`href`,`manifest`,`ping`,`poster`,`src`,`xlink:href`]),c=/^\s*(?:(?:javascript|vbscript|blob):|data:(?:[^,]*\/(?:html|svg\+xml)|application\/(?:xhtml|xml)))/i,l=/[\t\n\r]/g,u=/^[\s\p{C}]+|[\s\p{C}]+$/gu,d=e=>e.replace(l,``).replace(u,``),f=e=>c.test(d(e)),p=new Map,m=512,h=e=>{let t=p.get(e);if(t)return t;let n=e.toLowerCase(),r={event:/^on[a-z]/i.test(e),srcdoc:n===`srcdoc`,url:s.has(n)};return p.size>=m&&p.clear(),p.set(e,r),r},g=(e,t,n)=>{let r=h(t);if(r.event){`${t}${t.slice(2)}`,e.removeAttribute(t);return}if(r.srcdoc||n==null||n===!1){e.removeAttribute(t);return}let i=n===!0?`true`:String(n);r.url&&f(i)?(`${t}`,e.removeAttribute(t)):e.setAttribute(t,i)},_=(t,n,r,i)=>{if(!t)return e.ORE_ERRORS.listenNullTarget(n),()=>{};let a=r;return t.addEventListener(n,a,i),()=>t.removeEventListener(n,a,i)},v=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),y=e=>i(e.startsWith(`--`)?e:v(e)),b=e=>{let t=n(e);return t==null||t===!1?``:i(String(t))},x=e=>{let t=new Set;for(let[r,i]of Object.entries(e)){if(!n(i))continue;let e=r.replace(/\s+/g,``);e&&t.add(e)}return t},S=e=>Array.isArray(e)||typeof e==`object`&&!!e;exports.UNSAFE_CSS_CHARS=r,exports.createReplaceableSlot=o,exports.cssDeclarationName=y,exports.cssDeclarationValue=b,exports.isStructuredValue=S,exports.listen=_,exports.removeNodes=a,exports.resolveClassTokens=x,exports.resolveMaybeReactive=n,exports.sanitizeCssToken=i,exports.setAttr=g,exports.toKebab=v;
|
|
2
2
|
//# sourceMappingURL=dom.cjs.map
|