@vielzeug/ore 1.2.2 → 1.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"props.cjs","names":[],"sources":["../src/props.ts"],"sourcesContent":["import { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { warn } from './_dev';\nimport { invariant, OreApiError, ORE_ERRORS } from './errors';\nimport { watchEffect } from './runtime';\nimport { isStructuredValue, setAttr, toKebab } from './utils/dom';\n\nexport type PropsDef<T extends Record<string, unknown>> = {\n // All props must be explicitly defined via prop.* helpers or PropDef objects\n [K in keyof Required<T>]: PropDef<T[K & keyof T]>;\n};\n\nexport type PropDef<T> = { readonly default: T; readonly parse: (value: string | null) => T; reflect?: boolean };\nexport type PropInputDefs = Record<string, PropDef<unknown>>;\n\n/**\n * Prop definition factory — use these helpers for all prop definitions.\n * No implicit type inference; all parser behavior is explicit and intentional.\n */\ntype PropFactory = {\n bool(defaultValue?: boolean): PropDef<boolean>;\n /**\n * JS-only property — never reads from or writes to an HTML attribute.\n * Use for complex objects, arrays, callbacks, or any non-serialisable value.\n *\n * @example\n * ```ts\n * columns: prop.data<DataGridColumn[]>([]),\n * config: prop.data<Options>(),\n * getRowKey: prop.data<(row: T) => string>(),\n * onSelect: prop.data<(item: T) => void>(),\n * ```\n */\n data<T>(): PropDef<T | undefined>;\n data<T>(defaultValue: T): PropDef<T>;\n json<T>(defaultValue: T): PropDef<T>;\n number<T extends number = number>(): PropDef<T | undefined>;\n number<T extends number = number>(defaultValue: number): PropDef<T>;\n oneOf<T extends string | undefined, D extends T = T>(allowed: readonly NonNullable<T>[], defaultValue: D): PropDef<T>;\n string<T extends string = string>(): PropDef<T | undefined>;\n string<T extends string = string>(defaultValue: string): PropDef<T>;\n};\n\n/** @internal JS-only prop implementation — never reads/writes attributes. */\nfunction _jsOnlyProp<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return { default: defaultValue, parse: () => defaultValue, reflect: false } as PropDef<T | undefined>;\n}\n\nexport const prop: PropFactory = {\n /**\n * Boolean prop — reflects its value as a presence-only attribute (toggleAttribute).\n * Reflection is always enabled. To suppress attribute reflection for a boolean,\n * use `prop.data<boolean>(false)` instead.\n */\n bool(defaultValue?: boolean): PropDef<boolean> {\n const def = defaultValue ?? false;\n\n return {\n default: def,\n parse: (value) => value !== null && value !== 'false',\n reflect: true,\n };\n },\n data<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return _jsOnlyProp(defaultValue);\n },\n /**\n * JSON-serialisable prop — the value is stored as a JS object and parsed\n * from the attribute via `JSON.parse`. Reflection is always disabled\n * because serialising complex objects back to attributes on every change\n * would be expensive and produce unreadable HTML.\n *\n * @example\n * ```ts\n * columns: prop.json<Column[]>([]),\n * // attribute: <my-grid columns='[{\"id\":\"name\"}]'>\n * // change not reflected back → attribute stays static after upgrade\n * ```\n */\n json<T>(defaultValue: T): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => {\n if (value == null || value === '') return defaultValue;\n\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n },\n reflect: false,\n };\n },\n number<T extends number = number>(defaultValue?: number): PropDef<T> | PropDef<T | undefined> {\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value) => {\n if (value == null) return def;\n\n const n = Number(value);\n\n if (Number.isNaN(n)) {\n warn(`prop.number(): attribute value \"${value}\" is not a valid number, using default (${String(def)})`);\n\n return def;\n }\n\n return n as T;\n },\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n oneOf<T extends string | undefined, D extends T = T>(\n allowed: readonly NonNullable<T>[],\n defaultValue: D,\n ): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => (value != null && allowed.includes(value as NonNullable<T>) ? (value as T) : defaultValue),\n reflect: true,\n };\n },\n string<T extends string = string>(defaultValue?: string): PropDef<T> | PropDef<T | undefined> {\n // When no default provided, undefined sentinel → attribute removed when value is absent\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value: string | null) => (value == null ? def : (value as T)),\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n};\n\n/**\n * Define a custom prop with explicit parser and reflection behavior.\n * Use when prop.* helpers don't cover your type.\n *\n * @example\n * ```ts\n * const customProp = (): PropDef<MyCustomType> => ({\n * default: new MyCustomType(),\n * parse: (value) => JSON.parse(value || '{}') as MyCustomType,\n * reflect: false,\n * });\n * ```\n */\n\nconst isPropDef = (value: unknown): value is PropDef<unknown> =>\n typeof value === 'object' && value !== null && 'default' in value && 'parse' in value;\n\n/**\n * Validate and normalize a prop definition.\n * Throws if definition is invalid or incomplete.\n */\nexport function normalizePropDefinition<T>(value: unknown, propName: string): PropDef<T> {\n if (!isPropDef(value)) {\n throw new OreApiError(\n `Prop \"${propName}\" must use a prop.* helper (string/number/bool/json/oneOf). Received: ${typeof value}`,\n );\n }\n\n const descriptor = value as PropDef<T>;\n\n if (!descriptor.parse) {\n throw new OreApiError(`Prop \"${propName}\" must have a parse function. Use prop.* helpers.`);\n }\n\n const reflect = descriptor.reflect ?? false;\n\n // Validate: structured defaults with reflect:true are not allowed\n if (reflect && isStructuredValue(descriptor.default)) {\n throw new OreApiError(`Prop \"${propName}\": ${ORE_ERRORS.propInvalidReflect}`);\n }\n\n return {\n ...descriptor,\n reflect,\n };\n}\n\n/**\n * Validate all prop definitions at define-time.\n * Returns validation error messages or empty array if valid.\n */\nexport function validatePropDefs(defs: Record<string, unknown>): string[] {\n const errors: string[] = [];\n\n for (const [key, value] of Object.entries(defs)) {\n try {\n normalizePropDefinition(value, key);\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n }\n }\n\n return errors;\n}\n\ntype PropMeta<T = unknown> = {\n parse: (value: string | null) => T;\n reflect: boolean;\n signal: Signal<T>;\n};\n\nconst propRegistry = new WeakMap<HTMLElement, Map<string, PropMeta<unknown>>>();\n\n/**\n * Look up the registered prop metadata for a given attribute name on an element.\n * Used by the template compiler to resolve prop bindings without exposing the registry.\n */\nexport const getPropMeta = (el: HTMLElement, attrName: string): PropMeta<unknown> | undefined =>\n propRegistry.get(el)?.get(attrName);\n\n/**\n * A framework can hand a prop a raw attribute-style string well after the element\n * has upgraded — e.g. Vue reconciling server-rendered markup against a custom\n * element that auto-upgraded from its SSR attribute before hydration ran, or\n * assigning the pre-upgrade instance property it captured before `registerProp`\n * defined the real accessor. In both cases route the string through the same\n * parser an attribute value would use (so e.g. `size=\"16\"` doesn't end up stored\n * as the string `\"16\"` instead of the number `16`). Already-typed values (objects,\n * functions, booleans, numbers set deliberately as JS properties) pass through untouched.\n */\nconst coerceIncoming = <T>(value: unknown, parse: PropDef<T>['parse']): T =>\n (typeof value === 'string' ? parse(value) : value) as T;\n\n/** @internal Runtime prop registration (called by createProps) */\nconst registerProp = <T>(el: HTMLElement, propName: string, attrName: string, propDef: PropDef<T>): Signal<T> => {\n if (!propRegistry.has(el)) propRegistry.set(el, new Map());\n\n const { default: defaultValue, parse, reflect = false } = propDef;\n const s = signal<T>(defaultValue);\n const hasPreUpgradeProperty = Object.hasOwn(el as unknown as Record<string, unknown>, propName);\n const preUpgradeValue = hasPreUpgradeProperty ? (el as unknown as Record<string, unknown>)[propName] : undefined;\n\n const meta: PropMeta<unknown> = {\n parse,\n reflect,\n signal: s,\n };\n\n if (hasPreUpgradeProperty) {\n delete (el as unknown as Record<string, unknown>)[propName];\n s.value = coerceIncoming(preUpgradeValue, parse);\n } else if (el.hasAttribute(attrName)) {\n s.value = parse(el.getAttribute(attrName)) as T;\n }\n\n const registry = propRegistry.get(el);\n\n invariant(registry, `propRegistry entry missing for <${el.localName}> — registerProp() must create it above`);\n registry.set(attrName, meta);\n\n Object.defineProperty(el, propName, {\n configurable: true,\n enumerable: true,\n get: () => s.value,\n set: (value: T) => {\n s.value = coerceIncoming(value, parse);\n },\n });\n\n if (reflect) {\n watchEffect(() => {\n const v = s.value;\n\n if (v == null) {\n el.removeAttribute(attrName);\n } else if (typeof v === 'boolean') {\n el.toggleAttribute(attrName, v);\n } else {\n setAttr(el, attrName, v);\n }\n });\n }\n\n return s;\n};\n\nexport type InferPropValue<T> = T extends PropDef<infer U> ? U : T;\n\n/**\n * Infer the reactive props object type from a `PropInputDefs` map.\n * Each entry becomes a `Reactive<T>` keyed by the prop name.\n *\n * @example\n * ```ts\n * const propDefs = { count: prop.number(0), label: prop.string('hi') };\n * type Props = InferProps<typeof propDefs>;\n * // => { readonly count: Readable<number>; readonly label: Readable<string> }\n * ```\n */\nexport type InferProps<D extends PropInputDefs> = {\n readonly [K in keyof D]-?: Readable<InferPropValue<D[K]>>;\n};\n\nexport function createProps<D extends PropInputDefs>(el: HTMLElement, defs: D): InferProps<D> {\n const props = {} as Record<string, Signal<unknown>>;\n\n for (const [name, def] of Object.entries(defs)) {\n // defs passed here are already normalized by define(); skip re-normalization.\n const attrName = toKebab(name);\n\n props[name] = registerProp(el, name, attrName, def as PropDef<unknown>);\n }\n\n return props as unknown as InferProps<D>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"props.cjs","names":[],"sources":["../src/props.ts"],"sourcesContent":["import { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { warn } from './_dev';\nimport { invariant, OreApiError, ORE_ERRORS } from './errors';\nimport { watchEffect } from './runtime';\nimport { isStructuredValue, setAttr, toKebab } from './utils/dom';\n\nexport type PropsDef<T extends Record<string, unknown>> = {\n // All props must be explicitly defined via prop.* helpers or PropDef objects\n [K in keyof Required<T>]: PropDef<T[K & keyof T]>;\n};\n\nexport type PropDef<T> = { readonly default: T; readonly parse: (value: string | null) => T; reflect?: boolean };\nexport type PropInputDefs = Record<string, PropDef<unknown>>;\n\n/**\n * Prop definition factory — use these helpers for all prop definitions.\n * No implicit type inference; all parser behavior is explicit and intentional.\n */\ntype PropFactory = {\n bool(defaultValue?: boolean): PropDef<boolean>;\n /**\n * JS-only property — never reads from or writes to an HTML attribute.\n * Use for complex objects, arrays, callbacks, or any non-serialisable value.\n *\n * @example\n * ```ts\n * columns: prop.data<DataGridColumn[]>([]),\n * config: prop.data<Options>(),\n * getRowKey: prop.data<(row: T) => string>(),\n * onSelect: prop.data<(item: T) => void>(),\n * ```\n */\n data<T>(): PropDef<T | undefined>;\n data<T>(defaultValue: T): PropDef<T>;\n json<T>(defaultValue: T): PropDef<T>;\n number<T extends number = number>(): PropDef<T | undefined>;\n number<T extends number = number>(defaultValue: number): PropDef<T>;\n oneOf<T extends string | undefined, D extends T = T>(allowed: readonly NonNullable<T>[], defaultValue: D): PropDef<T>;\n string<T extends string = string>(): PropDef<T | undefined>;\n string<T extends string = string>(defaultValue: string): PropDef<T>;\n};\n\n/** @internal JS-only prop implementation — never reads/writes attributes. */\nfunction _jsOnlyProp<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return { default: defaultValue, parse: () => defaultValue, reflect: false } as PropDef<T | undefined>;\n}\n\nexport const prop: PropFactory = {\n /**\n * Boolean prop — reflects its value as a presence-only attribute (toggleAttribute).\n * Reflection is always enabled. To suppress attribute reflection for a boolean,\n * use `prop.data<boolean>(false)` instead.\n */\n bool(defaultValue?: boolean): PropDef<boolean> {\n const def = defaultValue ?? false;\n\n return {\n default: def,\n parse: (value) => value !== null && value !== 'false',\n reflect: true,\n };\n },\n data<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return _jsOnlyProp(defaultValue);\n },\n /**\n * JSON-serialisable prop — the value is stored as a JS object and parsed\n * from the attribute via `JSON.parse`. Reflection is always disabled\n * because serialising complex objects back to attributes on every change\n * would be expensive and produce unreadable HTML.\n *\n * @example\n * ```ts\n * columns: prop.json<Column[]>([]),\n * // attribute: <my-grid columns='[{\"id\":\"name\"}]'>\n * // change not reflected back → attribute stays static after upgrade\n * ```\n */\n json<T>(defaultValue: T): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => {\n if (value == null || value === '') return defaultValue;\n\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n },\n reflect: false,\n };\n },\n number<T extends number = number>(defaultValue?: number): PropDef<T> | PropDef<T | undefined> {\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value) => {\n if (value == null) return def;\n\n const n = Number(value);\n\n if (Number.isNaN(n)) {\n warn(`prop.number(): attribute value \"${value}\" is not a valid number, using default (${String(def)})`);\n\n return def;\n }\n\n return n as T;\n },\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n oneOf<T extends string | undefined, D extends T = T>(\n allowed: readonly NonNullable<T>[],\n defaultValue: D,\n ): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => (value != null && allowed.includes(value as NonNullable<T>) ? (value as T) : defaultValue),\n reflect: true,\n };\n },\n string<T extends string = string>(defaultValue?: string): PropDef<T> | PropDef<T | undefined> {\n // When no default provided, undefined sentinel → attribute removed when value is absent\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value: string | null) => (value == null ? def : (value as T)),\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n};\n\nconst isPropDef = (value: unknown): value is PropDef<unknown> =>\n typeof value === 'object' && value !== null && 'default' in value && 'parse' in value;\n\n/**\n * Validate and normalize a prop definition.\n * Throws if definition is invalid or incomplete.\n *\n * Accepts any object shaped like `PropDef` — not just `prop.*` helper output — so you can\n * define a custom prop with explicit parser and reflection behavior when the `prop.*` helpers\n * don't cover your type:\n *\n * ```ts\n * const customProp = (): PropDef<MyCustomType> => ({\n * default: new MyCustomType(),\n * parse: (value) => JSON.parse(value || '{}') as MyCustomType,\n * reflect: false,\n * });\n * ```\n */\nexport function normalizePropDefinition<T>(value: unknown, propName: string): PropDef<T> {\n if (!isPropDef(value)) {\n throw new OreApiError(\n `Prop \"${propName}\" must use a prop.* helper (string/number/bool/json/oneOf). Received: ${typeof value}`,\n );\n }\n\n const descriptor = value as PropDef<T>;\n\n if (!descriptor.parse) {\n throw new OreApiError(`Prop \"${propName}\" must have a parse function. Use prop.* helpers.`);\n }\n\n const reflect = descriptor.reflect ?? false;\n\n // Validate: structured defaults with reflect:true are not allowed\n if (reflect && isStructuredValue(descriptor.default)) {\n throw new OreApiError(`Prop \"${propName}\": ${ORE_ERRORS.propInvalidReflect}`);\n }\n\n return {\n ...descriptor,\n reflect,\n };\n}\n\n/**\n * Validate all prop definitions at define-time.\n * Returns validation error messages or empty array if valid.\n */\nexport function validatePropDefs(defs: Record<string, unknown>): string[] {\n const errors: string[] = [];\n\n for (const [key, value] of Object.entries(defs)) {\n try {\n normalizePropDefinition(value, key);\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n }\n }\n\n return errors;\n}\n\ntype PropMeta<T = unknown> = {\n parse: (value: string | null) => T;\n reflect: boolean;\n signal: Signal<T>;\n};\n\nconst propRegistry = new WeakMap<HTMLElement, Map<string, PropMeta<unknown>>>();\n\n/**\n * Look up the registered prop metadata for a given attribute name on an element.\n * Used by the template compiler to resolve prop bindings without exposing the registry.\n */\nexport const getPropMeta = (el: HTMLElement, attrName: string): PropMeta<unknown> | undefined =>\n propRegistry.get(el)?.get(attrName);\n\n/**\n * A framework can hand a prop a raw attribute-style string well after the element\n * has upgraded — e.g. Vue reconciling server-rendered markup against a custom\n * element that auto-upgraded from its SSR attribute before hydration ran, or\n * assigning the pre-upgrade instance property it captured before `registerProp`\n * defined the real accessor. In both cases route the string through the same\n * parser an attribute value would use (so e.g. `size=\"16\"` doesn't end up stored\n * as the string `\"16\"` instead of the number `16`). Already-typed values (objects,\n * functions, booleans, numbers set deliberately as JS properties) pass through untouched.\n */\nconst coerceIncoming = <T>(value: unknown, parse: PropDef<T>['parse']): T =>\n (typeof value === 'string' ? parse(value) : value) as T;\n\n/** @internal Runtime prop registration (called by createProps) */\nconst registerProp = <T>(el: HTMLElement, propName: string, attrName: string, propDef: PropDef<T>): Signal<T> => {\n if (!propRegistry.has(el)) propRegistry.set(el, new Map());\n\n const { default: defaultValue, parse, reflect = false } = propDef;\n const s = signal<T>(defaultValue);\n const hasPreUpgradeProperty = Object.hasOwn(el as unknown as Record<string, unknown>, propName);\n const preUpgradeValue = hasPreUpgradeProperty ? (el as unknown as Record<string, unknown>)[propName] : undefined;\n\n const meta: PropMeta<unknown> = {\n parse,\n reflect,\n signal: s,\n };\n\n if (hasPreUpgradeProperty) {\n delete (el as unknown as Record<string, unknown>)[propName];\n s.value = coerceIncoming(preUpgradeValue, parse);\n } else if (el.hasAttribute(attrName)) {\n s.value = parse(el.getAttribute(attrName)) as T;\n }\n\n const registry = propRegistry.get(el);\n\n invariant(registry, `propRegistry entry missing for <${el.localName}> — registerProp() must create it above`);\n registry.set(attrName, meta);\n\n Object.defineProperty(el, propName, {\n configurable: true,\n enumerable: true,\n get: () => s.value,\n set: (value: T) => {\n s.value = coerceIncoming(value, parse);\n },\n });\n\n if (reflect) {\n watchEffect(() => {\n const v = s.value;\n\n if (v == null) {\n el.removeAttribute(attrName);\n } else if (typeof v === 'boolean') {\n el.toggleAttribute(attrName, v);\n } else {\n setAttr(el, attrName, v);\n }\n });\n }\n\n return s;\n};\n\nexport type InferPropValue<T> = T extends PropDef<infer U> ? U : T;\n\n/**\n * Infer the reactive props object type from a `PropInputDefs` map.\n * Each entry becomes a `Reactive<T>` keyed by the prop name.\n *\n * @example\n * ```ts\n * const propDefs = { count: prop.number(0), label: prop.string('hi') };\n * type Props = InferProps<typeof propDefs>;\n * // => { readonly count: Readable<number>; readonly label: Readable<string> }\n * ```\n */\nexport type InferProps<D extends PropInputDefs> = {\n readonly [K in keyof D]-?: Readable<InferPropValue<D[K]>>;\n};\n\nexport function createProps<D extends PropInputDefs>(el: HTMLElement, defs: D): InferProps<D> {\n const props = {} as Record<string, Signal<unknown>>;\n\n for (const [name, def] of Object.entries(defs)) {\n // defs passed here are already normalized by define(); skip re-normalization.\n const attrName = toKebab(name);\n\n props[name] = registerProp(el, name, attrName, def as PropDef<unknown>);\n }\n\n return props as unknown as InferProps<D>;\n}\n"],"mappings":"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"}
package/dist/props.d.ts CHANGED
@@ -39,6 +39,18 @@ export declare const prop: PropFactory;
39
39
  /**
40
40
  * Validate and normalize a prop definition.
41
41
  * Throws if definition is invalid or incomplete.
42
+ *
43
+ * Accepts any object shaped like `PropDef` — not just `prop.*` helper output — so you can
44
+ * define a custom prop with explicit parser and reflection behavior when the `prop.*` helpers
45
+ * don't cover your type:
46
+ *
47
+ * ```ts
48
+ * const customProp = (): PropDef<MyCustomType> => ({
49
+ * default: new MyCustomType(),
50
+ * parse: (value) => JSON.parse(value || '{}') as MyCustomType,
51
+ * reflect: false,
52
+ * });
53
+ * ```
42
54
  */
43
55
  export declare function normalizePropDefinition<T>(value: unknown, propName: string): PropDef<T>;
44
56
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../src/props.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../src/props.ts"],"names":[],"mappings":"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"}
package/dist/props.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"props.js","names":[],"sources":["../src/props.ts"],"sourcesContent":["import { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { warn } from './_dev';\nimport { invariant, OreApiError, ORE_ERRORS } from './errors';\nimport { watchEffect } from './runtime';\nimport { isStructuredValue, setAttr, toKebab } from './utils/dom';\n\nexport type PropsDef<T extends Record<string, unknown>> = {\n // All props must be explicitly defined via prop.* helpers or PropDef objects\n [K in keyof Required<T>]: PropDef<T[K & keyof T]>;\n};\n\nexport type PropDef<T> = { readonly default: T; readonly parse: (value: string | null) => T; reflect?: boolean };\nexport type PropInputDefs = Record<string, PropDef<unknown>>;\n\n/**\n * Prop definition factory — use these helpers for all prop definitions.\n * No implicit type inference; all parser behavior is explicit and intentional.\n */\ntype PropFactory = {\n bool(defaultValue?: boolean): PropDef<boolean>;\n /**\n * JS-only property — never reads from or writes to an HTML attribute.\n * Use for complex objects, arrays, callbacks, or any non-serialisable value.\n *\n * @example\n * ```ts\n * columns: prop.data<DataGridColumn[]>([]),\n * config: prop.data<Options>(),\n * getRowKey: prop.data<(row: T) => string>(),\n * onSelect: prop.data<(item: T) => void>(),\n * ```\n */\n data<T>(): PropDef<T | undefined>;\n data<T>(defaultValue: T): PropDef<T>;\n json<T>(defaultValue: T): PropDef<T>;\n number<T extends number = number>(): PropDef<T | undefined>;\n number<T extends number = number>(defaultValue: number): PropDef<T>;\n oneOf<T extends string | undefined, D extends T = T>(allowed: readonly NonNullable<T>[], defaultValue: D): PropDef<T>;\n string<T extends string = string>(): PropDef<T | undefined>;\n string<T extends string = string>(defaultValue: string): PropDef<T>;\n};\n\n/** @internal JS-only prop implementation — never reads/writes attributes. */\nfunction _jsOnlyProp<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return { default: defaultValue, parse: () => defaultValue, reflect: false } as PropDef<T | undefined>;\n}\n\nexport const prop: PropFactory = {\n /**\n * Boolean prop — reflects its value as a presence-only attribute (toggleAttribute).\n * Reflection is always enabled. To suppress attribute reflection for a boolean,\n * use `prop.data<boolean>(false)` instead.\n */\n bool(defaultValue?: boolean): PropDef<boolean> {\n const def = defaultValue ?? false;\n\n return {\n default: def,\n parse: (value) => value !== null && value !== 'false',\n reflect: true,\n };\n },\n data<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return _jsOnlyProp(defaultValue);\n },\n /**\n * JSON-serialisable prop — the value is stored as a JS object and parsed\n * from the attribute via `JSON.parse`. Reflection is always disabled\n * because serialising complex objects back to attributes on every change\n * would be expensive and produce unreadable HTML.\n *\n * @example\n * ```ts\n * columns: prop.json<Column[]>([]),\n * // attribute: <my-grid columns='[{\"id\":\"name\"}]'>\n * // change not reflected back → attribute stays static after upgrade\n * ```\n */\n json<T>(defaultValue: T): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => {\n if (value == null || value === '') return defaultValue;\n\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n },\n reflect: false,\n };\n },\n number<T extends number = number>(defaultValue?: number): PropDef<T> | PropDef<T | undefined> {\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value) => {\n if (value == null) return def;\n\n const n = Number(value);\n\n if (Number.isNaN(n)) {\n warn(`prop.number(): attribute value \"${value}\" is not a valid number, using default (${String(def)})`);\n\n return def;\n }\n\n return n as T;\n },\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n oneOf<T extends string | undefined, D extends T = T>(\n allowed: readonly NonNullable<T>[],\n defaultValue: D,\n ): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => (value != null && allowed.includes(value as NonNullable<T>) ? (value as T) : defaultValue),\n reflect: true,\n };\n },\n string<T extends string = string>(defaultValue?: string): PropDef<T> | PropDef<T | undefined> {\n // When no default provided, undefined sentinel → attribute removed when value is absent\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value: string | null) => (value == null ? def : (value as T)),\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n};\n\n/**\n * Define a custom prop with explicit parser and reflection behavior.\n * Use when prop.* helpers don't cover your type.\n *\n * @example\n * ```ts\n * const customProp = (): PropDef<MyCustomType> => ({\n * default: new MyCustomType(),\n * parse: (value) => JSON.parse(value || '{}') as MyCustomType,\n * reflect: false,\n * });\n * ```\n */\n\nconst isPropDef = (value: unknown): value is PropDef<unknown> =>\n typeof value === 'object' && value !== null && 'default' in value && 'parse' in value;\n\n/**\n * Validate and normalize a prop definition.\n * Throws if definition is invalid or incomplete.\n */\nexport function normalizePropDefinition<T>(value: unknown, propName: string): PropDef<T> {\n if (!isPropDef(value)) {\n throw new OreApiError(\n `Prop \"${propName}\" must use a prop.* helper (string/number/bool/json/oneOf). Received: ${typeof value}`,\n );\n }\n\n const descriptor = value as PropDef<T>;\n\n if (!descriptor.parse) {\n throw new OreApiError(`Prop \"${propName}\" must have a parse function. Use prop.* helpers.`);\n }\n\n const reflect = descriptor.reflect ?? false;\n\n // Validate: structured defaults with reflect:true are not allowed\n if (reflect && isStructuredValue(descriptor.default)) {\n throw new OreApiError(`Prop \"${propName}\": ${ORE_ERRORS.propInvalidReflect}`);\n }\n\n return {\n ...descriptor,\n reflect,\n };\n}\n\n/**\n * Validate all prop definitions at define-time.\n * Returns validation error messages or empty array if valid.\n */\nexport function validatePropDefs(defs: Record<string, unknown>): string[] {\n const errors: string[] = [];\n\n for (const [key, value] of Object.entries(defs)) {\n try {\n normalizePropDefinition(value, key);\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n }\n }\n\n return errors;\n}\n\ntype PropMeta<T = unknown> = {\n parse: (value: string | null) => T;\n reflect: boolean;\n signal: Signal<T>;\n};\n\nconst propRegistry = new WeakMap<HTMLElement, Map<string, PropMeta<unknown>>>();\n\n/**\n * Look up the registered prop metadata for a given attribute name on an element.\n * Used by the template compiler to resolve prop bindings without exposing the registry.\n */\nexport const getPropMeta = (el: HTMLElement, attrName: string): PropMeta<unknown> | undefined =>\n propRegistry.get(el)?.get(attrName);\n\n/**\n * A framework can hand a prop a raw attribute-style string well after the element\n * has upgraded — e.g. Vue reconciling server-rendered markup against a custom\n * element that auto-upgraded from its SSR attribute before hydration ran, or\n * assigning the pre-upgrade instance property it captured before `registerProp`\n * defined the real accessor. In both cases route the string through the same\n * parser an attribute value would use (so e.g. `size=\"16\"` doesn't end up stored\n * as the string `\"16\"` instead of the number `16`). Already-typed values (objects,\n * functions, booleans, numbers set deliberately as JS properties) pass through untouched.\n */\nconst coerceIncoming = <T>(value: unknown, parse: PropDef<T>['parse']): T =>\n (typeof value === 'string' ? parse(value) : value) as T;\n\n/** @internal Runtime prop registration (called by createProps) */\nconst registerProp = <T>(el: HTMLElement, propName: string, attrName: string, propDef: PropDef<T>): Signal<T> => {\n if (!propRegistry.has(el)) propRegistry.set(el, new Map());\n\n const { default: defaultValue, parse, reflect = false } = propDef;\n const s = signal<T>(defaultValue);\n const hasPreUpgradeProperty = Object.hasOwn(el as unknown as Record<string, unknown>, propName);\n const preUpgradeValue = hasPreUpgradeProperty ? (el as unknown as Record<string, unknown>)[propName] : undefined;\n\n const meta: PropMeta<unknown> = {\n parse,\n reflect,\n signal: s,\n };\n\n if (hasPreUpgradeProperty) {\n delete (el as unknown as Record<string, unknown>)[propName];\n s.value = coerceIncoming(preUpgradeValue, parse);\n } else if (el.hasAttribute(attrName)) {\n s.value = parse(el.getAttribute(attrName)) as T;\n }\n\n const registry = propRegistry.get(el);\n\n invariant(registry, `propRegistry entry missing for <${el.localName}> — registerProp() must create it above`);\n registry.set(attrName, meta);\n\n Object.defineProperty(el, propName, {\n configurable: true,\n enumerable: true,\n get: () => s.value,\n set: (value: T) => {\n s.value = coerceIncoming(value, parse);\n },\n });\n\n if (reflect) {\n watchEffect(() => {\n const v = s.value;\n\n if (v == null) {\n el.removeAttribute(attrName);\n } else if (typeof v === 'boolean') {\n el.toggleAttribute(attrName, v);\n } else {\n setAttr(el, attrName, v);\n }\n });\n }\n\n return s;\n};\n\nexport type InferPropValue<T> = T extends PropDef<infer U> ? U : T;\n\n/**\n * Infer the reactive props object type from a `PropInputDefs` map.\n * Each entry becomes a `Reactive<T>` keyed by the prop name.\n *\n * @example\n * ```ts\n * const propDefs = { count: prop.number(0), label: prop.string('hi') };\n * type Props = InferProps<typeof propDefs>;\n * // => { readonly count: Readable<number>; readonly label: Readable<string> }\n * ```\n */\nexport type InferProps<D extends PropInputDefs> = {\n readonly [K in keyof D]-?: Readable<InferPropValue<D[K]>>;\n};\n\nexport function createProps<D extends PropInputDefs>(el: HTMLElement, defs: D): InferProps<D> {\n const props = {} as Record<string, Signal<unknown>>;\n\n for (const [name, def] of Object.entries(defs)) {\n // defs passed here are already normalized by define(); skip re-normalization.\n const attrName = toKebab(name);\n\n props[name] = registerProp(el, name, attrName, def as PropDef<unknown>);\n }\n\n return props as unknown as InferProps<D>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"props.js","names":[],"sources":["../src/props.ts"],"sourcesContent":["import { type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { warn } from './_dev';\nimport { invariant, OreApiError, ORE_ERRORS } from './errors';\nimport { watchEffect } from './runtime';\nimport { isStructuredValue, setAttr, toKebab } from './utils/dom';\n\nexport type PropsDef<T extends Record<string, unknown>> = {\n // All props must be explicitly defined via prop.* helpers or PropDef objects\n [K in keyof Required<T>]: PropDef<T[K & keyof T]>;\n};\n\nexport type PropDef<T> = { readonly default: T; readonly parse: (value: string | null) => T; reflect?: boolean };\nexport type PropInputDefs = Record<string, PropDef<unknown>>;\n\n/**\n * Prop definition factory — use these helpers for all prop definitions.\n * No implicit type inference; all parser behavior is explicit and intentional.\n */\ntype PropFactory = {\n bool(defaultValue?: boolean): PropDef<boolean>;\n /**\n * JS-only property — never reads from or writes to an HTML attribute.\n * Use for complex objects, arrays, callbacks, or any non-serialisable value.\n *\n * @example\n * ```ts\n * columns: prop.data<DataGridColumn[]>([]),\n * config: prop.data<Options>(),\n * getRowKey: prop.data<(row: T) => string>(),\n * onSelect: prop.data<(item: T) => void>(),\n * ```\n */\n data<T>(): PropDef<T | undefined>;\n data<T>(defaultValue: T): PropDef<T>;\n json<T>(defaultValue: T): PropDef<T>;\n number<T extends number = number>(): PropDef<T | undefined>;\n number<T extends number = number>(defaultValue: number): PropDef<T>;\n oneOf<T extends string | undefined, D extends T = T>(allowed: readonly NonNullable<T>[], defaultValue: D): PropDef<T>;\n string<T extends string = string>(): PropDef<T | undefined>;\n string<T extends string = string>(defaultValue: string): PropDef<T>;\n};\n\n/** @internal JS-only prop implementation — never reads/writes attributes. */\nfunction _jsOnlyProp<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return { default: defaultValue, parse: () => defaultValue, reflect: false } as PropDef<T | undefined>;\n}\n\nexport const prop: PropFactory = {\n /**\n * Boolean prop — reflects its value as a presence-only attribute (toggleAttribute).\n * Reflection is always enabled. To suppress attribute reflection for a boolean,\n * use `prop.data<boolean>(false)` instead.\n */\n bool(defaultValue?: boolean): PropDef<boolean> {\n const def = defaultValue ?? false;\n\n return {\n default: def,\n parse: (value) => value !== null && value !== 'false',\n reflect: true,\n };\n },\n data<T>(defaultValue?: T): PropDef<T | undefined> | PropDef<T> {\n return _jsOnlyProp(defaultValue);\n },\n /**\n * JSON-serialisable prop — the value is stored as a JS object and parsed\n * from the attribute via `JSON.parse`. Reflection is always disabled\n * because serialising complex objects back to attributes on every change\n * would be expensive and produce unreadable HTML.\n *\n * @example\n * ```ts\n * columns: prop.json<Column[]>([]),\n * // attribute: <my-grid columns='[{\"id\":\"name\"}]'>\n * // change not reflected back → attribute stays static after upgrade\n * ```\n */\n json<T>(defaultValue: T): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => {\n if (value == null || value === '') return defaultValue;\n\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n },\n reflect: false,\n };\n },\n number<T extends number = number>(defaultValue?: number): PropDef<T> | PropDef<T | undefined> {\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value) => {\n if (value == null) return def;\n\n const n = Number(value);\n\n if (Number.isNaN(n)) {\n warn(`prop.number(): attribute value \"${value}\" is not a valid number, using default (${String(def)})`);\n\n return def;\n }\n\n return n as T;\n },\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n oneOf<T extends string | undefined, D extends T = T>(\n allowed: readonly NonNullable<T>[],\n defaultValue: D,\n ): PropDef<T> {\n return {\n default: defaultValue,\n parse: (value) => (value != null && allowed.includes(value as NonNullable<T>) ? (value as T) : defaultValue),\n reflect: true,\n };\n },\n string<T extends string = string>(defaultValue?: string): PropDef<T> | PropDef<T | undefined> {\n // When no default provided, undefined sentinel → attribute removed when value is absent\n const def = defaultValue !== undefined ? (defaultValue as T) : undefined;\n\n return {\n default: def,\n parse: (value: string | null) => (value == null ? def : (value as T)),\n reflect: true,\n } as PropDef<T> | PropDef<T | undefined>;\n },\n};\n\nconst isPropDef = (value: unknown): value is PropDef<unknown> =>\n typeof value === 'object' && value !== null && 'default' in value && 'parse' in value;\n\n/**\n * Validate and normalize a prop definition.\n * Throws if definition is invalid or incomplete.\n *\n * Accepts any object shaped like `PropDef` — not just `prop.*` helper output — so you can\n * define a custom prop with explicit parser and reflection behavior when the `prop.*` helpers\n * don't cover your type:\n *\n * ```ts\n * const customProp = (): PropDef<MyCustomType> => ({\n * default: new MyCustomType(),\n * parse: (value) => JSON.parse(value || '{}') as MyCustomType,\n * reflect: false,\n * });\n * ```\n */\nexport function normalizePropDefinition<T>(value: unknown, propName: string): PropDef<T> {\n if (!isPropDef(value)) {\n throw new OreApiError(\n `Prop \"${propName}\" must use a prop.* helper (string/number/bool/json/oneOf). Received: ${typeof value}`,\n );\n }\n\n const descriptor = value as PropDef<T>;\n\n if (!descriptor.parse) {\n throw new OreApiError(`Prop \"${propName}\" must have a parse function. Use prop.* helpers.`);\n }\n\n const reflect = descriptor.reflect ?? false;\n\n // Validate: structured defaults with reflect:true are not allowed\n if (reflect && isStructuredValue(descriptor.default)) {\n throw new OreApiError(`Prop \"${propName}\": ${ORE_ERRORS.propInvalidReflect}`);\n }\n\n return {\n ...descriptor,\n reflect,\n };\n}\n\n/**\n * Validate all prop definitions at define-time.\n * Returns validation error messages or empty array if valid.\n */\nexport function validatePropDefs(defs: Record<string, unknown>): string[] {\n const errors: string[] = [];\n\n for (const [key, value] of Object.entries(defs)) {\n try {\n normalizePropDefinition(value, key);\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n }\n }\n\n return errors;\n}\n\ntype PropMeta<T = unknown> = {\n parse: (value: string | null) => T;\n reflect: boolean;\n signal: Signal<T>;\n};\n\nconst propRegistry = new WeakMap<HTMLElement, Map<string, PropMeta<unknown>>>();\n\n/**\n * Look up the registered prop metadata for a given attribute name on an element.\n * Used by the template compiler to resolve prop bindings without exposing the registry.\n */\nexport const getPropMeta = (el: HTMLElement, attrName: string): PropMeta<unknown> | undefined =>\n propRegistry.get(el)?.get(attrName);\n\n/**\n * A framework can hand a prop a raw attribute-style string well after the element\n * has upgraded — e.g. Vue reconciling server-rendered markup against a custom\n * element that auto-upgraded from its SSR attribute before hydration ran, or\n * assigning the pre-upgrade instance property it captured before `registerProp`\n * defined the real accessor. In both cases route the string through the same\n * parser an attribute value would use (so e.g. `size=\"16\"` doesn't end up stored\n * as the string `\"16\"` instead of the number `16`). Already-typed values (objects,\n * functions, booleans, numbers set deliberately as JS properties) pass through untouched.\n */\nconst coerceIncoming = <T>(value: unknown, parse: PropDef<T>['parse']): T =>\n (typeof value === 'string' ? parse(value) : value) as T;\n\n/** @internal Runtime prop registration (called by createProps) */\nconst registerProp = <T>(el: HTMLElement, propName: string, attrName: string, propDef: PropDef<T>): Signal<T> => {\n if (!propRegistry.has(el)) propRegistry.set(el, new Map());\n\n const { default: defaultValue, parse, reflect = false } = propDef;\n const s = signal<T>(defaultValue);\n const hasPreUpgradeProperty = Object.hasOwn(el as unknown as Record<string, unknown>, propName);\n const preUpgradeValue = hasPreUpgradeProperty ? (el as unknown as Record<string, unknown>)[propName] : undefined;\n\n const meta: PropMeta<unknown> = {\n parse,\n reflect,\n signal: s,\n };\n\n if (hasPreUpgradeProperty) {\n delete (el as unknown as Record<string, unknown>)[propName];\n s.value = coerceIncoming(preUpgradeValue, parse);\n } else if (el.hasAttribute(attrName)) {\n s.value = parse(el.getAttribute(attrName)) as T;\n }\n\n const registry = propRegistry.get(el);\n\n invariant(registry, `propRegistry entry missing for <${el.localName}> — registerProp() must create it above`);\n registry.set(attrName, meta);\n\n Object.defineProperty(el, propName, {\n configurable: true,\n enumerable: true,\n get: () => s.value,\n set: (value: T) => {\n s.value = coerceIncoming(value, parse);\n },\n });\n\n if (reflect) {\n watchEffect(() => {\n const v = s.value;\n\n if (v == null) {\n el.removeAttribute(attrName);\n } else if (typeof v === 'boolean') {\n el.toggleAttribute(attrName, v);\n } else {\n setAttr(el, attrName, v);\n }\n });\n }\n\n return s;\n};\n\nexport type InferPropValue<T> = T extends PropDef<infer U> ? U : T;\n\n/**\n * Infer the reactive props object type from a `PropInputDefs` map.\n * Each entry becomes a `Reactive<T>` keyed by the prop name.\n *\n * @example\n * ```ts\n * const propDefs = { count: prop.number(0), label: prop.string('hi') };\n * type Props = InferProps<typeof propDefs>;\n * // => { readonly count: Readable<number>; readonly label: Readable<string> }\n * ```\n */\nexport type InferProps<D extends PropInputDefs> = {\n readonly [K in keyof D]-?: Readable<InferPropValue<D[K]>>;\n};\n\nexport function createProps<D extends PropInputDefs>(el: HTMLElement, defs: D): InferProps<D> {\n const props = {} as Record<string, Signal<unknown>>;\n\n for (const [name, def] of Object.entries(defs)) {\n // defs passed here are already normalized by define(); skip re-normalization.\n const attrName = toKebab(name);\n\n props[name] = registerProp(el, name, attrName, def as PropDef<unknown>);\n }\n\n return props as unknown as InferProps<D>;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/ore",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "Custom element authoring primitives — reactive templates, signals, slots, and automatic lifecycle management",
5
5
  "type": "module",
6
6
  "files": [
@@ -62,10 +62,10 @@
62
62
  "registry": "https://registry.npmjs.org/"
63
63
  },
64
64
  "engines": {
65
- "node": ">=18"
65
+ "node": ">=22"
66
66
  },
67
67
  "dependencies": {
68
- "@vielzeug/ripple": "1.2.2"
68
+ "@vielzeug/ripple": "1.2.3"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.1.0",