@vielzeug/ore 1.0.3
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 +73 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +2 -0
- package/dist/_dev.js.map +1 -0
- package/dist/aria.cjs +2 -0
- package/dist/aria.cjs.map +1 -0
- package/dist/aria.d.ts +12 -0
- package/dist/aria.d.ts.map +1 -0
- package/dist/aria.js +2 -0
- package/dist/aria.js.map +1 -0
- package/dist/base-element.cjs +2 -0
- package/dist/base-element.cjs.map +1 -0
- package/dist/base-element.d.ts +22 -0
- package/dist/base-element.d.ts.map +1 -0
- package/dist/base-element.js +2 -0
- package/dist/base-element.js.map +1 -0
- package/dist/component-types.d.ts +104 -0
- package/dist/component-types.d.ts.map +1 -0
- package/dist/context-bag.cjs +2 -0
- package/dist/context-bag.cjs.map +1 -0
- package/dist/context-bag.d.ts +12 -0
- package/dist/context-bag.d.ts.map +1 -0
- package/dist/context-bag.js +2 -0
- package/dist/context-bag.js.map +1 -0
- package/dist/context.cjs +2 -0
- package/dist/context.cjs.map +1 -0
- package/dist/context.d.ts +15 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/define.cjs +2 -0
- package/dist/define.cjs.map +1 -0
- package/dist/define.d.ts +33 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +2 -0
- package/dist/define.js.map +1 -0
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.ts +32 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +2 -0
- package/dist/devtools.js.map +1 -0
- package/dist/directives/classMap.cjs +2 -0
- package/dist/directives/classMap.cjs.map +1 -0
- package/dist/directives/classMap.d.ts +19 -0
- package/dist/directives/classMap.d.ts.map +1 -0
- package/dist/directives/classMap.js +2 -0
- package/dist/directives/classMap.js.map +1 -0
- package/dist/directives/each.cjs +2 -0
- package/dist/directives/each.cjs.map +1 -0
- package/dist/directives/each.d.ts +25 -0
- package/dist/directives/each.d.ts.map +1 -0
- package/dist/directives/each.js +2 -0
- package/dist/directives/each.js.map +1 -0
- package/dist/directives/index.d.ts +8 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/directives/live.cjs +2 -0
- package/dist/directives/live.cjs.map +1 -0
- package/dist/directives/live.d.ts +25 -0
- package/dist/directives/live.d.ts.map +1 -0
- package/dist/directives/live.js +2 -0
- package/dist/directives/live.js.map +1 -0
- package/dist/directives/model.cjs +2 -0
- package/dist/directives/model.cjs.map +1 -0
- package/dist/directives/model.d.ts +28 -0
- package/dist/directives/model.d.ts.map +1 -0
- package/dist/directives/model.js +2 -0
- package/dist/directives/model.js.map +1 -0
- package/dist/directives/raw.cjs +2 -0
- package/dist/directives/raw.cjs.map +1 -0
- package/dist/directives/raw.d.ts +16 -0
- package/dist/directives/raw.d.ts.map +1 -0
- package/dist/directives/raw.js +2 -0
- package/dist/directives/raw.js.map +1 -0
- package/dist/directives/styleMap.cjs +2 -0
- package/dist/directives/styleMap.cjs.map +1 -0
- package/dist/directives/styleMap.d.ts +25 -0
- package/dist/directives/styleMap.d.ts.map +1 -0
- package/dist/directives/styleMap.js +2 -0
- package/dist/directives/styleMap.js.map +1 -0
- package/dist/directives/when.cjs +2 -0
- package/dist/directives/when.cjs.map +1 -0
- package/dist/directives/when.d.ts +19 -0
- package/dist/directives/when.d.ts.map +1 -0
- package/dist/directives/when.js +2 -0
- package/dist/directives/when.js.map +1 -0
- package/dist/directives.cjs +1 -0
- package/dist/directives.js +1 -0
- package/dist/errors.cjs +3 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +47 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -0
- package/dist/form-context.cjs +2 -0
- package/dist/form-context.cjs.map +1 -0
- package/dist/form-context.d.ts +56 -0
- package/dist/form-context.d.ts.map +1 -0
- package/dist/form-context.js +2 -0
- package/dist/form-context.js.map +1 -0
- package/dist/form-field.cjs +2 -0
- package/dist/form-field.cjs.map +1 -0
- package/dist/form-field.d.ts +31 -0
- package/dist/form-field.d.ts.map +1 -0
- package/dist/form-field.js +2 -0
- package/dist/form-field.js.map +1 -0
- package/dist/host-bind.cjs +2 -0
- package/dist/host-bind.cjs.map +1 -0
- package/dist/host-bind.d.ts +37 -0
- package/dist/host-bind.d.ts.map +1 -0
- package/dist/host-bind.js +2 -0
- package/dist/host-bind.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/observers/index.d.ts +5 -0
- package/dist/observers/index.d.ts.map +1 -0
- package/dist/observers/intersection-observe.cjs +2 -0
- package/dist/observers/intersection-observe.cjs.map +1 -0
- package/dist/observers/intersection-observe.d.ts +9 -0
- package/dist/observers/intersection-observe.d.ts.map +1 -0
- package/dist/observers/intersection-observe.js +2 -0
- package/dist/observers/intersection-observe.js.map +1 -0
- package/dist/observers/media-observe.cjs +2 -0
- package/dist/observers/media-observe.cjs.map +1 -0
- package/dist/observers/media-observe.d.ts +8 -0
- package/dist/observers/media-observe.d.ts.map +1 -0
- package/dist/observers/media-observe.js +2 -0
- package/dist/observers/media-observe.js.map +1 -0
- package/dist/observers/mutation-observe.cjs +2 -0
- package/dist/observers/mutation-observe.cjs.map +1 -0
- package/dist/observers/mutation-observe.d.ts +10 -0
- package/dist/observers/mutation-observe.d.ts.map +1 -0
- package/dist/observers/mutation-observe.js +2 -0
- package/dist/observers/mutation-observe.js.map +1 -0
- package/dist/observers/resize-observe.cjs +2 -0
- package/dist/observers/resize-observe.cjs.map +1 -0
- package/dist/observers/resize-observe.d.ts +11 -0
- package/dist/observers/resize-observe.d.ts.map +1 -0
- package/dist/observers/resize-observe.js +2 -0
- package/dist/observers/resize-observe.js.map +1 -0
- package/dist/observers.cjs +1 -0
- package/dist/observers.js +1 -0
- package/dist/ore.cjs +3 -0
- package/dist/ore.cjs.map +1 -0
- package/dist/ore.iife.js +3 -0
- package/dist/ore.iife.js.map +1 -0
- package/dist/ore.js +3 -0
- package/dist/ore.js.map +1 -0
- package/dist/props.cjs +2 -0
- package/dist/props.cjs.map +1 -0
- package/dist/props.d.ts +76 -0
- package/dist/props.d.ts.map +1 -0
- package/dist/props.js +2 -0
- package/dist/props.js.map +1 -0
- package/dist/runtime.cjs +2 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2 -0
- package/dist/runtime.js.map +1 -0
- package/dist/slots.cjs +2 -0
- package/dist/slots.cjs.map +1 -0
- package/dist/slots.d.ts +13 -0
- package/dist/slots.d.ts.map +1 -0
- package/dist/slots.js +2 -0
- package/dist/slots.js.map +1 -0
- package/dist/template/bindings.cjs +2 -0
- package/dist/template/bindings.cjs.map +1 -0
- package/dist/template/bindings.d.ts +17 -0
- package/dist/template/bindings.d.ts.map +1 -0
- package/dist/template/bindings.js +2 -0
- package/dist/template/bindings.js.map +1 -0
- package/dist/template/compiler.cjs +2 -0
- package/dist/template/compiler.cjs.map +1 -0
- package/dist/template/compiler.d.ts +27 -0
- package/dist/template/compiler.d.ts.map +1 -0
- package/dist/template/compiler.js +2 -0
- package/dist/template/compiler.js.map +1 -0
- package/dist/template/instantiator.cjs +2 -0
- package/dist/template/instantiator.cjs.map +1 -0
- package/dist/template/instantiator.d.ts +16 -0
- package/dist/template/instantiator.d.ts.map +1 -0
- package/dist/template/instantiator.js +2 -0
- package/dist/template/instantiator.js.map +1 -0
- package/dist/template.cjs +1 -0
- package/dist/template.d.ts +4 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +1 -0
- package/dist/testing/events.cjs +2 -0
- package/dist/testing/events.cjs.map +1 -0
- package/dist/testing/events.d.ts +37 -0
- package/dist/testing/events.d.ts.map +1 -0
- package/dist/testing/events.js +2 -0
- package/dist/testing/events.js.map +1 -0
- package/dist/testing/flush.cjs +2 -0
- package/dist/testing/flush.cjs.map +1 -0
- package/dist/testing/flush.d.ts +55 -0
- package/dist/testing/flush.d.ts.map +1 -0
- package/dist/testing/flush.js +2 -0
- package/dist/testing/flush.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/interactions.cjs +2 -0
- package/dist/testing/interactions.cjs.map +1 -0
- package/dist/testing/interactions.d.ts +27 -0
- package/dist/testing/interactions.d.ts.map +1 -0
- package/dist/testing/interactions.js +2 -0
- package/dist/testing/interactions.js.map +1 -0
- package/dist/testing/mount.cjs +2 -0
- package/dist/testing/mount.cjs.map +1 -0
- package/dist/testing/mount.d.ts +107 -0
- package/dist/testing/mount.d.ts.map +1 -0
- package/dist/testing/mount.js +2 -0
- package/dist/testing/mount.js.map +1 -0
- package/dist/testing/query.cjs +2 -0
- package/dist/testing/query.cjs.map +1 -0
- package/dist/testing/query.d.ts +24 -0
- package/dist/testing/query.d.ts.map +1 -0
- package/dist/testing/query.js +2 -0
- package/dist/testing/query.js.map +1 -0
- package/dist/testing/render-hook.cjs +2 -0
- package/dist/testing/render-hook.cjs.map +1 -0
- package/dist/testing/render-hook.d.ts +44 -0
- package/dist/testing/render-hook.d.ts.map +1 -0
- package/dist/testing/render-hook.js +2 -0
- package/dist/testing/render-hook.js.map +1 -0
- package/dist/testing/testing.cjs +2 -0
- package/dist/testing/testing.cjs.map +1 -0
- package/dist/testing/testing.d.ts +26 -0
- package/dist/testing/testing.d.ts.map +1 -0
- package/dist/testing/testing.js +2 -0
- package/dist/testing/testing.js.map +1 -0
- package/dist/testing/wait.cjs +2 -0
- package/dist/testing/wait.cjs.map +1 -0
- package/dist/testing/wait.d.ts +35 -0
- package/dist/testing/wait.d.ts.map +1 -0
- package/dist/testing/wait.js +2 -0
- package/dist/testing/wait.js.map +1 -0
- package/dist/testing.cjs +1 -0
- package/dist/testing.js +1 -0
- package/dist/types/bindings.cjs +2 -0
- package/dist/types/bindings.cjs.map +1 -0
- package/dist/types/bindings.d.ts +125 -0
- package/dist/types/bindings.d.ts.map +1 -0
- package/dist/types/bindings.js +2 -0
- package/dist/types/bindings.js.map +1 -0
- package/dist/types.cjs +2 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/aria.cjs +2 -0
- package/dist/utils/aria.cjs.map +1 -0
- package/dist/utils/aria.d.ts +16 -0
- package/dist/utils/aria.d.ts.map +1 -0
- package/dist/utils/aria.js +2 -0
- package/dist/utils/aria.js.map +1 -0
- package/dist/utils/css.cjs +2 -0
- package/dist/utils/css.cjs.map +1 -0
- package/dist/utils/css.d.ts +11 -0
- package/dist/utils/css.d.ts.map +1 -0
- package/dist/utils/css.js +2 -0
- package/dist/utils/css.js.map +1 -0
- package/dist/utils/dom.cjs +2 -0
- package/dist/utils/dom.cjs.map +1 -0
- package/dist/utils/dom.d.ts +10 -0
- package/dist/utils/dom.d.ts.map +1 -0
- package/dist/utils/dom.js +2 -0
- package/dist/utils/dom.js.map +1 -0
- package/dist/utils/emit.cjs +2 -0
- package/dist/utils/emit.cjs.map +1 -0
- package/dist/utils/emit.d.ts +16 -0
- package/dist/utils/emit.d.ts.map +1 -0
- package/dist/utils/emit.js +2 -0
- package/dist/utils/emit.js.map +1 -0
- package/dist/utils/event-modifiers.cjs +2 -0
- package/dist/utils/event-modifiers.cjs.map +1 -0
- package/dist/utils/event-modifiers.d.ts +11 -0
- package/dist/utils/event-modifiers.d.ts.map +1 -0
- package/dist/utils/event-modifiers.js +2 -0
- package/dist/utils/event-modifiers.js.map +1 -0
- package/dist/utils/id.cjs +2 -0
- package/dist/utils/id.cjs.map +1 -0
- package/dist/utils/id.d.ts +18 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +2 -0
- package/dist/utils/id.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vielzeug/ore — debug utilities for component update visualisation.
|
|
3
|
+
*
|
|
4
|
+
* Import from the dedicated sub-path so it is tree-shaken from production bundles:
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { debugFlush } from '@vielzeug/ore/devtools';
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
import type { FlushOptions } from './testing/flush';
|
|
10
|
+
/**
|
|
11
|
+
* Flushes pending reactive updates with debug logging pre-wired to `console.debug`.
|
|
12
|
+
*
|
|
13
|
+
* Equivalent to `flush({ ...options, logger: console.debug })` but imported from a
|
|
14
|
+
* dedicated sub-path so the `console.debug` reference is tree-shaken from production
|
|
15
|
+
* bundles when this sub-path is not imported.
|
|
16
|
+
*
|
|
17
|
+
* Logs each turn and timing phase to `console.debug` so you can diagnose
|
|
18
|
+
* unexpected update order or timing issues.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { debugFlush } from '@vielzeug/ore/devtools';
|
|
23
|
+
*
|
|
24
|
+
* // in a test
|
|
25
|
+
* await debugFlush();
|
|
26
|
+
* // [flush] starting with maxTurns=5
|
|
27
|
+
* // [flush] microtask turn 1/5
|
|
28
|
+
* // ...
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function debugFlush(options?: Omit<FlushOptions, 'logger'>): Promise<void>;
|
|
32
|
+
//# sourceMappingURL=devtools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtF"}
|
package/dist/devtools.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/ore — debug utilities for component update visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugFlush } from '@vielzeug/ore/devtools';\n * ```\n */\n\nimport type { FlushOptions } from './testing/flush';\n\nimport { flush } from './testing/flush';\n\n/**\n * Flushes pending reactive updates with debug logging pre-wired to `console.debug`.\n *\n * Equivalent to `flush({ ...options, logger: console.debug })` but imported from a\n * dedicated sub-path so the `console.debug` reference is tree-shaken from production\n * bundles when this sub-path is not imported.\n *\n * Logs each turn and timing phase to `console.debug` so you can diagnose\n * unexpected update order or timing issues.\n *\n * @example\n * ```ts\n * import { debugFlush } from '@vielzeug/ore/devtools';\n *\n * // in a test\n * await debugFlush();\n * // [flush] starting with maxTurns=5\n * // [flush] microtask turn 1/5\n * // ...\n * ```\n */\nexport async function debugFlush(options?: Omit<FlushOptions, 'logger'>): Promise<void> {\n return flush({ ...options, logger: console.debug });\n}\n"],"mappings":"2CAkCA,eAAsB,EAAW,EAAuD,CACtF,OAAO,EAAM,CAAE,GAAG,EAAS,OAAQ,QAAQ,KAAM,CAAC,CACpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classMap.cjs","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, (() => boolean) | Readable<boolean> | boolean>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => (typeof v === 'function' ? v() : isReactive(v) ? v.value : v))\n // Strip whitespace from each key — spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"kCAkBA,IAAa,EAAY,IACvB,EAAA,EAAA,SAAA,KACE,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAQ,OAAO,GAAM,WAAa,EAAE,GAAA,EAAA,EAAA,WAAA,CAAe,CAAC,EAAI,EAAE,MAAQ,CAAE,CAAC,CAEhF,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
+
/**
|
|
3
|
+
* Produces a reactive string of class names from an object map.
|
|
4
|
+
*
|
|
5
|
+
* Each key is a class name. Its value may be:
|
|
6
|
+
* - a static `boolean`
|
|
7
|
+
* - a `Signal<boolean>`
|
|
8
|
+
* - a getter `() => boolean`
|
|
9
|
+
*
|
|
10
|
+
* Returns a `Reactive<string>` that can be used directly in a template
|
|
11
|
+
* class attribute:
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* html`<div class="${classMap({ active: isActive, hidden: () => !isVisible.value })}"></div>`
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const classMap: (map: Record<string, (() => boolean) | Readable<boolean> | boolean>) => Readable<string>;
|
|
19
|
+
//# sourceMappingURL=classMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classMap.d.ts","sourceRoot":"","sources":["../../src/directives/classMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,KAAG,QAAQ,CAAC,MAAM,CAS5G,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classMap.js","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, (() => boolean) | Readable<boolean> | boolean>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => (typeof v === 'function' ? v() : isReactive(v) ? v.value : v))\n // Strip whitespace from each key — spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"4DAkBA,IAAa,EAAY,GAChB,MACL,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAQ,OAAO,GAAM,WAAa,EAAE,EAAI,EAAW,CAAC,EAAI,EAAE,MAAQ,CAAE,CAAC,CAEhF,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_dev.cjs"),t=require("../errors.cjs"),n=require("../types/bindings.cjs"),r=require("../utils/dom.cjs");let i=require("@vielzeug/ripple");var a=(e,t,n,r,a)=>{let o=(0,i.signal)(e),s=(0,i.signal)(t),c=n(o,s),l=[];return{cleanups:l,data:o,index:s,key:``,nodes:c.mount(r,a,e=>l.push(e))}},o=e=>{r.runAll(e.cleanups),r.removeNodes(e.nodes)},s=(e,n,r,s,c,l)=>{let u=[],d=new Set;for(let e=0;e<n.length;e++){let i=String(r(n[e],e));if(d.has(i))throw new t.OreApiError(t.ORE_ERRORS.eachDuplicateKey(i,e));d.add(i),u.push(i)}for(let[t,n]of e)d.has(t)||(o(n),e.delete(t));let f=[];for(let t=0;t<n.length;t++){let r=u[t],o=e.get(r);if(o)(0,i.batch)(()=>{o.data.value=n[t],o.index.value=t}),f.push(o);else{let o=(0,i.untrack)(()=>a(n[t],t,s,c,l));o.key=r,e.set(r,o),f.push(o)}}let p=l;for(let e=f.length-1;e>=0;e--){let t=f[e],n=t.nodes[0];if(n&&n!==p.previousSibling)for(let e of t.nodes)c.insertBefore(e,p);p=n??p}return f};function c(t,a,c,l){let u=Array.isArray(t)?(0,i.signal)(t):typeof t==`function`?(0,i.computed)(t):t;return e.devOnly(()=>{try{let t=99999;a({},t)===t&&e.warn(`each(): key function returns only the index. Index keys cause full list re-renders on insert/remove — pass a stable item identifier (e.g. item.id) instead.`)}catch{}}),n.createDirectiveResult((t,n)=>{let d=t.parentNode,f=document.createComment(`each/end`);d.insertBefore(f,t.nextSibling);let p=new Map,m=[],h=null,g=[],_=()=>{l&&(h=l().mount(d,f,e=>g.push(e)))},v=()=>{h&&(r.runAll(g),r.removeNodes(h),h=null,g=[])},y=(0,i.effect)(()=>{let t=u.value??[];if(t.length===0){for(let e of(0,i.untrack)(()=>m))o(e);p=new Map,m=[],h||(0,i.untrack)(_);return}v();try{m=(0,i.untrack)(()=>s(p,t,a,c,d,f))}catch(t){e.warn(`each() reconciliation error: ${t instanceof Error?t.message:String(t)}`);for(let e of p.values())o(e);p=new Map,m=[]}});n(()=>y.dispose()),n(()=>{v();for(let e of m)o(e);f.remove()})})}exports.each=c;
|
|
2
|
+
//# sourceMappingURL=each.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"each.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, computed, effect as rawEffect, type Readable, signal, type Signal, untrack } from '@vielzeug/ripple';\n\nimport { devOnly, warn } from '../_dev';\nimport { OreApiError, ORE_ERRORS } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../types/bindings';\nimport { removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n /** The key used to identify this entry. */\n key: string;\n nodes: Node[];\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n\n const result = render(dataSignal, indexSignal);\n const cleanups: (() => void)[] = [];\n const nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n\n return { cleanups, data: dataSignal, index: indexSignal, key: '', nodes };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<string, ItemEntry<T>>,\n next: T[],\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeys: string[] = [];\n const nextKeySet = new Set<string>();\n\n for (let i = 0; i < next.length; i++) {\n const key = String(keyFn(next[i], i));\n\n if (nextKeySet.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(key, i));\n\n nextKeySet.add(key);\n nextKeys.push(key);\n }\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n entry.key = key;\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass — move any item not already adjacent to cursor.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n\n if (firstNode && firstNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n devOnly(() => {\n try {\n const SENTINEL_IDX = 99999;\n const testKey = keyFn({} as T, SENTINEL_IDX);\n\n if (testKey === SENTINEL_IDX) {\n warn(\n 'each(): key function returns only the index. Index keys cause full list re-renders on insert/remove — pass a stable item identifier (e.g. item.id) instead.',\n );\n }\n } catch {\n /* ignore — keyFn may throw on a non-item sentinel; that is fine */\n }\n });\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode!;\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<string, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, keyFn, render, parent, endMarker));\n } catch (err) {\n warn(`each() reconciliation error: ${err instanceof Error ? err.message : String(err)}`);\n\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
+
import { type DirectiveResult, type HTMLResult } from '../types/bindings';
|
|
3
|
+
type MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];
|
|
4
|
+
/**
|
|
5
|
+
* Renders a keyed list of items as a `DirectiveResult`.
|
|
6
|
+
*
|
|
7
|
+
* Each item is rendered by the provided render function.
|
|
8
|
+
* Items are reused by key when the list changes; only stale items are destroyed.
|
|
9
|
+
*
|
|
10
|
+
* The render function receives a `Readable<T>` signal and a `Readable<number>` index
|
|
11
|
+
* signal. Use `item.value` to read the current item inside reactive expressions:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is
|
|
18
|
+
* treated as a one-time static render. Mutations to the original array are not
|
|
19
|
+
* tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.
|
|
20
|
+
*
|
|
21
|
+
* **Optional fallback:** the fourth argument renders when the list is empty.
|
|
22
|
+
*/
|
|
23
|
+
export declare function each<T>(list: MaybeReactiveArray<T>, keyFn: (item: T, index: number) => string | number, render: (item: Readable<T>, index: Readable<number>) => HTMLResult, fallback?: () => HTMLResult): DirectiveResult;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=each.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/directives/each.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,QAAQ,EAAgC,MAAM,kBAAkB,CAAC;AAIrH,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAKjG,KAAK,kBAAkB,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;AAkH/D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACpB,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,EAClD,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,UAAU,EAClE,QAAQ,CAAC,EAAE,MAAM,UAAU,GAC1B,eAAe,CAmFjB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{devOnly as e,warn as t}from"../_dev.js";import{ORE_ERRORS as n,OreApiError as r}from"../errors.js";import{createDirectiveResult as i}from"../types/bindings.js";import{removeNodes as a,runAll as o}from"../utils/dom.js";import{batch as s,computed as c,effect as l,signal as u,untrack as d}from"@vielzeug/ripple";var f=(e,t,n,r,i)=>{let a=u(e),o=u(t),s=n(a,o),c=[];return{cleanups:c,data:a,index:o,key:``,nodes:s.mount(r,i,e=>c.push(e))}},p=e=>{o(e.cleanups),a(e.nodes)},m=(e,t,i,a,o,c)=>{let l=[],u=new Set;for(let e=0;e<t.length;e++){let a=String(i(t[e],e));if(u.has(a))throw new r(n.eachDuplicateKey(a,e));u.add(a),l.push(a)}for(let[t,n]of e)u.has(t)||(p(n),e.delete(t));let m=[];for(let n=0;n<t.length;n++){let r=l[n],i=e.get(r);if(i)s(()=>{i.data.value=t[n],i.index.value=n}),m.push(i);else{let i=d(()=>f(t[n],n,a,o,c));i.key=r,e.set(r,i),m.push(i)}}let h=c;for(let e=m.length-1;e>=0;e--){let t=m[e],n=t.nodes[0];if(n&&n!==h.previousSibling)for(let e of t.nodes)o.insertBefore(e,h);h=n??h}return m};function h(n,r,s,f){let h=Array.isArray(n)?u(n):typeof n==`function`?c(n):n;return e(()=>{try{let e=99999;r({},e)===e&&t(`each(): key function returns only the index. Index keys cause full list re-renders on insert/remove — pass a stable item identifier (e.g. item.id) instead.`)}catch{}}),i((e,n)=>{let i=e.parentNode,c=document.createComment(`each/end`);i.insertBefore(c,e.nextSibling);let u=new Map,g=[],_=null,v=[],y=()=>{f&&(_=f().mount(i,c,e=>v.push(e)))},b=()=>{_&&(o(v),a(_),_=null,v=[])},x=l(()=>{let e=h.value??[];if(e.length===0){for(let e of d(()=>g))p(e);u=new Map,g=[],_||d(y);return}b();try{g=d(()=>m(u,e,r,s,i,c))}catch(e){t(`each() reconciliation error: ${e instanceof Error?e.message:String(e)}`);for(let e of u.values())p(e);u=new Map,g=[]}});n(()=>x.dispose()),n(()=>{b();for(let e of g)p(e);c.remove()})})}export{h as each};
|
|
2
|
+
//# sourceMappingURL=each.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, computed, effect as rawEffect, type Readable, signal, type Signal, untrack } from '@vielzeug/ripple';\n\nimport { devOnly, warn } from '../_dev';\nimport { OreApiError, ORE_ERRORS } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../types/bindings';\nimport { removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n /** The key used to identify this entry. */\n key: string;\n nodes: Node[];\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n\n const result = render(dataSignal, indexSignal);\n const cleanups: (() => void)[] = [];\n const nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n\n return { cleanups, data: dataSignal, index: indexSignal, key: '', nodes };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<string, ItemEntry<T>>,\n next: T[],\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeys: string[] = [];\n const nextKeySet = new Set<string>();\n\n for (let i = 0; i < next.length; i++) {\n const key = String(keyFn(next[i], i));\n\n if (nextKeySet.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(key, i));\n\n nextKeySet.add(key);\n nextKeys.push(key);\n }\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n entry.key = key;\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass — move any item not already adjacent to cursor.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n\n if (firstNode && firstNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n devOnly(() => {\n try {\n const SENTINEL_IDX = 99999;\n const testKey = keyFn({} as T, SENTINEL_IDX);\n\n if (testKey === SENTINEL_IDX) {\n warn(\n 'each(): key function returns only the index. Index keys cause full list re-renders on insert/remove — pass a stable item identifier (e.g. item.id) instead.',\n );\n }\n } catch {\n /* ignore — keyFn may throw on a non-item sentinel; that is fine */\n }\n });\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode!;\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<string, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, keyFn, render, parent, endMarker));\n } catch (err) {\n warn(`each() reconciliation error: ${err instanceof Error ? err.message : String(err)}`);\n\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { classMap } from './classMap';
|
|
2
|
+
export { each } from './each';
|
|
3
|
+
export { live, type LiveSignal } from './live';
|
|
4
|
+
export { model } from './model';
|
|
5
|
+
export { raw, setRawSanitizer } from './raw';
|
|
6
|
+
export { styleMap } from './styleMap';
|
|
7
|
+
export { when } from './when';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/directives/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nconst LIVE_BRAND: unique symbol = Symbol.for('ore:live');\n\n/**\n * A branded signal that tells the attribute binding engine to skip writing when\n * the DOM value has diverged from the last programmatically-written value.\n * Created via live(signal) — pass a signal directly rather than wrapping a value.\n *\n * @example\n * html`<input :value=\"${live(model)}\" />`\n */\nexport type LiveSignal<T> = Readable<T> & { readonly [LIVE_BRAND]: true };\n\n// WeakSet tracks which signal objects have been marked live — zero allocation on call.\nlet liveSignals = new WeakSet<object>();\n\n/**\n * Marks a signal binding as \"live\" so stale app-state writes never clobber\n * in-progress user input.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveSignal<T> => {\n liveSignals.add(source as object);\n\n return source as LiveSignal<T>;\n};\n\nexport const isLiveSignal = (value: unknown): value is LiveSignal<unknown> =>\n typeof value === 'object' && value !== null && liveSignals.has(value as object);\n\n/** @internal Reset live signal registry. Called by cleanup() for test isolation. */\nexport const _resetLiveSignals = (): void => {\n liveSignals = new WeakSet();\n};\n"],"mappings":"AAeA,IAAI,EAAc,IAAI,QAUT,EAAW,IACtB,EAAY,IAAI,CAAgB,EAEzB,GAGI,EAAgB,GAC3B,OAAO,GAAU,YAAY,GAAkB,EAAY,IAAI,CAAe,EAGnE,MAAgC,CAC3C,EAAc,IAAI,OACpB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Readable } from '@vielzeug/ripple';
|
|
2
|
+
declare const LIVE_BRAND: unique symbol;
|
|
3
|
+
/**
|
|
4
|
+
* A branded signal that tells the attribute binding engine to skip writing when
|
|
5
|
+
* the DOM value has diverged from the last programmatically-written value.
|
|
6
|
+
* Created via live(signal) — pass a signal directly rather than wrapping a value.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* html`<input :value="${live(model)}" />`
|
|
10
|
+
*/
|
|
11
|
+
export type LiveSignal<T> = Readable<T> & {
|
|
12
|
+
readonly [LIVE_BRAND]: true;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Marks a signal binding as "live" so stale app-state writes never clobber
|
|
16
|
+
* in-progress user input.
|
|
17
|
+
*
|
|
18
|
+
* For form controls: if the current DOM value diverges from the last write made
|
|
19
|
+
* by this binding, subsequent app-state writes are silently dropped until the
|
|
20
|
+
* DOM value matches the incoming value or no prior write has been recorded.
|
|
21
|
+
*/
|
|
22
|
+
export declare const live: <T>(source: Readable<T>) => LiveSignal<T>;
|
|
23
|
+
export declare const isLiveSignal: (value: unknown) => value is LiveSignal<unknown>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=live.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/directives/live.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,QAAA,MAAM,UAAU,EAAE,OAAO,MAA+B,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAK1E;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,QAAQ,QAAQ,CAAC,CAAC,CAAC,KAAG,UAAU,CAAC,CAAC,CAIzD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UAAU,CAAC,OAAO,CACQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nconst LIVE_BRAND: unique symbol = Symbol.for('ore:live');\n\n/**\n * A branded signal that tells the attribute binding engine to skip writing when\n * the DOM value has diverged from the last programmatically-written value.\n * Created via live(signal) — pass a signal directly rather than wrapping a value.\n *\n * @example\n * html`<input :value=\"${live(model)}\" />`\n */\nexport type LiveSignal<T> = Readable<T> & { readonly [LIVE_BRAND]: true };\n\n// WeakSet tracks which signal objects have been marked live — zero allocation on call.\nlet liveSignals = new WeakSet<object>();\n\n/**\n * Marks a signal binding as \"live\" so stale app-state writes never clobber\n * in-progress user input.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveSignal<T> => {\n liveSignals.add(source as object);\n\n return source as LiveSignal<T>;\n};\n\nexport const isLiveSignal = (value: unknown): value is LiveSignal<unknown> =>\n typeof value === 'object' && value !== null && liveSignals.has(value as object);\n\n/** @internal Reset live signal registry. Called by cleanup() for test isolation. */\nexport const _resetLiveSignals = (): void => {\n liveSignals = new WeakSet();\n};\n"],"mappings":"AAeA,IAAI,EAAc,IAAI,QAUT,EAAW,IACtB,EAAY,IAAI,CAAgB,EAEzB,GAGI,EAAgB,GAC3B,OAAO,GAAU,YAAY,GAAkB,EAAY,IAAI,CAAe,EAGnE,MAAgC,CAC3C,EAAc,IAAI,OACpB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../types/bindings.cjs"),t=require("../utils/dom.cjs");let n=require("@vielzeug/ripple");function r(r){return e.createSpreadObject((e,i)=>{let a=e,o=a.type===`checkbox`,s=a.type===`number`||a.type===`range`,c=e instanceof HTMLSelectElement,l=c&&e.multiple,u=(0,n.effect)(()=>{let t=r.value;if(l){let n=Array.isArray(t)?t:[],r=e;for(let e of r.options)e.selected=n.includes(e.value)}else o?a.checked=!!t:e.value=t==null?``:String(t)});i(()=>u.dispose()),i(t.listen(e,c?`change`:`input`,e=>{let t=e.target;if(l){let t=e.target;r.value=Array.from(t.selectedOptions).map(e=>e.value)}else o?r.value=t.checked:s?r.value=t.value===``?0:Number(t.value):r.value=t.value}))})}exports.model=r;
|
|
2
|
+
//# sourceMappingURL=model.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.cjs","names":[],"sources":["../../src/directives/model.ts"],"sourcesContent":["import { effect as rawEffect, type Signal } from '@vielzeug/ripple';\n\nimport { createSpreadObject, type SpreadObject } from '../types/bindings';\nimport { listen } from '../utils/dom';\n\n/**\n * Two-way binding spread for form inputs.\n * Syncs the signal value to the element's DOM value and back.\n *\n * Supports `type=\"text\"` (default), `type=\"number\"`, `type=\"range\"`,\n * `type=\"checkbox\"`, `<select>` (single-select), and\n * `<select multiple>` (multi-select, `Signal<string[]>`).\n *\n * @example\n * ```ts\n * const name = signal('');\n * html`<input type=\"text\" ${model(name)} />`\n *\n * const count = signal(0);\n * html`<input type=\"number\" ${model(count)} />`\n *\n * const checked = signal(false);\n * html`<input type=\"checkbox\" ${model(checked)} />`\n *\n * const selected = signal<string[]>([]);\n * html`<select multiple ${model(selected)}><option value=\"a\">A</option></select>`\n * ```\n */\nexport function model<T extends string | number | boolean | null>(source: Signal<T>): SpreadObject;\nexport function model(source: Signal<string[]>): SpreadObject;\nexport function model<T extends string | number | boolean | null | string[]>(source: Signal<T>): SpreadObject {\n return createSpreadObject((el, registerCleanup) => {\n const input = el as HTMLInputElement;\n const isCheckbox = input.type === 'checkbox';\n const isNumeric = input.type === 'number' || input.type === 'range';\n const isSelect = el instanceof HTMLSelectElement;\n const isMultiSelect = isSelect && (el as HTMLSelectElement).multiple;\n\n // Signal → DOM\n const sub = rawEffect(() => {\n const v = source.value;\n\n if (isMultiSelect) {\n const selected = Array.isArray(v) ? (v as string[]) : [];\n const selectEl = el as HTMLSelectElement;\n\n for (const opt of selectEl.options) {\n opt.selected = selected.includes(opt.value);\n }\n } else if (isCheckbox) {\n input.checked = Boolean(v);\n } else {\n (el as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).value =\n v == null ? '' : String(v as string | number | boolean | null);\n }\n });\n\n registerCleanup(() => sub.dispose());\n\n // DOM → Signal\n // <select> fires 'change'; text/number/range/textarea/checkbox fire 'input'.\n const eventName = isSelect ? 'change' : 'input';\n\n registerCleanup(\n listen(el, eventName, (e: Event) => {\n const target = e.target as HTMLInputElement;\n\n if (isMultiSelect) {\n const selectEl = e.target as HTMLSelectElement;\n const values = Array.from(selectEl.selectedOptions).map((opt) => opt.value);\n\n (source as Signal<string[]>).value = values;\n } else if (isCheckbox) {\n source.value = target.checked as T;\n } else if (isNumeric) {\n source.value = (target.value === '' ? 0 : Number(target.value)) as T;\n } else {\n source.value = target.value as T;\n }\n }),\n );\n });\n}\n"],"mappings":"yGA8BA,SAAgB,EAA6D,EAAiC,CAC5G,OAAO,EAAA,oBAAoB,EAAI,IAAoB,CACjD,IAAM,EAAQ,EACR,EAAa,EAAM,OAAS,WAC5B,EAAY,EAAM,OAAS,UAAY,EAAM,OAAS,QACtD,EAAW,aAAc,kBACzB,EAAgB,GAAa,EAAyB,SAGtD,GAAA,EAAA,EAAA,OAAA,KAAsB,CAC1B,IAAM,EAAI,EAAO,MAEjB,GAAI,EAAe,CACjB,IAAM,EAAW,MAAM,QAAQ,CAAC,EAAK,EAAiB,CAAC,EACjD,EAAW,EAEjB,IAAK,IAAM,KAAO,EAAS,QACzB,EAAI,SAAW,EAAS,SAAS,EAAI,KAAK,CAE9C,MAAW,EACT,EAAM,QAAU,EAAQ,EAExB,EAAmE,MACjE,GAAK,KAAO,GAAK,OAAO,CAAqC,CAEnE,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EAMnC,EACE,EAAA,OAAO,EAHS,EAAW,SAAW,QAGf,GAAa,CAClC,IAAM,EAAS,EAAE,OAEjB,GAAI,EAAe,CACjB,IAAM,EAAW,EAAE,OAGnB,EAA6B,MAFd,MAAM,KAAK,EAAS,eAAe,CAAC,CAAC,IAAK,GAAQ,EAAI,KAEhC,CACvC,MAAW,EACT,EAAO,MAAQ,EAAO,QACb,EACT,EAAO,MAAS,EAAO,QAAU,GAAK,EAAI,OAAO,EAAO,KAAK,EAE7D,EAAO,MAAQ,EAAO,KAE1B,CAAC,CACH,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Signal } from '@vielzeug/ripple';
|
|
2
|
+
import { type SpreadObject } from '../types/bindings';
|
|
3
|
+
/**
|
|
4
|
+
* Two-way binding spread for form inputs.
|
|
5
|
+
* Syncs the signal value to the element's DOM value and back.
|
|
6
|
+
*
|
|
7
|
+
* Supports `type="text"` (default), `type="number"`, `type="range"`,
|
|
8
|
+
* `type="checkbox"`, `<select>` (single-select), and
|
|
9
|
+
* `<select multiple>` (multi-select, `Signal<string[]>`).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const name = signal('');
|
|
14
|
+
* html`<input type="text" ${model(name)} />`
|
|
15
|
+
*
|
|
16
|
+
* const count = signal(0);
|
|
17
|
+
* html`<input type="number" ${model(count)} />`
|
|
18
|
+
*
|
|
19
|
+
* const checked = signal(false);
|
|
20
|
+
* html`<input type="checkbox" ${model(checked)} />`
|
|
21
|
+
*
|
|
22
|
+
* const selected = signal<string[]>([]);
|
|
23
|
+
* html`<select multiple ${model(selected)}><option value="a">A</option></select>`
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function model<T extends string | number | boolean | null>(source: Signal<T>): SpreadObject;
|
|
27
|
+
export declare function model(source: Signal<string[]>): SpreadObject;
|
|
28
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/directives/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAG1E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;AACnG,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,YAAY,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createSpreadObject as e}from"../types/bindings.js";import{listen as t}from"../utils/dom.js";import{effect as n}from"@vielzeug/ripple";function r(r){return e((e,i)=>{let a=e,o=a.type===`checkbox`,s=a.type===`number`||a.type===`range`,c=e instanceof HTMLSelectElement,l=c&&e.multiple,u=n(()=>{let t=r.value;if(l){let n=Array.isArray(t)?t:[],r=e;for(let e of r.options)e.selected=n.includes(e.value)}else o?a.checked=!!t:e.value=t==null?``:String(t)});i(()=>u.dispose()),i(t(e,c?`change`:`input`,e=>{let t=e.target;if(l){let t=e.target;r.value=Array.from(t.selectedOptions).map(e=>e.value)}else o?r.value=t.checked:s?r.value=t.value===``?0:Number(t.value):r.value=t.value}))})}export{r as model};
|
|
2
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","names":[],"sources":["../../src/directives/model.ts"],"sourcesContent":["import { effect as rawEffect, type Signal } from '@vielzeug/ripple';\n\nimport { createSpreadObject, type SpreadObject } from '../types/bindings';\nimport { listen } from '../utils/dom';\n\n/**\n * Two-way binding spread for form inputs.\n * Syncs the signal value to the element's DOM value and back.\n *\n * Supports `type=\"text\"` (default), `type=\"number\"`, `type=\"range\"`,\n * `type=\"checkbox\"`, `<select>` (single-select), and\n * `<select multiple>` (multi-select, `Signal<string[]>`).\n *\n * @example\n * ```ts\n * const name = signal('');\n * html`<input type=\"text\" ${model(name)} />`\n *\n * const count = signal(0);\n * html`<input type=\"number\" ${model(count)} />`\n *\n * const checked = signal(false);\n * html`<input type=\"checkbox\" ${model(checked)} />`\n *\n * const selected = signal<string[]>([]);\n * html`<select multiple ${model(selected)}><option value=\"a\">A</option></select>`\n * ```\n */\nexport function model<T extends string | number | boolean | null>(source: Signal<T>): SpreadObject;\nexport function model(source: Signal<string[]>): SpreadObject;\nexport function model<T extends string | number | boolean | null | string[]>(source: Signal<T>): SpreadObject {\n return createSpreadObject((el, registerCleanup) => {\n const input = el as HTMLInputElement;\n const isCheckbox = input.type === 'checkbox';\n const isNumeric = input.type === 'number' || input.type === 'range';\n const isSelect = el instanceof HTMLSelectElement;\n const isMultiSelect = isSelect && (el as HTMLSelectElement).multiple;\n\n // Signal → DOM\n const sub = rawEffect(() => {\n const v = source.value;\n\n if (isMultiSelect) {\n const selected = Array.isArray(v) ? (v as string[]) : [];\n const selectEl = el as HTMLSelectElement;\n\n for (const opt of selectEl.options) {\n opt.selected = selected.includes(opt.value);\n }\n } else if (isCheckbox) {\n input.checked = Boolean(v);\n } else {\n (el as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).value =\n v == null ? '' : String(v as string | number | boolean | null);\n }\n });\n\n registerCleanup(() => sub.dispose());\n\n // DOM → Signal\n // <select> fires 'change'; text/number/range/textarea/checkbox fire 'input'.\n const eventName = isSelect ? 'change' : 'input';\n\n registerCleanup(\n listen(el, eventName, (e: Event) => {\n const target = e.target as HTMLInputElement;\n\n if (isMultiSelect) {\n const selectEl = e.target as HTMLSelectElement;\n const values = Array.from(selectEl.selectedOptions).map((opt) => opt.value);\n\n (source as Signal<string[]>).value = values;\n } else if (isCheckbox) {\n source.value = target.checked as T;\n } else if (isNumeric) {\n source.value = (target.value === '' ? 0 : Number(target.value)) as T;\n } else {\n source.value = target.value as T;\n }\n }),\n );\n });\n}\n"],"mappings":"6IA8BA,SAAgB,EAA6D,EAAiC,CAC5G,OAAO,GAAoB,EAAI,IAAoB,CACjD,IAAM,EAAQ,EACR,EAAa,EAAM,OAAS,WAC5B,EAAY,EAAM,OAAS,UAAY,EAAM,OAAS,QACtD,EAAW,aAAc,kBACzB,EAAgB,GAAa,EAAyB,SAGtD,EAAM,MAAgB,CAC1B,IAAM,EAAI,EAAO,MAEjB,GAAI,EAAe,CACjB,IAAM,EAAW,MAAM,QAAQ,CAAC,EAAK,EAAiB,CAAC,EACjD,EAAW,EAEjB,IAAK,IAAM,KAAO,EAAS,QACzB,EAAI,SAAW,EAAS,SAAS,EAAI,KAAK,CAE9C,MAAW,EACT,EAAM,QAAU,EAAQ,EAExB,EAAmE,MACjE,GAAK,KAAO,GAAK,OAAO,CAAqC,CAEnE,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EAMnC,EACE,EAAO,EAHS,EAAW,SAAW,QAGf,GAAa,CAClC,IAAM,EAAS,EAAE,OAEjB,GAAI,EAAe,CACjB,IAAM,EAAW,EAAE,OAGnB,EAA6B,MAFd,MAAM,KAAK,EAAS,eAAe,CAAC,CAAC,IAAK,GAAQ,EAAI,KAEhC,CACvC,MAAW,EACT,EAAO,MAAQ,EAAO,QACb,EACT,EAAO,MAAS,EAAO,QAAU,GAAK,EAAI,OAAO,EAAO,KAAK,EAE7D,EAAO,MAAQ,EAAO,KAE1B,CAAC,CACH,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_dev.cjs"),t=require("../types/bindings.cjs"),n=require("../utils/dom.cjs");let r=require("@vielzeug/ripple");var i=null,a=!1,o=e=>{i=e,e||(a=!1)},s=()=>{i=null,a=!1},c=t=>i?i(t):(t&&!a&&(a=!0,e.warn(`raw() was called without a sanitizer registered. Passing user-supplied HTML directly to raw() is an XSS risk. Register a sanitizer with setRawSanitizer() — e.g. setRawSanitizer(DOMPURIFY.sanitize).`)),t),l=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function u(e){if(typeof e==`function`){let n=(0,r.computed)(e);return t.createDirectiveResult((e,t)=>{t(()=>n.dispose()),u(n).mount(e,t)})}return t.createDirectiveResult((t,i)=>{let a=t.parentNode,o=document.createComment(`raw/end`);if(a.insertBefore(o,t.nextSibling),(0,r.isReactive)(e)){let t=[],s=e,u=(0,r.effect)(()=>{n.removeNodes(t),t=l(c(s.value),a,o)});i(()=>u.dispose()),i(()=>{n.removeNodes(t),o.remove()})}else l(c(e),a,o),i(()=>o.remove())})}exports._resetRawSanitizer=s,exports.raw=u,exports.setRawSanitizer=o;
|
|
2
|
+
//# sourceMappingURL=raw.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw.cjs","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { createDirectiveResult, type DirectiveResult } from '../types/bindings';\nimport { removeNodes } from '../utils/dom';\n\ntype RawSanitizer = (html: string) => string;\n\nlet _sanitizer: RawSanitizer | null = null;\nlet _warned = false;\n\nexport const setRawSanitizer = (fn: RawSanitizer | null): void => {\n _sanitizer = fn;\n\n if (!fn) _warned = false;\n};\n\n/** @internal */\nexport const _resetRawSanitizer = (): void => {\n _sanitizer = null;\n _warned = false;\n};\n\nconst sanitize = (value: string): string => {\n if (_sanitizer) return _sanitizer(value);\n\n if (value && !_warned) {\n _warned = true;\n warn(\n 'raw() was called without a sanitizer registered. ' +\n 'Passing user-supplied HTML directly to raw() is an XSS risk. ' +\n 'Register a sanitizer with setRawSanitizer() — e.g. setRawSanitizer(DOMPURIFY.sanitize).',\n );\n }\n\n return value;\n};\n\nconst parseRaw = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders a trusted HTML string without escaping as a DirectiveResult.\n * Only use with content you control — passing user-supplied strings\n * directly is an XSS risk. Register a sanitizer with setRawSanitizer()\n * to add a runtime safety net.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function raw(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n if (typeof value === 'function') {\n const c = computed(value);\n\n return createDirectiveResult((anchor, registerCleanup) => {\n registerCleanup(() => c.dispose());\n raw(c).mount(anchor, registerCleanup);\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode!;\n const endMarker = document.createComment('raw/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n if (isReactive(value)) {\n let currentNodes: Node[] = [];\n const src = value as Readable<string>;\n\n const stop = rawEffect(() => {\n removeNodes(currentNodes);\n currentNodes = parseRaw(sanitize(src.value), parent, endMarker);\n });\n\n registerCleanup(() => stop.dispose());\n registerCleanup(() => {\n removeNodes(currentNodes);\n endMarker.remove();\n });\n } else {\n parseRaw(sanitize(value), parent, endMarker);\n\n registerCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"kIAQA,IAAI,EAAkC,KAClC,EAAU,GAED,EAAmB,GAAkC,CAChE,EAAa,EAER,IAAI,EAAU,GACrB,EAGa,MAAiC,CAC5C,EAAa,KACb,EAAU,EACZ,EAEM,EAAY,GACZ,EAAmB,EAAW,CAAK,GAEnC,GAAS,CAAC,IACZ,EAAU,GACV,EAAA,KACE,uMAGF,GAGK,GAGH,GAAY,EAAc,EAAoB,IAA+B,CACjF,IAAM,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAY,EAEhB,IAAM,EAAQ,MAAM,KAAK,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAM,KAAQ,EAAO,EAAO,aAAa,EAAM,CAAY,EAEhE,OAAO,CACT,EAWA,SAAgB,EAAI,EAAqF,CACvG,GAAI,OAAO,GAAU,WAAY,CAC/B,IAAM,GAAA,EAAA,EAAA,SAAA,CAAa,CAAK,EAExB,OAAO,EAAA,uBAAuB,EAAQ,IAAoB,CACxD,MAAsB,EAAE,QAAQ,CAAC,EACjC,EAAI,CAAC,CAAC,CAAC,MAAM,EAAQ,CAAe,CACtC,CAAC,CACH,CAEA,OAAO,EAAA,uBAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAO,WAChB,EAAY,SAAS,cAAc,SAAS,EAIlD,GAFA,EAAO,aAAa,EAAW,EAAO,WAAW,GAEjD,EAAA,EAAA,WAAA,CAAe,CAAK,EAAG,CACrB,IAAI,EAAuB,CAAC,EACtB,EAAM,EAEN,GAAA,EAAA,EAAA,OAAA,KAAuB,CAC3B,EAAA,YAAY,CAAY,EACxB,EAAe,EAAS,EAAS,EAAI,KAAK,EAAG,EAAQ,CAAS,CAChE,CAAC,EAED,MAAsB,EAAK,QAAQ,CAAC,EACpC,MAAsB,CACpB,EAAA,YAAY,CAAY,EACxB,EAAU,OAAO,CACnB,CAAC,CACH,MACE,EAAS,EAAS,CAAK,EAAG,EAAQ,CAAS,EAE3C,MAAsB,EAAU,OAAO,CAAC,CAE5C,CAAC,CACH"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Readable, type Signal } from '@vielzeug/ripple';
|
|
2
|
+
import { type DirectiveResult } from '../types/bindings';
|
|
3
|
+
type RawSanitizer = (html: string) => string;
|
|
4
|
+
export declare const setRawSanitizer: (fn: RawSanitizer | null) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Renders a trusted HTML string without escaping as a DirectiveResult.
|
|
7
|
+
* Only use with content you control — passing user-supplied strings
|
|
8
|
+
* directly is an XSS risk. Register a sanitizer with setRawSanitizer()
|
|
9
|
+
* to add a runtime safety net.
|
|
10
|
+
*
|
|
11
|
+
* Supports static strings, signals, and getter functions `() => string`.
|
|
12
|
+
* When reactive, the DOM is updated in-place whenever the value changes.
|
|
13
|
+
*/
|
|
14
|
+
export declare function raw(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=raw.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/directives/raw.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGzG,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGhF,KAAK,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAK7C,eAAO,MAAM,eAAe,GAAI,IAAI,YAAY,GAAG,IAAI,KAAG,IAIzD,CAAC;AAmCF;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,eAAe,CAoCvG"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{warn as e}from"../_dev.js";import{createDirectiveResult as t}from"../types/bindings.js";import{removeNodes as n}from"../utils/dom.js";import{computed as r,effect as i,isReactive as a}from"@vielzeug/ripple";var o=null,s=!1,c=e=>{o=e,e||(s=!1)},l=()=>{o=null,s=!1},u=t=>o?o(t):(t&&!s&&(s=!0,e(`raw() was called without a sanitizer registered. Passing user-supplied HTML directly to raw() is an XSS risk. Register a sanitizer with setRawSanitizer() — e.g. setRawSanitizer(DOMPURIFY.sanitize).`)),t),d=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function f(e){if(typeof e==`function`){let n=r(e);return t((e,t)=>{t(()=>n.dispose()),f(n).mount(e,t)})}return t((t,r)=>{let o=t.parentNode,s=document.createComment(`raw/end`);if(o.insertBefore(s,t.nextSibling),a(e)){let t=[],a=e,c=i(()=>{n(t),t=d(u(a.value),o,s)});r(()=>c.dispose()),r(()=>{n(t),s.remove()})}else d(u(e),o,s),r(()=>s.remove())})}export{l as _resetRawSanitizer,f as raw,c as setRawSanitizer};
|
|
2
|
+
//# sourceMappingURL=raw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw.js","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { createDirectiveResult, type DirectiveResult } from '../types/bindings';\nimport { removeNodes } from '../utils/dom';\n\ntype RawSanitizer = (html: string) => string;\n\nlet _sanitizer: RawSanitizer | null = null;\nlet _warned = false;\n\nexport const setRawSanitizer = (fn: RawSanitizer | null): void => {\n _sanitizer = fn;\n\n if (!fn) _warned = false;\n};\n\n/** @internal */\nexport const _resetRawSanitizer = (): void => {\n _sanitizer = null;\n _warned = false;\n};\n\nconst sanitize = (value: string): string => {\n if (_sanitizer) return _sanitizer(value);\n\n if (value && !_warned) {\n _warned = true;\n warn(\n 'raw() was called without a sanitizer registered. ' +\n 'Passing user-supplied HTML directly to raw() is an XSS risk. ' +\n 'Register a sanitizer with setRawSanitizer() — e.g. setRawSanitizer(DOMPURIFY.sanitize).',\n );\n }\n\n return value;\n};\n\nconst parseRaw = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders a trusted HTML string without escaping as a DirectiveResult.\n * Only use with content you control — passing user-supplied strings\n * directly is an XSS risk. Register a sanitizer with setRawSanitizer()\n * to add a runtime safety net.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function raw(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n if (typeof value === 'function') {\n const c = computed(value);\n\n return createDirectiveResult((anchor, registerCleanup) => {\n registerCleanup(() => c.dispose());\n raw(c).mount(anchor, registerCleanup);\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode!;\n const endMarker = document.createComment('raw/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n if (isReactive(value)) {\n let currentNodes: Node[] = [];\n const src = value as Readable<string>;\n\n const stop = rawEffect(() => {\n removeNodes(currentNodes);\n currentNodes = parseRaw(sanitize(src.value), parent, endMarker);\n });\n\n registerCleanup(() => stop.dispose());\n registerCleanup(() => {\n removeNodes(currentNodes);\n endMarker.remove();\n });\n } else {\n parseRaw(sanitize(value), parent, endMarker);\n\n registerCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"qNAQA,IAAI,EAAkC,KAClC,EAAU,GAED,EAAmB,GAAkC,CAChE,EAAa,EAER,IAAI,EAAU,GACrB,EAGa,MAAiC,CAC5C,EAAa,KACb,EAAU,EACZ,EAEM,EAAY,GACZ,EAAmB,EAAW,CAAK,GAEnC,GAAS,CAAC,IACZ,EAAU,GACV,EACE,uMAGF,GAGK,GAGH,GAAY,EAAc,EAAoB,IAA+B,CACjF,IAAM,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAY,EAEhB,IAAM,EAAQ,MAAM,KAAK,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAM,KAAQ,EAAO,EAAO,aAAa,EAAM,CAAY,EAEhE,OAAO,CACT,EAWA,SAAgB,EAAI,EAAqF,CACvG,GAAI,OAAO,GAAU,WAAY,CAC/B,IAAM,EAAI,EAAS,CAAK,EAExB,OAAO,GAAuB,EAAQ,IAAoB,CACxD,MAAsB,EAAE,QAAQ,CAAC,EACjC,EAAI,CAAC,CAAC,CAAC,MAAM,EAAQ,CAAe,CACtC,CAAC,CACH,CAEA,OAAO,GAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAO,WAChB,EAAY,SAAS,cAAc,SAAS,EAIlD,GAFA,EAAO,aAAa,EAAW,EAAO,WAAW,EAE7C,EAAW,CAAK,EAAG,CACrB,IAAI,EAAuB,CAAC,EACtB,EAAM,EAEN,EAAO,MAAgB,CAC3B,EAAY,CAAY,EACxB,EAAe,EAAS,EAAS,EAAI,KAAK,EAAG,EAAQ,CAAS,CAChE,CAAC,EAED,MAAsB,EAAK,QAAQ,CAAC,EACpC,MAAsB,CACpB,EAAY,CAAY,EACxB,EAAU,OAAO,CACnB,CAAC,CACH,MACE,EAAS,EAAS,CAAK,EAAG,EAAQ,CAAS,EAE3C,MAAsB,EAAU,OAAO,CAAC,CAE5C,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../utils/dom.cjs");let t=require("@vielzeug/ripple");var n=e=>{let n=typeof e==`function`?e():(0,t.isReactive)(e)?e.value:e;return n==null||n===!1?``:String(n).replace(/[;{}]/g,``)},r=/[;{}]/g,i=i=>(0,t.computed)(()=>{let t=[];for(let[a,o]of Object.entries(i)){let i=n(o);if(!i)continue;let s=e.toKebab(a).replace(r,``);s&&t.push(`${s}:${i}`)}return t.join(`;`)});exports.styleMap=i;
|
|
2
|
+
//# sourceMappingURL=styleMap.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styleMap.cjs","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { toKebab } from '../utils/dom';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | Readable<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = typeof value === 'function' ? value() : isReactive(value) ? value.value : value;\n\n if (resolved == null || resolved === false) return '';\n\n // Strip characters that can break out of a CSS declaration in inline styles.\n // Semicolons end the current declaration; braces are meaningful in stylesheets\n // but not inline style values and signal injection attempts.\n return String(resolved).replace(/[;{}]/g, '');\n};\n\n// Strip characters that can break out of a CSS declaration in inline styles.\n// This is applied to both property names and values: semicolons end declarations\n// and braces are meaningful in stylesheet rules (but not inline style values).\nconst UNSAFE_CSS_CHARS = /[;{}]/g;\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * Takes a record of CSS property names (camelCase) with values that can be:\n * - Static strings/numbers\n * - Functions that return strings/numbers\n * - Signals for reactive updates\n *\n * @example\n * ```ts\n * const color = signal('red');\n * const size = signal(16);\n *\n * html`<div :style=${styleMap({\n * backgroundColor: color,\n * width: () => `${size.value}px`,\n * padding: '10px'\n * })}></div>`\n * ```\n */\nexport const styleMap = (record: Record<string, StyleInput>): Readable<string> => {\n return computed(() => {\n const declarations: string[] = [];\n\n for (const [name, input] of Object.entries(record)) {\n const value = toStyleValue(input);\n\n if (!value) continue;\n\n const safeName = toKebab(name).replace(UNSAFE_CSS_CHARS, '');\n\n if (!safeName) continue;\n\n declarations.push(`${safeName}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"sEAaA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,OAAO,GAAU,WAAa,EAAM,GAAA,EAAA,EAAA,WAAA,CAAe,CAAK,EAAI,EAAM,MAAQ,EAO3F,OALI,GAAY,MAAQ,IAAa,GAAc,GAK5C,OAAO,CAAQ,CAAC,CAAC,QAAQ,SAAU,EAAE,CAC9C,EAKM,EAAmB,SAsBZ,EAAY,IACvB,EAAA,EAAA,SAAA,KAAsB,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAEhC,GAAI,CAAC,EAAO,SAEZ,IAAM,EAAW,EAAA,QAAQ,CAAI,CAAC,CAAC,QAAQ,EAAkB,EAAE,EAEtD,GAEL,EAAa,KAAK,GAAG,EAAS,GAAG,GAAO,CAC1C,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
+
type StyleInput = string | number | null | undefined | false | (() => string | number | null | undefined | false) | Readable<string | number | null | undefined | false>;
|
|
3
|
+
/**
|
|
4
|
+
* Builds a reactive inline style string from a style object.
|
|
5
|
+
*
|
|
6
|
+
* Takes a record of CSS property names (camelCase) with values that can be:
|
|
7
|
+
* - Static strings/numbers
|
|
8
|
+
* - Functions that return strings/numbers
|
|
9
|
+
* - Signals for reactive updates
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const color = signal('red');
|
|
14
|
+
* const size = signal(16);
|
|
15
|
+
*
|
|
16
|
+
* html`<div :style=${styleMap({
|
|
17
|
+
* backgroundColor: color,
|
|
18
|
+
* width: () => `${size.value}px`,
|
|
19
|
+
* padding: '10px'
|
|
20
|
+
* })}></div>`
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const styleMap: (record: Record<string, StyleInput>) => Readable<string>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=styleMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styleMap.d.ts","sourceRoot":"","sources":["../../src/directives/styleMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIvE,KAAK,UAAU,GACX,MAAM,GACN,MAAM,GACN,IAAI,GACJ,SAAS,GACT,KAAK,GACL,CAAC,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,GAClD,QAAQ,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC;AAkBzD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,KAAG,QAAQ,CAAC,MAAM,CAkB5E,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toKebab as e}from"../utils/dom.js";import{computed as t,isReactive as n}from"@vielzeug/ripple";var r=e=>{let t=typeof e==`function`?e():n(e)?e.value:e;return t==null||t===!1?``:String(t).replace(/[;{}]/g,``)},i=/[;{}]/g,a=n=>t(()=>{let t=[];for(let[a,o]of Object.entries(n)){let n=r(o);if(!n)continue;let s=e(a).replace(i,``);s&&t.push(`${s}:${n}`)}return t.join(`;`)});export{a as styleMap};
|
|
2
|
+
//# sourceMappingURL=styleMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styleMap.js","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, isReactive, type Readable } from '@vielzeug/ripple';\n\nimport { toKebab } from '../utils/dom';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | Readable<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = typeof value === 'function' ? value() : isReactive(value) ? value.value : value;\n\n if (resolved == null || resolved === false) return '';\n\n // Strip characters that can break out of a CSS declaration in inline styles.\n // Semicolons end the current declaration; braces are meaningful in stylesheets\n // but not inline style values and signal injection attempts.\n return String(resolved).replace(/[;{}]/g, '');\n};\n\n// Strip characters that can break out of a CSS declaration in inline styles.\n// This is applied to both property names and values: semicolons end declarations\n// and braces are meaningful in stylesheet rules (but not inline style values).\nconst UNSAFE_CSS_CHARS = /[;{}]/g;\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * Takes a record of CSS property names (camelCase) with values that can be:\n * - Static strings/numbers\n * - Functions that return strings/numbers\n * - Signals for reactive updates\n *\n * @example\n * ```ts\n * const color = signal('red');\n * const size = signal(16);\n *\n * html`<div :style=${styleMap({\n * backgroundColor: color,\n * width: () => `${size.value}px`,\n * padding: '10px'\n * })}></div>`\n * ```\n */\nexport const styleMap = (record: Record<string, StyleInput>): Readable<string> => {\n return computed(() => {\n const declarations: string[] = [];\n\n for (const [name, input] of Object.entries(record)) {\n const value = toStyleValue(input);\n\n if (!value) continue;\n\n const safeName = toKebab(name).replace(UNSAFE_CSS_CHARS, '');\n\n if (!safeName) continue;\n\n declarations.push(`${safeName}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"sGAaA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,OAAO,GAAU,WAAa,EAAM,EAAI,EAAW,CAAK,EAAI,EAAM,MAAQ,EAO3F,OALI,GAAY,MAAQ,IAAa,GAAc,GAK5C,OAAO,CAAQ,CAAC,CAAC,QAAQ,SAAU,EAAE,CAC9C,EAKM,EAAmB,SAsBZ,EAAY,GAChB,MAAe,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAEhC,GAAI,CAAC,EAAO,SAEZ,IAAM,EAAW,EAAQ,CAAI,CAAC,CAAC,QAAQ,EAAkB,EAAE,EAEtD,GAEL,EAAa,KAAK,GAAG,EAAS,GAAG,GAAO,CAC1C,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../types/bindings.cjs"),t=require("../utils/dom.cjs");let n=require("@vielzeug/ripple");var r=(e,t,n,r)=>e.mount(t,n,r);function i(i,a,o){return typeof i!=`function`&&!(0,n.isReactive)(i)?e.createDirectiveResult((n,s)=>{let c=i?a():o?o():null;if(!c||!e.isHtmlResult(c))return;let l=n.parentNode,u=r(c,l,n,s);s(()=>t.removeNodes(u))}):e.createDirectiveResult((s,c)=>{let l=typeof i==`function`?(0,n.computed)(i):null,u=l??i;l&&c(()=>l.dispose());let d=s.parentNode,f=document.createComment(`when/end`);d.insertBefore(f,s.nextSibling);let p=[],m=[],h=(0,n.effect)(()=>{let i=u.value;t.runAll(m),t.removeNodes(p),m=[],p=[];let s=i?a():o?o():null;if(!s||!e.isHtmlResult(s))return;let c=[];p=(0,n.untrack)(()=>r(s,d,f,e=>c.push(e))),m=c});c(()=>h.dispose()),c(()=>{t.runAll(m),t.removeNodes(p),f.remove()})})}exports.when=i;
|
|
2
|
+
//# sourceMappingURL=when.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, untrack } from '@vielzeug/ripple';\n\nimport type { RegisterCleanup } from '../template';\n\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../types/bindings';\nimport { removeNodes, runAll } from '../utils/dom';\n\ntype MaybeReactive<T> = T | (() => T) | Readable<T>;\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst mountBranch = (\n result: HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n registerCleanup: RegisterCleanup,\n): Node[] => result.mount(parent, insertBefore, registerCleanup);\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor, registerCleanup) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode!;\n const nodes = mountBranch(branch, parent, anchor, registerCleanup);\n\n registerCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n if (ownedComputed) registerCleanup(() => ownedComputed.dispose());\n\n const parent = anchor.parentNode!;\n const endMarker = document.createComment('when/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let currentNodes: Node[] = [];\n let currentCleanups: (() => void)[] = [];\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n\n runAll(currentCleanups);\n removeNodes(currentNodes);\n currentCleanups = [];\n currentNodes = [];\n\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const branchCleanups: (() => void)[] = [];\n\n currentNodes = untrack(() => mountBranch(branch, parent, endMarker, (fn) => branchCleanups.push(fn)));\n currentCleanups = branchCleanups;\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n runAll(currentCleanups);\n removeNodes(currentNodes);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"yGAWA,IAAM,GACJ,EACA,EACA,EACA,IACW,EAAO,MAAM,EAAQ,EAAc,CAAe,EAc/D,SAAgB,EACd,EACA,EACA,EACiB,CAcjB,OAbI,OAAO,GAAc,YAAc,EAAA,EAAA,EAAA,WAAA,CAAY,CAAS,EACnD,EAAA,uBAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAY,EAAO,EAAI,EAAQ,EAAM,EAAI,KAExD,GAAI,CAAC,GAAU,CAAC,EAAA,aAAa,CAAM,EAAG,OAEtC,IAAM,EAAS,EAAO,WAChB,EAAQ,EAAY,EAAQ,EAAQ,EAAQ,CAAe,EAEjE,MAAsB,EAAA,YAAY,CAAK,CAAC,CAC1C,CAAC,EAGI,EAAA,uBAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAgB,OAAO,GAAc,YAAA,EAAA,EAAA,SAAA,CAAsB,CAA0B,EAAI,KACzF,EAAkB,GAAkB,EAEtC,GAAe,MAAsB,EAAc,QAAQ,CAAC,EAEhE,IAAM,EAAS,EAAO,WAChB,EAAY,SAAS,cAAc,UAAU,EAEnD,EAAO,aAAa,EAAW,EAAO,WAAW,EAEjD,IAAI,EAAuB,CAAC,EACxB,EAAkC,CAAC,EAEjC,GAAA,EAAA,EAAA,OAAA,KAAsB,CAC1B,IAAM,EAAO,EAAgB,MAE7B,EAAA,OAAO,CAAe,EACtB,EAAA,YAAY,CAAY,EACxB,EAAkB,CAAC,EACnB,EAAe,CAAC,EAEhB,IAAM,EAAS,EAAO,EAAO,EAAI,EAAQ,EAAM,EAAI,KAEnD,GAAI,CAAC,GAAU,CAAC,EAAA,aAAa,CAAM,EAAG,OAEtC,IAAM,EAAiC,CAAC,EAExC,GAAA,EAAA,EAAA,QAAA,KAA6B,EAAY,EAAQ,EAAQ,EAAY,GAAO,EAAe,KAAK,CAAE,CAAC,CAAC,EACpG,EAAkB,CACpB,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EACnC,MAAsB,CACpB,EAAA,OAAO,CAAe,EACtB,EAAA,YAAY,CAAY,EACxB,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
+
import { type DirectiveResult, type HTMLResult } from '../types/bindings';
|
|
3
|
+
type MaybeReactive<T> = T | (() => T) | Readable<T>;
|
|
4
|
+
type WhenRenderable = HTMLResult | null | undefined | false;
|
|
5
|
+
/**
|
|
6
|
+
* Conditionally renders one of two branches as a `DirectiveResult`.
|
|
7
|
+
*
|
|
8
|
+
* The condition may be a boolean, signal, or getter function.
|
|
9
|
+
* When the condition changes, the current branch is cleaned up and
|
|
10
|
+
* the new branch is mounted in its place.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function when(condition: MaybeReactive<boolean>, truthy: () => WhenRenderable, falsy?: () => WhenRenderable): DirectiveResult;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=when.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"when.d.ts","sourceRoot":"","sources":["../../src/directives/when.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,QAAQ,EAAW,MAAM,kBAAkB,CAAC;AAIrG,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,UAAU,EAAgB,MAAM,mBAAmB,CAAC;AAG/G,KAAK,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAEpD,KAAK,cAAc,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC;AAS5D;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,EACjC,MAAM,EAAE,MAAM,cAAc,EAC5B,KAAK,CAAC,EAAE,MAAM,cAAc,GAC3B,eAAe,CAqDjB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createDirectiveResult as e,isHtmlResult as t}from"../types/bindings.js";import{removeNodes as n,runAll as r}from"../utils/dom.js";import{computed as i,effect as a,isReactive as o,untrack as s}from"@vielzeug/ripple";var c=(e,t,n,r)=>e.mount(t,n,r);function l(l,u,d){return typeof l!=`function`&&!o(l)?e((e,r)=>{let i=l?u():d?d():null;if(!i||!t(i))return;let a=e.parentNode,o=c(i,a,e,r);r(()=>n(o))}):e((e,o)=>{let f=typeof l==`function`?i(l):null,p=f??l;f&&o(()=>f.dispose());let m=e.parentNode,h=document.createComment(`when/end`);m.insertBefore(h,e.nextSibling);let g=[],_=[],v=a(()=>{let e=p.value;r(_),n(g),_=[],g=[];let i=e?u():d?d():null;if(!i||!t(i))return;let a=[];g=s(()=>c(i,m,h,e=>a.push(e))),_=a});o(()=>v.dispose()),o(()=>{r(_),n(g),h.remove()})})}export{l as when};
|
|
2
|
+
//# sourceMappingURL=when.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"when.js","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, untrack } from '@vielzeug/ripple';\n\nimport type { RegisterCleanup } from '../template';\n\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../types/bindings';\nimport { removeNodes, runAll } from '../utils/dom';\n\ntype MaybeReactive<T> = T | (() => T) | Readable<T>;\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst mountBranch = (\n result: HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n registerCleanup: RegisterCleanup,\n): Node[] => result.mount(parent, insertBefore, registerCleanup);\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor, registerCleanup) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode!;\n const nodes = mountBranch(branch, parent, anchor, registerCleanup);\n\n registerCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n if (ownedComputed) registerCleanup(() => ownedComputed.dispose());\n\n const parent = anchor.parentNode!;\n const endMarker = document.createComment('when/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let currentNodes: Node[] = [];\n let currentCleanups: (() => void)[] = [];\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n\n runAll(currentCleanups);\n removeNodes(currentNodes);\n currentCleanups = [];\n currentNodes = [];\n\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const branchCleanups: (() => void)[] = [];\n\n currentNodes = untrack(() => mountBranch(branch, parent, endMarker, (fn) => branchCleanups.push(fn)));\n currentCleanups = branchCleanups;\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n runAll(currentCleanups);\n removeNodes(currentNodes);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"8NAWA,IAAM,GACJ,EACA,EACA,EACA,IACW,EAAO,MAAM,EAAQ,EAAc,CAAe,EAc/D,SAAgB,EACd,EACA,EACA,EACiB,CAcjB,OAbI,OAAO,GAAc,YAAc,CAAC,EAAW,CAAS,EACnD,GAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAY,EAAO,EAAI,EAAQ,EAAM,EAAI,KAExD,GAAI,CAAC,GAAU,CAAC,EAAa,CAAM,EAAG,OAEtC,IAAM,EAAS,EAAO,WAChB,EAAQ,EAAY,EAAQ,EAAQ,EAAQ,CAAe,EAEjE,MAAsB,EAAY,CAAK,CAAC,CAC1C,CAAC,EAGI,GAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAgB,OAAO,GAAc,WAAa,EAAS,CAA0B,EAAI,KACzF,EAAkB,GAAkB,EAEtC,GAAe,MAAsB,EAAc,QAAQ,CAAC,EAEhE,IAAM,EAAS,EAAO,WAChB,EAAY,SAAS,cAAc,UAAU,EAEnD,EAAO,aAAa,EAAW,EAAO,WAAW,EAEjD,IAAI,EAAuB,CAAC,EACxB,EAAkC,CAAC,EAEjC,EAAM,MAAgB,CAC1B,IAAM,EAAO,EAAgB,MAE7B,EAAO,CAAe,EACtB,EAAY,CAAY,EACxB,EAAkB,CAAC,EACnB,EAAe,CAAC,EAEhB,IAAM,EAAS,EAAO,EAAO,EAAI,EAAQ,EAAM,EAAI,KAEnD,GAAI,CAAC,GAAU,CAAC,EAAa,CAAM,EAAG,OAEtC,IAAM,EAAiC,CAAC,EAExC,EAAe,MAAc,EAAY,EAAQ,EAAQ,EAAY,GAAO,EAAe,KAAK,CAAE,CAAC,CAAC,EACpG,EAAkB,CACpB,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EACnC,MAAsB,CACpB,EAAO,CAAe,EACtB,EAAY,CAAY,EACxB,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./directives/classMap.cjs"),t=require("./directives/each.cjs"),n=require("./directives/live.cjs"),r=require("./directives/model.cjs"),i=require("./directives/raw.cjs"),a=require("./directives/styleMap.cjs"),o=require("./directives/when.cjs");exports.classMap=e.classMap,exports.each=t.each,exports.live=n.live,exports.model=r.model,exports.raw=i.raw,exports.setRawSanitizer=i.setRawSanitizer,exports.styleMap=a.styleMap,exports.when=o.when;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{classMap as e}from"./directives/classMap.js";import{each as t}from"./directives/each.js";import{live as n}from"./directives/live.js";import{model as r}from"./directives/model.js";import{raw as i,setRawSanitizer as a}from"./directives/raw.js";import{styleMap as o}from"./directives/styleMap.js";import{when as s}from"./directives/when.js";export{e as classMap,t as each,n as live,r as model,i as raw,a as setRawSanitizer,o as styleMap,s as when};
|
package/dist/errors.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const e=require("./_dev.cjs");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{constructor(e){super(e)}},r=class extends t{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(t,n){e.error(`<${t.component}> setup error (phase: ${t.phase}):`,t.cause),n.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:t}))}var a=class extends t{},o={defineDuplicate:e=>`define('${e}') called twice — custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,propInvalidReflect:`Structured props cannot use reflect:true — use prop.json() with reflect:false`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
|
|
2
|
+
`)}`};exports.ORE_ERRORS=o,exports.OreApiError=n,exports.OreError=t,exports.OreLifecycleError=r,exports.OreTimeoutError=a,exports.reportRuntimeError=i;
|
|
3
|
+
//# sourceMappingURL=errors.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is OreError {\n return err instanceof OreError;\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'` — synchronous setup() threw\n * - `'async-setup'` — async setup() promise rejected\n * - `'mounted'` — an onMounted callback threw\n */\nexport type OreErrorPhase = 'async-setup' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n */\nexport function reportRuntimeError(error: OreLifecycleError, element: HTMLElement): void {\n logError(`<${error.component}> setup error (phase: ${error.phase}):`, error.cause);\n\n element.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\n/** Thrown by `waitFor` and `waitForEvent` in the testing sub-path when a condition is not met within the timeout. */\nexport class OreTimeoutError extends OreError {}\n\nexport const ORE_ERRORS = {\n defineDuplicate: (tag: string): string => `define('${tag}') called twice — custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n propInvalidReflect: 'Structured props cannot use reflect:true — use prop.json() with reflect:false',\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n"],"mappings":"8BAKA,IAAa,EAAb,MAAa,UAAiB,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,CAClD,CAEA,OAAO,GAAG,EAA+B,CACvC,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAiC,CAAS,CACxC,YAAY,EAAiB,CAC3B,MAAM,CAAO,CACf,CACF,EAca,EAAb,cAAuC,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAM,EAAS,CAAE,MAAO,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAY,EAAQ,UACzB,KAAK,MAAQ,EAAQ,KACvB,CACF,EAKA,SAAgB,EAAmB,EAA0B,EAA4B,CACvF,EAAA,MAAS,IAAI,EAAM,UAAU,wBAAwB,EAAM,MAAM,IAAK,EAAM,KAAK,EAEjF,EAAQ,cACN,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQ,CACV,CAAC,CACH,CACF,CAKA,IAAa,EAAb,cAAqC,CAAS,CAAC,EAElC,EAAa,CACxB,gBAAkB,GAAwB,WAAW,EAAI,qDACzD,kCAAoC,GAClC,+BAA+B,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkC,EAAI,aAAa,IAC7G,oBAAqB,EAAa,IAAwB,yCAAyC,EAAI,QAAQ,EAAI,GACnH,sBAAuB,wDACvB,mBAAoB,gFACpB,kBAAmB,EAAa,IAA6B,0BAA0B,EAAI,MAAM,EAAO,KAAK;CAAI,GACnH"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */
|
|
2
|
+
export declare class OreError extends Error {
|
|
3
|
+
constructor(message: string, opts?: ErrorOptions);
|
|
4
|
+
static is(err: unknown): err is OreError;
|
|
5
|
+
}
|
|
6
|
+
/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */
|
|
7
|
+
export declare class OreApiError extends OreError {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The phase in which a component error occurred.
|
|
12
|
+
* - `'setup'` — synchronous setup() threw
|
|
13
|
+
* - `'async-setup'` — async setup() promise rejected
|
|
14
|
+
* - `'mounted'` — an onMounted callback threw
|
|
15
|
+
*/
|
|
16
|
+
export type OreErrorPhase = 'async-setup' | 'mounted' | 'setup';
|
|
17
|
+
/**
|
|
18
|
+
* Structured error thrown by the Ore runtime when component setup fails.
|
|
19
|
+
* Provides component name and original cause for debugging.
|
|
20
|
+
*/
|
|
21
|
+
export declare class OreLifecycleError extends OreError {
|
|
22
|
+
readonly component: string;
|
|
23
|
+
readonly phase: OreErrorPhase;
|
|
24
|
+
constructor(message: string, options: {
|
|
25
|
+
cause: Error;
|
|
26
|
+
component: string;
|
|
27
|
+
phase: OreErrorPhase;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Report a runtime error via the ore:error event and console.
|
|
32
|
+
*/
|
|
33
|
+
export declare function reportRuntimeError(error: OreLifecycleError, element: HTMLElement): void;
|
|
34
|
+
/** Thrown by `waitFor` and `waitForEvent` in the testing sub-path when a condition is not met within the timeout. */
|
|
35
|
+
export declare class OreTimeoutError extends OreError {
|
|
36
|
+
}
|
|
37
|
+
export declare const ORE_ERRORS: {
|
|
38
|
+
readonly defineDuplicate: (tag: string) => string;
|
|
39
|
+
readonly defineFieldRequiresFormAssociated: (tag: string) => string;
|
|
40
|
+
readonly defineRequiresTag: "define() requires a tag name";
|
|
41
|
+
readonly eachDuplicateKey: (key: string, index: number) => string;
|
|
42
|
+
readonly injectStrictFailed: (key: string, tag: string) => string;
|
|
43
|
+
readonly lifecycleOutsideSetup: "Lifecycle hooks must be called during component setup";
|
|
44
|
+
readonly propInvalidReflect: "Structured props cannot use reflect:true — use prop.json() with reflect:false";
|
|
45
|
+
readonly validationFailed: (tag: string, errors: string[]) => string;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=errors.d.ts.map
|