praxis-kit 0.1.1 → 1.0.2

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,4 +1,4 @@
1
- import { C as MergeRecords, E as AnyRecord, S as EmptyRecord, T as NoVariants, _ as ExtractPluginProps, a as PolymorphicProps, b as VariantMap, c as RenderCallbackProps, d as SlottableProps, f as mergeRefs, g as AnyClassPluginFactory, h as FactoryOptions, i as PolymorphicComponent, l as UnknownProps, m as AnyFactoryOptions, n as ContractProps, o as PolymorphicWithAsChild, p as defineContractComponent, r as ElementRef, s as PolymorphicWithRender, t as ReactFactoryOptions, u as Slottable, v as PolymorphicGenerics, w as NoPreset, x as ElementType, y as RecipeMap } from "../index-DiPzmjWW.js";
1
+ import { C as RecipeMap, D as MergeRecords, E as EmptyRecord, O as AnyRecord, S as PolymorphicGenerics, T as ElementType, _ as ContractTagOf, a as PolymorphicProps, b as AnyClassPluginFactory, c as RenderCallbackProps, d as SlottableProps, f as mergeRefs, g as ContractPropsOf, h as ContractPresetOf, i as PolymorphicComponent, l as UnknownProps, m as ContractPluginOf, n as ContractProps, o as PolymorphicWithAsChild, p as ContractAllowedOf, r as ElementRef, s as PolymorphicWithRender, t as ReactFactoryOptions, u as Slottable, v as ContractVariantsOf, w as VariantMap, x as ExtractPluginProps, y as FactoryOptions } from "../index-zJDSXTxL.js";
2
2
  //#region ../../adapters/react/src/current/create-contract-component.d.ts
3
3
  /**
4
4
  * Creates a polymorphic React 19 component with praxis-kit contracts applied.
@@ -20,9 +20,18 @@ import { C as MergeRecords, E as AnyRecord, S as EmptyRecord, T as NoVariants, _
20
20
  * `ref` is accepted as a plain prop (React 19) and forwarded to the rendered host element or,
21
21
  * with `asChild`, to the consumer's own element. Pass `subComponents` to attach named
22
22
  * sub-components (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
23
+ *
24
+ * `TDefault`/`Props`/`Variants`/`TPreset`/`TPlugin`/`TAllowed` are each `ContractXOf<C>`-derived
25
+ * *defaults* on this function's own type parameter list — not computed inline in the function
26
+ * body from the still-abstract `C` (an earlier draft did this; confirmed broken, since
27
+ * `ContractXOf<C>` doesn't simplify to a concrete value for an abstract `C` the way it does once
28
+ * `C` is resolved at a real call site — the same lesson `defineContract`'s own signature already
29
+ * encodes). Declaring them as defaults means each resolves once, concretely, at the actual call
30
+ * site, exactly mirroring how these six dimensions were independently inferred pre-refactor —
31
+ * `C` is now the single authoritative input; these are its projection, not a fresh inference.
23
32
  */
24
- export declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
33
+ export declare function createContractComponent<C extends ReactFactoryOptions, TDefault extends ElementType = ContractTagOf<C>, Props extends UnknownProps = ContractPropsOf<C>, Variants extends Readonly<VariantMap> = ContractVariantsOf<C>, TPreset extends RecipeMap<VariantMap> = ContractPresetOf<C>, TPlugin extends AnyClassPluginFactory = ContractPluginOf<C>, TAllowed extends ElementType = ContractAllowedOf<C>, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: C & {
25
34
  readonly subComponents?: TSubComponents;
26
- }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>>, TSubComponents>;
35
+ }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>, C>, TSubComponents>;
27
36
  //#endregion
28
- export { type AnyFactoryOptions, type ContractProps, type ElementRef, type ElementType, type EmptyRecord, type FactoryOptions, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type PolymorphicWithAsChild, type PolymorphicWithRender, type ReactFactoryOptions, type RenderCallbackProps, Slottable, type SlottableProps, defineContractComponent, mergeRefs };
37
+ export { type ContractProps, type ElementRef, type ElementType, type EmptyRecord, type FactoryOptions, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type PolymorphicWithAsChild, type PolymorphicWithRender, type ReactFactoryOptions, type RenderCallbackProps, Slottable, type SlottableProps, mergeRefs };
@@ -1,74 +1,5 @@
1
- import { _ as invariant, a as getElementRef, c as Slottable, d as finalizeComponent, f as SLOT_NAME, g as defineContractComponent, h as isString, i as makeCloneSlotChild, l as applyDisplayName, m as isObject, n as render, o as getPropsRef, p as isFunction, r as applySlot, s as hasWarningGetter, t as buildRuntime$1, u as mergeRefs } from "../build-runtime-CJ_nQEaZ.js";
1
+ import { a as getElementRef, c as Slottable, d as isReactFactoryOptions, f as finalizeComponent, g as invariant, h as isString, i as makeCloneSlotChild, l as applyDisplayName, m as isFunction, n as render, o as getPropsRef, p as SLOT_NAME, r as applySlot, s as hasWarningGetter, t as buildRuntime$1, u as mergeRefs } from "../build-runtime-cNkW-D62.js";
2
2
  import { isValidElement, useCallback, useRef } from "react";
3
- //#region ../../lib/adapter-utils/src/runtime/is-factory-options-like.ts
4
- /**
5
- * Loosely validates one recognized `FactoryOptions` field's runtime shape.
6
- * "Loosely" because the field's declared type is itself parameterized by a
7
- * generic (Props, Variants, TPlugin, ...) that's erased at runtime — these
8
- * checks confirm the field is the right *kind* of value (object, function,
9
- * string), not that it satisfies its exact generic instantiation, which no
10
- * runtime check can ever do.
11
- *
12
- * Covers every field `FactoryOptions` itself declares — identical across
13
- * every adapter, since they all extend the same core type. Adapter-specific
14
- * additions (React's `slotComponent`/`artifact`, every adapter's own
15
- * `filterProps`, etc.) are passed in by the caller as extra entries.
16
- */
17
- const FACTORY_OPTIONS_FIELD_VALIDATORS = {
18
- tag: (v) => v === void 0 || isString(v),
19
- name: (v) => v === void 0 || isString(v),
20
- defaults: (v) => v === void 0 || isObject(v),
21
- normalize: (v) => v === void 0 || isFunction(v),
22
- styling: (v) => v === void 0 || isObject(v),
23
- enforcement: (v) => v === void 0 || isObject(v),
24
- diagnostics: (v) => v === void 0 || isObject(v),
25
- subComponents: (v) => v === void 0 || isObject(v),
26
- onElement: (v) => v === void 0 || isFunction(v)
27
- };
28
- /**
29
- * Type guard narrowing the generic `FactoryOptions` shape down to an
30
- * adapter-specific factory options type `T` — the type each adapter's
31
- * `buildRuntime` is declared against.
32
- *
33
- * Walks every own enumerable property on `options` and checks it against
34
- * `FACTORY_OPTIONS_FIELD_VALIDATORS` plus `extraFieldValidators` (the
35
- * adapter's own additions on top of `FactoryOptions`): an unrecognized key,
36
- * or a recognized key holding a value of the wrong kind, fails the guard.
37
- * This is a genuine structural check, not a relabeled assertion — though it
38
- * necessarily stops at each field's runtime *kind*, since the field's actual
39
- * generic instantiation is erased at runtime and no guard can validate it.
40
- * That part remains the caller's responsibility, same as with any other
41
- * generic function in TypeScript.
42
- */
43
- function isFactoryOptionsLike(options, extraFieldValidators) {
44
- if (!isObject(options)) return false;
45
- const validators = {
46
- ...FACTORY_OPTIONS_FIELD_VALIDATORS,
47
- ...extraFieldValidators
48
- };
49
- for (const [key, value] of Object.entries(options)) {
50
- const validate = validators[key];
51
- if (!validate || !validate(value)) return false;
52
- }
53
- return true;
54
- }
55
- //#endregion
56
- //#region ../../adapters/react/src/shared/to-react-factory-options.ts
57
- /** React-specific additions on top of `FactoryOptions`. */
58
- const REACT_FIELD_VALIDATORS = {
59
- slotComponent: (v) => v === void 0 || isFunction(v) || isObject(v),
60
- filterProps: (v) => v === void 0 || isFunction(v),
61
- artifact: (v) => v === void 0 || isObject(v)
62
- };
63
- /**
64
- * Type guard narrowing the generic `FactoryOptions` shape down to
65
- * `ReactFactoryOptions` — the type `buildRuntime` is declared against. See
66
- * `isFactoryOptionsLike` for what this does and doesn't validate.
67
- */
68
- function isReactFactoryOptions(options) {
69
- return isFactoryOptionsLike(options, REACT_FIELD_VALIDATORS);
70
- }
71
- //#endregion
72
3
  //#region ../../adapters/react/src/shared/is-polymorphic-component.ts
73
4
  /**
74
5
  * Type guard narrowing a generated component's real (framework-specific)
@@ -137,9 +68,26 @@ function buildRuntime(options) {
137
68
  * `ref` is accepted as a plain prop (React 19) and forwarded to the rendered host element or,
138
69
  * with `asChild`, to the consumer's own element. Pass `subComponents` to attach named
139
70
  * sub-components (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
71
+ *
72
+ * `TDefault`/`Props`/`Variants`/`TPreset`/`TPlugin`/`TAllowed` are each `ContractXOf<C>`-derived
73
+ * *defaults* on this function's own type parameter list — not computed inline in the function
74
+ * body from the still-abstract `C` (an earlier draft did this; confirmed broken, since
75
+ * `ContractXOf<C>` doesn't simplify to a concrete value for an abstract `C` the way it does once
76
+ * `C` is resolved at a real call site — the same lesson `defineContract`'s own signature already
77
+ * encodes). Declaring them as defaults means each resolves once, concretely, at the actual call
78
+ * site, exactly mirroring how these six dimensions were independently inferred pre-refactor —
79
+ * `C` is now the single authoritative input; these are its projection, not a fresh inference.
140
80
  */
141
81
  function createContractComponent(options) {
142
82
  invariant(isReactFactoryOptions(options), "options is not a valid ReactFactoryOptions object");
83
+ /**
84
+ * `C` and `TDefault`/`Props`/`Variants`/`TPreset`/`TAllowed` are formally independent type
85
+ * parameters to the checker — `TDefault = ContractTagOf<C>` is a *default value*, used only
86
+ * when nothing else is inferred, not a provable relationship the checker can use inside this
87
+ * body. The `invariant` above is what actually guarantees `options` is `ReactFactoryOptions`
88
+ * shaped; this assertion bridges the gap in the compiler's reasoning the same way the return
89
+ * statement below already does.
90
+ */
143
91
  const bundle = buildRuntime(options);
144
92
  /** Captured once from the factory options so the callback ref below can remain stable. */
145
93
  const { onElement } = options;
@@ -197,9 +145,11 @@ function createContractComponent(options) {
197
145
  * prove that the assembled value satisfies the same conditional expression used by the
198
146
  * declared return type. Once the generics are instantiated at a call site, the conditional
199
147
  * simplifies correctly. The invariant above validates the runtime shape; this assertion
200
- * bridges the gap in the compiler's type reasoning.
148
+ * bridges the gap in the compiler's type reasoning. Also where `__contract`'s `C` is attached —
149
+ * type-only, matching `__generics`: `assembled` never actually gains a `__contract` property at
150
+ * runtime, only in the type this assertion claims.
201
151
  */
202
152
  return assembled;
203
153
  }
204
154
  //#endregion
205
- export { Slottable, createContractComponent, defineContractComponent, mergeRefs };
155
+ export { Slottable, createContractComponent, mergeRefs };
@@ -1,4 +1,4 @@
1
- import { C as MergeRecords, E as AnyRecord, S as EmptyRecord, T as NoVariants, _ as ExtractPluginProps, a as PolymorphicProps, b as VariantMap, c as RenderCallbackProps, d as SlottableProps, f as mergeRefs, g as AnyClassPluginFactory, h as FactoryOptions, i as PolymorphicComponent, l as UnknownProps, m as AnyFactoryOptions, n as ContractProps, o as PolymorphicWithAsChild, p as defineContractComponent, r as ElementRef, s as PolymorphicWithRender, t as ReactFactoryOptions, u as Slottable, v as PolymorphicGenerics, w as NoPreset, x as ElementType, y as RecipeMap } from "../index-DiPzmjWW.js";
1
+ import { C as RecipeMap, D as MergeRecords, E as EmptyRecord, O as AnyRecord, S as PolymorphicGenerics, T as ElementType, _ as ContractTagOf, a as PolymorphicProps, b as AnyClassPluginFactory, c as RenderCallbackProps, d as SlottableProps, f as mergeRefs, g as ContractPropsOf, h as ContractPresetOf, i as PolymorphicComponent, l as UnknownProps, m as ContractPluginOf, n as ContractProps, o as PolymorphicWithAsChild, p as ContractAllowedOf, r as ElementRef, s as PolymorphicWithRender, t as ReactFactoryOptions, u as Slottable, v as ContractVariantsOf, w as VariantMap, x as ExtractPluginProps, y as FactoryOptions } from "../index-zJDSXTxL.js";
2
2
  //#region ../../adapters/react/src/legacy/create-contract-component.d.ts
3
3
  /**
4
4
  * Creates a polymorphic React component with praxis-kit contracts applied, for React 18 and
@@ -21,9 +21,14 @@ import { C as MergeRecords, E as AnyRecord, S as EmptyRecord, T as NoVariants, _
21
21
  * Returns a `forwardRef` component — `ref` is forwarded to the rendered host element the same
22
22
  * way it works in `praxis-kit/react`. Pass `subComponents` to attach named sub-components
23
23
  * (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
24
+ *
25
+ * `TDefault`/`Props`/`Variants`/`TPreset`/`TPlugin`/`TAllowed` are each `ContractXOf<C>`-derived
26
+ * *defaults* on this function's own type parameter list, mirroring `current/create-contract-component.ts`
27
+ * exactly — see that file's own doc comment for why (computed as function type-parameter defaults,
28
+ * not inline body computations, which doesn't resolve for a still-abstract `C`).
24
29
  */
25
- export declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
30
+ export declare function createContractComponent<C extends ReactFactoryOptions, TDefault extends ElementType = ContractTagOf<C>, Props extends UnknownProps = ContractPropsOf<C>, Variants extends Readonly<VariantMap> = ContractVariantsOf<C>, TPreset extends RecipeMap<VariantMap> = ContractPresetOf<C>, TPlugin extends AnyClassPluginFactory = ContractPluginOf<C>, TAllowed extends ElementType = ContractAllowedOf<C>, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: C & {
26
31
  readonly subComponents?: TSubComponents;
27
- }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>>, TSubComponents>;
32
+ }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>, C>, TSubComponents>;
28
33
  //#endregion
29
- export { type AnyFactoryOptions, type ContractProps, type ElementRef, type ElementType, type EmptyRecord, type FactoryOptions, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type PolymorphicWithAsChild, type PolymorphicWithRender, type ReactFactoryOptions, type RenderCallbackProps, Slottable, type SlottableProps, defineContractComponent, mergeRefs };
34
+ export { type ContractProps, type ElementRef, type ElementType, type EmptyRecord, type FactoryOptions, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type PolymorphicWithAsChild, type PolymorphicWithRender, type ReactFactoryOptions, type RenderCallbackProps, Slottable, type SlottableProps, mergeRefs };
@@ -1,4 +1,4 @@
1
- import { a as getElementRef, c as Slottable, d as finalizeComponent, f as SLOT_NAME, g as defineContractComponent, i as makeCloneSlotChild, l as applyDisplayName, n as render, o as getPropsRef, r as applySlot, s as hasWarningGetter, t as buildRuntime$1, u as mergeRefs } from "../build-runtime-CJ_nQEaZ.js";
1
+ import { a as getElementRef, c as Slottable, d as isReactFactoryOptions, f as finalizeComponent, g as invariant, i as makeCloneSlotChild, l as applyDisplayName, n as render, o as getPropsRef, p as SLOT_NAME, r as applySlot, s as hasWarningGetter, t as buildRuntime$1, u as mergeRefs } from "../build-runtime-cNkW-D62.js";
2
2
  import { Children, forwardRef, isValidElement, useCallback, useRef } from "react";
3
3
  //#region ../../adapters/react/src/legacy/slot/composeRefs.ts
4
4
  function getChildRef(element) {
@@ -47,8 +47,20 @@ function buildRuntime(options) {
47
47
  * Returns a `forwardRef` component — `ref` is forwarded to the rendered host element the same
48
48
  * way it works in `praxis-kit/react`. Pass `subComponents` to attach named sub-components
49
49
  * (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
50
+ *
51
+ * `TDefault`/`Props`/`Variants`/`TPreset`/`TPlugin`/`TAllowed` are each `ContractXOf<C>`-derived
52
+ * *defaults* on this function's own type parameter list, mirroring `current/create-contract-component.ts`
53
+ * exactly — see that file's own doc comment for why (computed as function type-parameter defaults,
54
+ * not inline body computations, which doesn't resolve for a still-abstract `C`).
50
55
  */
51
56
  function createContractComponent(options) {
57
+ invariant(isReactFactoryOptions(options), "options is not a valid ReactFactoryOptions object");
58
+ /**
59
+ * `C` and `TDefault`/`Props`/`Variants`/`TPreset`/`TAllowed` are formally independent type
60
+ * parameters to the checker — see `current/create-contract-component.ts`'s identical comment
61
+ * for the full explanation. The `invariant` above is what actually guarantees `options` is
62
+ * `ReactFactoryOptions` shaped; this assertion bridges the gap in the compiler's reasoning.
63
+ */
52
64
  const bundle = buildRuntime(options);
53
65
  const { onElement } = options;
54
66
  const Component = forwardRef(function Component(props, ref) {
@@ -77,4 +89,4 @@ function createContractComponent(options) {
77
89
  return finalizeComponent(Component, bundle.runtime.options.defaultTag, options.subComponents);
78
90
  }
79
91
  //#endregion
80
- export { Slottable, createContractComponent, defineContractComponent, mergeRefs };
92
+ export { Slottable, createContractComponent, mergeRefs };
@@ -25,20 +25,6 @@ type EmptyRecord = Record<never, never>;
25
25
  * `{ Header, Content, Footer }`.
26
26
  */
27
27
  type SubComponentMap = Readonly<AnyRecord>;
28
- /**
29
- * Default `Variants` type for components that declare no variants.
30
- *
31
- * Structurally identical to `Readonly<EmptyRecord>`, but named separately so
32
- * editor hovers remain self-descriptive.
33
- */
34
- type NoVariants = Readonly<EmptyRecord>;
35
- /**
36
- * Default `TPreset` type for components that declare no named presets.
37
- *
38
- * Structurally identical to `Readonly<EmptyRecord>`, but named separately so
39
- * editor hovers remain self-descriptive.
40
- */
41
- type NoPreset = Readonly<EmptyRecord>;
42
28
  /**
43
29
  * Fallback for `ExtractPluginProps<TPlugin>` when a plugin contributes no
44
30
  * props, including the no-plugin case.
@@ -537,16 +523,6 @@ type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPre
537
523
  type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
538
524
  normalize(props: Readonly<Props & IntrinsicProps>): Props & IntrinsicProps;
539
525
  }['normalize'];
540
- /**
541
- * The type-erased shape of {@link FactoryOptions} — every generic parameter widened to its bound.
542
- *
543
- * Use it for a value that must hold *any* factory config (a registry, a generic wrapper). It
544
- * cannot check `styling.compounds` conditions against the real variant keys/values, because it
545
- * has forgotten what they are — for that, annotate against `FactoryOptions<...>` with the concrete
546
- * generics (or `satisfies FactoryOptions<'button', Props, typeof variants>`), which keeps an
547
- * invalid compound condition a type error rather than a silent no-op.
548
- */
549
- type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, RecipeMap<VariantMap>, AnyClassPluginFactory>;
550
526
  /**
551
527
  * The framework-neutral component-authoring config passed to `createContractComponent` in every
552
528
  * adapter: default tag + name, own-prop defaults, a `normalize` transform, `styling` (variants,
@@ -556,7 +532,9 @@ type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, Reci
556
532
  * `satisfies FactoryOptions<TDefault, Props, typeof variants, ...>` on a config object narrows
557
533
  * `styling.compounds` conditions to the real per-variant-key shape — including resolving a
558
534
  * boolean-shaped axis (`{ true, false }`) to a real `boolean` — so a condition naming a variant or
559
- * value that does not exist is a compile error. `AnyFactoryOptions` cannot do this.
535
+ * value that does not exist is a compile error. Leaving `V` at the bare `VariantMap` instead
536
+ * (whether via `FactoryOptions`'s own default or an explicit erased instantiation) forgets that
537
+ * shape entirely, so the same invalid condition becomes a silent no-op instead.
560
538
  */
561
539
  type FactoryOptions<TDefault extends ElementType = ElementType, Props extends AnyRecord = EmptyRecord, V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = {
562
540
  /** The intrinsic tag the component renders by default. Overridable per instance via `as`. */
@@ -565,6 +543,33 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
565
543
  readonly name?: string;
566
544
  /** Values used for the component's own (non-variant) props when the consumer omits them. */
567
545
  readonly defaults?: Partial<NoInfer<Props>>;
546
+ /**
547
+ * Optional, type-only declaration of this component's complete own-prop shape — present purely
548
+ * for type recovery, never read at runtime (see `declareProps` in `@praxis-kit/adapter-utils`).
549
+ *
550
+ * `defaults` alone can only prove a prop *has a default*, not that it's the complete prop model
551
+ * a component accepts — a `defaults: { size: 'md' }` component may still take `onClick`,
552
+ * `disabled`, and other props with no default at all, none of which a `defaults`-only recovery
553
+ * can see (see `ContractPropsFrom`'s own doc comment for the general shape of this problem).
554
+ * `props` closes that gap: when present, `ContractPropsOf<C>` / `ContractProps<typeof Component>`
555
+ * recover this declared type directly and exactly, in place of the necessarily-partial,
556
+ * literal-widened recovery `defaults` alone allows.
557
+ *
558
+ * Typed as `object | undefined`, not `NoInfer<Props> | undefined` like `defaults`/`onElement` —
559
+ * deliberately decoupled from this interface's own `Props` generic, unlike those two fields.
560
+ * `defaults`/`onElement` are tied to `Props` because real runtime code reads them against a
561
+ * concretely-resolved `Props` for a real call; `props` is never read at runtime at all (see
562
+ * above), so it has no such need, and tying it to `Props extends AnyRecord` would force every
563
+ * hand-declared prop `interface`/`type` an author passes through `declareProps<Props>()` to
564
+ * structurally satisfy `Record<string, unknown>` — a real TypeScript limitation (a named type
565
+ * without an index signature never satisfies that, even via plain assignment, only a fresh
566
+ * object literal does) that would make this field far more awkward to use for its one real job:
567
+ * carrying an author's own already-precise prop type through untouched. `ContractPropsFrom`
568
+ * (`contract-model.ts`) recovers the real value here structurally, straight off `O`'s own
569
+ * literal type — independent of this field's declared type, same as every other `Contract*From`
570
+ * derivation in that file.
571
+ */
572
+ readonly props?: object | undefined;
568
573
  /**
569
574
  * A pure `(props) => props` transform run on every render, after `enforcement.props`'s
570
575
  * normalizers see the same input. Use this for component-specific prop shaping — anything
@@ -619,18 +624,172 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
619
624
  *
620
625
  * Return a cleanup function to run when the instance unmounts.
621
626
  */
622
- readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
627
+ readonly onElement?: {
628
+ onElement(element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>): void | (() => void);
629
+ }['onElement'];
623
630
  };
624
631
  //#endregion
625
- //#region ../../lib/adapter-utils/src/runtime/define-component.d.ts
626
- export declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
632
+ //#region ../../lib/primitive/src/types/factory/contract-model.d.ts
633
+ /**
634
+ * The pipeline this file implements:
635
+ *
636
+ * ```text
637
+ * O (a raw contract literal)
638
+ * → Contract*From<O> one derivation per dimension, straight off O's own shape
639
+ * → ContractDimensions<O> the six derivations, assembled
640
+ * → ContractModel<...> the same six values, as a required-field carrier
641
+ * → ContractModelOf<C> resolves an already-`defineContract`-ed C's real model,
642
+ * or reconstructs one from a raw C via ContractModelFrom
643
+ * ```
644
+ *
645
+ * See `DECISIONS.md`'s `defineContract` entry for the TypeScript limitations that shaped the
646
+ * derivations below.
647
+ */
648
+ /**
649
+ * Canonical type-level representation of a contract's dimensions — the contract-level counterpart
650
+ * to `PolymorphicGenerics`. Every field is required, so a field's presence is never ambiguous the
651
+ * way it is on an optional `FactoryOptions` field.
652
+ */
653
+ interface ContractModel<TDefault extends ElementType = ElementType, Props extends AnyRecord = AnyRecord, V extends Readonly<VariantMap> = Readonly<VariantMap>, TPreset extends RecipeMap<VariantMap> = RecipeMap<VariantMap>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> {
654
+ readonly tag: TDefault;
655
+ readonly props: Props;
656
+ readonly variants: V;
657
+ readonly preset: TPreset;
658
+ readonly plugin: TPlugin;
659
+ readonly allowed: TAllowed;
660
+ }
661
+ /**
662
+ * The phantom-marker shape read back via `T extends HasContractModel<infer M> ? M : ...` — the
663
+ * contract-level counterpart to `lib/contract-props`'s `HasGenerics<G>`. Type-only: never assigned
664
+ * at runtime.
665
+ *
666
+ * `__model` is **required**, not optional like `HasGenerics<G>`'s `__generics?` — an optional field
667
+ * would make `C extends HasContractModel<infer M>` trivially succeed for any `C`, defeating the
668
+ * one thing this marker exists to answer: did `C` really go through `defineContract`?
669
+ */
670
+ interface HasContractModel<M extends ContractModel = ContractModel> {
671
+ readonly __model: M;
672
+ }
673
+ type ContractTagFrom<O> = O extends {
674
+ tag: infer TDefault extends ElementType;
675
+ } ? TDefault : ElementType;
676
+ /** `'img'` → `string`, `1` → `number`, `true` → `boolean` — undoes `defineContract`'s `const O`
677
+ * literal narrowing so a `defaults` value doesn't become the only value a caller may pass. */
678
+ type WidenLiteral<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
679
+ type WidenShallow<T> = { [K in keyof T]: WidenLiteral<T[K]>; };
680
+ /** What the author explicitly declared, via `props: declareProps<Props>()`
681
+ * (`@praxis-kit/adapter-utils`) — used exactly as declared, no `Partial`/widening/`data-*`
682
+ * stripping (see `DefaultPropsFrom` for why those exist there but not here). */
683
+ type DeclaredPropsFrom<O> = O extends {
684
+ props: infer Props extends object | undefined;
685
+ } ? [NonNullable<Props>] extends [never] ? never : NonNullable<Props> & AnyRecord : never;
686
+ /** What can be inferred from `defaults` — partial at best, since a default only proves a prop
687
+ * *has* one, not that it's the complete prop set. `Partial`: a defaulted prop is optional to the
688
+ * caller by definition. `data-*` keys are dropped — every adapter has its own passthrough for
689
+ * those already (finding #43). */
690
+ type DefaultPropsFrom<O> = O extends {
691
+ defaults: infer Props extends AnyRecord;
692
+ } ? Partial<WidenShallow<Omit<Props, Extract<keyof Props, `data-${string}`>>>> : never;
693
+ /** Declared beats defaulted beats nothing. `[X] extends [never]`, not bare `X extends never` —
694
+ * tuple-wrapped so the check doesn't distribute if `X` is ever a union containing `never`. */
695
+ type ContractPropsFrom<O> = [DeclaredPropsFrom<O>] extends [never] ? [DefaultPropsFrom<O>] extends [never] ? EmptyRecord : DefaultPropsFrom<O> : DeclaredPropsFrom<O>;
696
+ type ContractVariantsFrom<O> = O extends {
697
+ styling: {
698
+ variants: infer V extends Readonly<VariantMap>;
699
+ };
700
+ } ? V : Readonly<EmptyRecord>;
701
+ type ContractPresetFrom<O> = O extends {
702
+ styling: {
703
+ presets: infer TPreset extends RecipeMap<VariantMap>;
704
+ };
705
+ } ? TPreset : Readonly<EmptyRecord>;
706
+ type ContractPluginFrom<O> = O extends {
707
+ styling: {
708
+ plugin: infer TPlugin extends AnyClassPluginFactory;
709
+ };
710
+ } ? TPlugin : AnyClassPluginFactory;
711
+ type ContractAllowedFrom<O> = O extends {
712
+ enforcement: {
713
+ allowedAs: readonly (infer TAllowed extends ElementType)[];
714
+ };
715
+ } ? TAllowed : ElementType;
716
+ /** The six derivations above, assembled — the direct input to `ContractModel`. */
717
+ type ContractDimensions<O> = {
718
+ readonly tag: ContractTagFrom<O>;
719
+ readonly props: ContractPropsFrom<O>;
720
+ readonly variants: ContractVariantsFrom<O>;
721
+ readonly preset: ContractPresetFrom<O>;
722
+ readonly plugin: ContractPluginFrom<O>;
723
+ readonly allowed: ContractAllowedFrom<O>;
724
+ };
725
+ /**
726
+ * Builds a `ContractModel` from a raw literal `O` — the fallback path `ContractModelOf<C>` uses
727
+ * when `C` never went through `defineContract` (no `__model` marker to read directly), and what
728
+ * `defineContract` itself attaches as that marker for a `C` that did.
729
+ */
730
+ type ContractModelFrom<O> = ContractDimensions<O> extends (infer D extends ContractDimensions<O>) ? ContractModel<D['tag'], D['props'], D['variants'], D['preset'], D['plugin'], D['allowed']> : never;
731
+ /**
732
+ * Resolves "does `C` carry a real `ContractModel` already, or do we need to build one from its raw
733
+ * shape" — every `Contract*Of` accessor (`contract-of.ts`) is a one-line projection off this.
734
+ */
735
+ type ContractModelOf<C> = C extends HasContractModel<infer M> ? M : ContractModelFrom<C>;
736
+ //#endregion
737
+ //#region ../../lib/primitive/src/types/factory/contract-of.d.ts
738
+ /**
739
+ * `FactoryOptions`-level accessor family, mirroring `polymorphic-generics.ts`'s `*Of<T>`
740
+ * convention (`DefaultOf<G>`, `PropsOf<G>`, etc.) but applied one layer up, to a contract itself
741
+ * rather than to the `PolymorphicGenerics` an adapter derives from it.
742
+ *
743
+ * Named with a `Contract` prefix specifically to avoid colliding with `polymorphic-generics.ts`'s
744
+ * own `PropsOf`/`VariantsOf`/`RecipeOf`/`AllowedOf`/`DefaultOf` — both families are re-exported
745
+ * from `@praxis-kit/core`, so a name clash would be a real conflict, not a style nit.
746
+ *
747
+ * Each accessor is a trivial projection off `ContractModelOf<C>` (`contract-model.ts`) — the one
748
+ * place that resolves "does `C` carry a real `ContractModel` (via `defineContract`) or does one
749
+ * need reconstructing from `C`'s raw shape," so every accessor shares one implementation of that
750
+ * resolution rather than repeating it. See `contract-model.ts`'s own doc comments for the
751
+ * `ContractXFrom<O>` derivation each of these ultimately reads through, and `DECISIONS.md`'s
752
+ * `defineContract` entry for the design history behind the required-pattern-match technique.
753
+ */
754
+ type ContractTagOf<C extends FactoryOptions> = ContractModelOf<C>['tag'];
755
+ /** See this file's own doc comment. Best-effort when `C` has no `ContractModel` marker — see
756
+ * `ContractPropsFrom`'s own doc comment for why. */
757
+ type ContractPropsOf<C extends FactoryOptions> = ContractModelOf<C>['props'];
758
+ /** See this file's own doc comment. */
759
+ type ContractVariantsOf<C extends FactoryOptions> = ContractModelOf<C>['variants'];
760
+ /** See this file's own doc comment. */
761
+ type ContractPresetOf<C extends FactoryOptions> = ContractModelOf<C>['preset'];
762
+ /** See this file's own doc comment. */
763
+ type ContractPluginOf<C extends FactoryOptions> = ContractModelOf<C>['plugin'];
764
+ //#endregion
765
+ //#region ../../lib/primitive/src/types/factory/contract-generics.d.ts
766
+ /**
767
+ * The canonical projection from a defined contract `C` to the `PolymorphicGenerics` shape every
768
+ * adapter's prop types are built from — the one place "G" gets computed, so it can no longer
769
+ * silently drift per adapter the way today's hand-assembled `PolymorphicGenerics<...>` instantiation
770
+ * at each `createContractComponent` call site can (see `ContractGenericsWithAllowedOf` below for
771
+ * the one confirmed instance of that drift).
772
+ *
773
+ * Folds the class-resolution plugin's own contributed props (`ExtractPluginProps<TPlugin>`) into
774
+ * `props` here, once, rather than leaving each adapter's `ContractProps` to re-derive that merge
775
+ * via its own distributive conditional type on every read — the root cause of the ~22-member
776
+ * layout-union bug PR #95 patched per-adapter (see `DECISIONS.md`'s `defineContract` entry).
777
+ */
778
+ type ContractGenericsOf<C extends FactoryOptions> = PolymorphicGenerics<ContractTagOf<C>, MergeRecords<ContractPropsOf<C>, ExtractPluginProps<ContractPluginOf<C>>>, ContractVariantsOf<C>, ContractPresetOf<C>>;
627
779
  //#endregion
628
780
  //#region ../../adapters/solid/src/types/primitives.d.ts
629
781
  type UnknownProps = AnyRecord;
630
782
  type SolidElement = JSX.Element;
631
783
  //#endregion
632
784
  //#region ../../adapters/solid/src/solid-options.d.ts
633
- type SolidFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
785
+ /**
786
+ * Every generic parameter has a default (widened to that parameter's own *bound*, matching
787
+ * a wide-bound, type-erased philosophy, not `FactoryOptions`'s own narrower `EmptyRecord`-style
788
+ * defaults) so `SolidFactoryOptions` can be used bare, as `createContractComponent`'s single
789
+ * `C extends SolidFactoryOptions` constraint — see `ReactFactoryOptions`'s identical fix for the
790
+ * same reason.
791
+ */
792
+ type SolidFactoryOptions<TDefault extends ElementType = ElementType, Props extends UnknownProps = UnknownProps, Variants extends Readonly<VariantMap> = Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = RecipeMap<Variants>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
634
793
  /**
635
794
  * Return true for any prop key that should be consumed but not forwarded to the DOM.
636
795
  * Receives `runtime.options.variantKeys` as a convenience if needed.
@@ -638,6 +797,34 @@ type SolidFactoryOptions<TDefault extends ElementType, Props extends UnknownProp
638
797
  filterProps?: (key: string, variantKeys: ReadonlySet<string>) => boolean;
639
798
  };
640
799
  //#endregion
800
+ //#region ../../lib/contract-props/src/has-contract.d.ts
801
+ /**
802
+ * The phantom-marker shape read back via `T extends HasContract<infer C> ? C : never` — the
803
+ * contract-retention counterpart to `HasGenerics<G>` in this same package. Where `HasGenerics<G>`
804
+ * lets a built component recover the `PolymorphicGenerics` an adapter derived for it,
805
+ * `HasContract<C>` lets it recover the *complete, authoritative contract* (`C`, the argument
806
+ * `createContractComponent<C extends XFactoryOptions>` was actually called with) it was derived
807
+ * from — the two are deliberately separate markers, not one broadened to do both jobs: `G` is
808
+ * "what the adapter needs to implement the component," `C` is "what the component was configured
809
+ * with" (see `DECISIONS.md`'s `defineContract` entry). A component carries both.
810
+ *
811
+ * Type-only: never assigned at runtime, same rationale as `HasGenerics<G>` (see that type's own
812
+ * doc comment) — a `createContractComponent` return value gets this shape via a type assertion,
813
+ * not a real property write.
814
+ *
815
+ * Unconstrained (no `C extends FactoryOptions` bound), matching `HasGenerics<G>`'s own choice —
816
+ * this package has no dependency on `@praxis-kit/core`/`@praxis-kit/primitive`, and adding one
817
+ * just to write a bound here isn't worth it: the accessor types that actually consume `C`
818
+ * (`ContractTagOf<C>`, etc., in `@praxis-kit/primitive`) already declare their own constraint.
819
+ *
820
+ * Do **not** "harden" this with a `unique symbol` or other nominal brand, for the same reason
821
+ * `HasGenerics<G>` doesn't: a real component's callable type needs to structurally satisfy this
822
+ * shape by declaring the same inline optional field, not by importing a nominal brand.
823
+ */
824
+ interface HasContract<C> {
825
+ readonly __contract?: C;
826
+ }
827
+ //#endregion
641
828
  //#region ../../adapters/solid/src/types/polymorphic-props.d.ts
642
829
  type ElementRef<T extends ElementType> = T extends IntrinsicTag ? HTMLElementTagNameMap[T] : unknown;
643
830
  type IntrinsicJSXProps<T extends ElementType> = T extends IntrinsicTag ? JSX.IntrinsicElements[T] : UnknownProps;
@@ -677,7 +864,7 @@ type PolymorphicProps<G extends PolymorphicGenerics, TAs extends ElementType = D
677
864
  asChild?: false;
678
865
  children?: unknown;
679
866
  }) | AsChildProps<G>>;
680
- type PolymorphicComponent<G extends PolymorphicGenerics> = {
867
+ type PolymorphicComponent<G extends PolymorphicGenerics, C extends FactoryOptions = FactoryOptions> = {
681
868
  <TAs extends ElementType = DefaultOf<G>>(props: PolymorphicProps<G, TAs>): JSX.Element;
682
869
  /**
683
870
  * Non-generic fallback overload used for type extraction.
@@ -688,17 +875,37 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
688
875
  * inference for tools such as Storybook and `ComponentProps`.
689
876
  */
690
877
  (props: PolymorphicProps<G, DefaultOf<G>>): JSX.Element;
878
+ /**
879
+ * Type-only; never assigned at runtime — same rationale as React's/Preact's `__contract` (see
880
+ * `HasContract<C>`, `@praxis-kit/contract-props`). Carries the *complete* contract this
881
+ * component was built from (`C`, the argument `createContractComponent<C extends
882
+ * SolidFactoryOptions>` was actually called with). Unlike React's/Preact's `__generics` (which
883
+ * this adapter never needed — `PolymorphicProps<G, TAs>` already folds both render modes into
884
+ * one type, with no overload-resolution ceiling forcing a marker for `G` the way those two
885
+ * adapters need), `C` still needs one: nothing else on this type exposes the *complete*
886
+ * contract, only its `PolymorphicGenerics` projection. Defaults to the widest `FactoryOptions`
887
+ * so every existing one-argument `PolymorphicComponent<G>` reference keeps resolving exactly as
888
+ * before.
889
+ */
890
+ readonly __contract?: C;
691
891
  displayName?: string;
692
892
  };
693
893
  /**
694
- * A component's full prop contract — naming symmetry with React's/Preact's `ContractProps<T,
695
- * Mode>`, not a fix for a gap: Solid has no version of the
696
- * overload-resolution ceiling those two adapters need a marker to work around. `PolymorphicProps<G,
697
- * TAs>` already folds both render modes into one unioned type (rather than two separate types the
698
- * way React/Preact split them), and `PolymorphicComponent<G>`'s fallback overload already returns
699
- * that whole union — so this alias is just `PolymorphicProps<G>` under a familiar name.
894
+ * A component's full prop contract — `PolymorphicProps<G, TAs>` already folds both render modes
895
+ * into one unioned type (rather than two separate types the way React/Preact split them), and
896
+ * `PolymorphicComponent<G>`'s fallback overload already returns that whole union, so this is that
897
+ * same shape, projected from the component's retained `__contract` rather than a bare `G` the
898
+ * caller must already have in hand — `ContractProps<typeof Box>`, matching every other adapter,
899
+ * not `ContractProps<SomeG>`.
900
+ *
901
+ * An earlier version of this type took `G` directly (`ContractProps<G extends
902
+ * PolymorphicGenerics>`) — a genuinely different public shape, not a bug fix here: Solid's
903
+ * `PolymorphicProps<G, TAs>` never had React's/Preact's overload-resolution ceiling, so there was
904
+ * no *forced* reason for a marker. Unified to `ContractProps<typeof Component>` now that
905
+ * `__contract` exists anyway (Phase 3 retention, needed regardless of this decision), mirroring
906
+ * Vue's identical Phase 4 decision for the identical reason.
700
907
  */
701
- type ContractProps<G extends PolymorphicGenerics> = PolymorphicProps<G>;
908
+ type ContractProps<T extends HasContract<FactoryOptions>> = T extends HasContract<infer C extends FactoryOptions> ? ContractGenericsOf<C> extends (infer G extends PolymorphicGenerics) ? PolymorphicProps<G> : never : never;
702
909
  //#endregion
703
910
  //#region ../../adapters/solid/src/create-contract-component.d.ts
704
911
  /**
@@ -720,9 +927,15 @@ type ContractProps<G extends PolymorphicGenerics> = PolymorphicProps<G>;
720
927
  *
721
928
  * `ref` is forwarded as an ordinary Solid ref callback. Pass `subComponents` to attach named
722
929
  * sub-components (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
930
+ *
931
+ * `TDefault`/`Props`/`Variants`/`TPreset`/`TPlugin` are each `ContractXOf<C>`-derived *defaults*
932
+ * on this function's own type parameter list, mirroring `@praxis-kit/react`'s identical fix — see
933
+ * that adapter's own doc comment for why (computed as function type-parameter defaults, not inline
934
+ * body computations, which doesn't resolve for a still-abstract `C`). No `TAllowed` here, matching
935
+ * this adapter's pre-refactor behavior — Solid never threaded it as its own generic.
723
936
  */
724
- export declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: SolidFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
937
+ export declare function createContractComponent<C extends SolidFactoryOptions, TDefault extends ElementType = ContractTagOf<C>, Props extends UnknownProps = ContractPropsOf<C>, Variants extends Readonly<VariantMap> = ContractVariantsOf<C>, TPreset extends RecipeMap<VariantMap> = ContractPresetOf<C>, TPlugin extends AnyClassPluginFactory = ContractPluginOf<C>, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: C & {
725
938
  readonly subComponents?: TSubComponents;
726
- }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>>, TSubComponents>;
939
+ }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>, C>, TSubComponents>;
727
940
  //#endregion
728
- export type { AnyFactoryOptions, ContractProps, ElementRef, ElementType, EmptyRecord, FactoryOptions, PolymorphicComponent, PolymorphicGenerics, PolymorphicProps, ResolvedSlotProps, SlotRenderFn, SolidFactoryOptions };
941
+ export type { ContractProps, ElementRef, ElementType, EmptyRecord, FactoryOptions, PolymorphicComponent, PolymorphicGenerics, PolymorphicProps, ResolvedSlotProps, SlotRenderFn, SolidFactoryOptions };