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.
@@ -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,11 +624,158 @@ 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/preact/src/slot/Slottable.d.ts
629
781
  type SlottableProps = {
@@ -637,7 +789,14 @@ type SlotComponent = ComponentType<UnknownProps>;
637
789
  type AnyVNode = VNode<any>;
638
790
  //#endregion
639
791
  //#region ../../adapters/preact/src/preact-options.d.ts
640
- type PreactFactoryOptions<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> & {
792
+ /**
793
+ * Every generic parameter has a default (widened to that parameter's own *bound*, matching
794
+ * a wide-bound, type-erased philosophy, not `FactoryOptions`'s own narrower `EmptyRecord`-style
795
+ * defaults) so `PreactFactoryOptions` can be used bare, as `createContractComponent`'s single
796
+ * `C extends PreactFactoryOptions` constraint — see `ReactFactoryOptions`'s identical fix for the
797
+ * same reason.
798
+ */
799
+ type PreactFactoryOptions<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> & {
641
800
  /** Component used to render the asChild slot. Defaults to the built-in Slot. */
642
801
  slotComponent?: SlotComponent;
643
802
  /**
@@ -647,6 +806,34 @@ type PreactFactoryOptions<TDefault extends ElementType, Props extends UnknownPro
647
806
  filterProps?: (key: string, variantKeys: ReadonlySet<string>) => boolean;
648
807
  };
649
808
  //#endregion
809
+ //#region ../../lib/tailwind/src/layout-keys.d.ts
810
+ /**
811
+ * Canonical list of reserved layout prop names.
812
+ *
813
+ * This array is the single source of truth for every supported CSS `display`
814
+ * value exposed as a boolean prop. The `LayoutKey` type is derived directly
815
+ * from this list.
816
+ *
817
+ * To add a new display mode:
818
+ * 1. Add the display value here.
819
+ * 2. Register its layout family in `LAYOUT_FAMILY_MAP`.
820
+ *
821
+ * Prop names intentionally match the corresponding Tailwind/CSS display
822
+ * utilities, so no additional prop-to-class mapping is required.
823
+ */
824
+ declare const layoutKeys: readonly ["flex", "inline-flex", "grid", "inline-grid", "block", "inline-block", "inline", "hidden", "contents", "flow-root", "list-item", "table", "inline-table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", "table-row-group", "table-row"];
825
+ /**
826
+ * The concrete union of layout prop names — the pre-resolved form of
827
+ * `LayoutKey<typeof layoutKeys>`, colocated with the tuple it derives from.
828
+ *
829
+ * For code that needs to *name* the layout keys in a type without also importing the runtime
830
+ * tuple. A framework adapter uses it to collapse the mutually-exclusive `LayoutProps` union back
831
+ * out of an *extracted* prop type (`ContractProps<typeof Component>`): there a caller wants "the
832
+ * layout props exist and are optional `true`s", not the ~22-member discriminated union that the
833
+ * component's own call signature carries — where "only one may be `true`" is a useful error.
834
+ */
835
+ type LayoutKeyName = (typeof layoutKeys)[number];
836
+ //#endregion
650
837
  //#region ../../lib/contract-props/src/mode.d.ts
651
838
  /**
652
839
  * The three render modes a praxis-kit component's props can be typed for, shared across every
@@ -666,26 +853,32 @@ type PreactFactoryOptions<TDefault extends ElementType, Props extends UnknownPro
666
853
  */
667
854
  type Mode = 'normal' | 'asChild' | 'render';
668
855
  //#endregion
669
- //#region ../../lib/contract-props/src/has-generics.d.ts
856
+ //#region ../../lib/contract-props/src/has-contract.d.ts
670
857
  /**
671
- * The phantom-marker shape read back via `T extends HasGenerics<infer G> ? G : never`. See the
672
- * README for why this exists.
858
+ * The phantom-marker shape read back via `T extends HasContract<infer C> ? C : never` — the
859
+ * contract-retention counterpart to `HasGenerics<G>` in this same package. Where `HasGenerics<G>`
860
+ * lets a built component recover the `PolymorphicGenerics` an adapter derived for it,
861
+ * `HasContract<C>` lets it recover the *complete, authoritative contract* (`C`, the argument
862
+ * `createContractComponent<C extends XFactoryOptions>` was actually called with) it was derived
863
+ * from — the two are deliberately separate markers, not one broadened to do both jobs: `G` is
864
+ * "what the adapter needs to implement the component," `C` is "what the component was configured
865
+ * with" (see `DECISIONS.md`'s `defineContract` entry). A component carries both.
866
+ *
867
+ * Type-only: never assigned at runtime, same rationale as `HasGenerics<G>` (see that type's own
868
+ * doc comment) — a `createContractComponent` return value gets this shape via a type assertion,
869
+ * not a real property write.
673
870
  *
674
- * Type-only: never assigned at runtime. In React's `PolymorphicComponent<G>`, declaring `readonly
675
- * __generics?: G` inline (structurally matching this shape) rather than writing `HasGenerics<G> &
676
- * { ...call signatures... }` was necessary to keep `PolymorphicComponent<any>`-typed test helpers
677
- * assignable — confirmed directly against that adapter's own real component type (see
678
- * `adapters/react/src/shared/types/polymorphic-props.test.ts`), not reproducible in an isolated
679
- * minimal mock (see this file's own `has-generics.test.ts`), so treat "inline, not intersected"
680
- * as an adapter-level implementation detail this package's marker shape must stay compatible
681
- * with, not a property `HasGenerics<G>` itself enforces.
871
+ * Unconstrained (no `C extends FactoryOptions` bound), matching `HasGenerics<G>`'s own choice —
872
+ * this package has no dependency on `@praxis-kit/core`/`@praxis-kit/primitive`, and adding one
873
+ * just to write a bound here isn't worth it: the accessor types that actually consume `C`
874
+ * (`ContractTagOf<C>`, etc., in `@praxis-kit/primitive`) already declare their own constraint.
682
875
  *
683
- * Do **not** "harden" this with a `unique symbol` or other nominal brand: the whole point is that
684
- * an adapter's real callable component type structurally satisfies this shape by declaring the
685
- * same optional string-keyed field inline. Nominal purity here would break that compatibility.
876
+ * Do **not** "harden" this with a `unique symbol` or other nominal brand, for the same reason
877
+ * `HasGenerics<G>` doesn't: a real component's callable type needs to structurally satisfy this
878
+ * shape by declaring the same inline optional field, not by importing a nominal brand.
686
879
  */
687
- interface HasGenerics<G> {
688
- readonly __generics?: G;
880
+ interface HasContract<C> {
881
+ readonly __contract?: C;
689
882
  }
690
883
  //#endregion
691
884
  //#region ../../lib/contract-props/src/pick-mode.d.ts
@@ -723,7 +916,36 @@ type PolymorphicWithAsChild<G extends PolymorphicGenerics, TAs extends ElementTy
723
916
  as?: never;
724
917
  children: AnyVNode | AnyVNode[];
725
918
  }>;
726
- type PolymorphicComponent<G extends PolymorphicGenerics> = {
919
+ /**
920
+ * The layout keys a prop-union actually carries as the tailwind plugin's mutually-exclusive
921
+ * shape: distributed over every member of `P`, a key counts when its value there is exactly
922
+ * `true` or `never` — the two shapes `ExclusiveTrueProp` produces. `[M[K]] extends [true]`
923
+ * matches both and rejects `boolean`, so a component's own same-named prop and the intrinsic
924
+ * `hidden` attribute are not counted, and a plugin-less component yields `never`.
925
+ */
926
+ type LayoutPluginKeys<P> = { [K in LayoutKeyName]-?: P extends (infer M) ? K extends keyof M ? [M[K]] extends [true] ? K : never : never : never; }[LayoutKeyName];
927
+ /**
928
+ * Collapses the mutually-exclusive `LayoutProps` union down to one flat shape — every layout key
929
+ * an optional `true` — for the **type-extraction** path (`ContractProps<T>`) only. Mirrors the
930
+ * React adapter's `FlattenLayout` (`adapters/react/src/shared/types/polymorphic-props.ts`), where
931
+ * the full rationale lives.
932
+ *
933
+ * `styling.plugin: createTailwindPipeline` contributes `ExclusiveTrueProp<LayoutKey>` — a ~22-way
934
+ * union (`{ flex: true } | { grid: true } | …`) that distributes through `PropsOf<G>` and
935
+ * everything built from it, so a `ContractProps<T>` built from it becomes a ~22-member union
936
+ * whose members share no common layout key: hostile to a spread (matches no member), to
937
+ * `Omit`/`Pick`/`Merge` (`TS2590 union too complex`) and to a rest-destructure (`TS2700`). A
938
+ * consumer extracting props for a wrapper wants "the layout props exist and are optional", not
939
+ * the discriminated union. The strict union stays on `PolymorphicComponent<G>`'s call overloads.
940
+ * See finding #44 / `praxis-kit-0.1.x-contractprops-regression.md` item #2.
941
+ *
942
+ * `Omit<P, LayoutKeyName>` (a static key set) drops every layout key from every member so the
943
+ * union dedupes to one; the flat `{ flex?: true; … }` is added back from just the keys the union
944
+ * really had. The `LayoutPluginKeys<P> extends never` fast-path returns `P` untouched for a
945
+ * plugin-less component, so the `Omit` never runs on its large intrinsic-prop object.
946
+ */
947
+ type FlattenLayout<P> = [P] extends [never] ? never : [LayoutPluginKeys<P>] extends [never] ? P : Simplify<Omit<P, LayoutKeyName> & { [K in LayoutPluginKeys<P>]?: true; }>;
948
+ type PolymorphicComponent<G extends PolymorphicGenerics, C extends FactoryOptions = FactoryOptions> = {
727
949
  <TAs extends ElementType = DefaultOf<G>>(props: PolymorphicWithAsChild<G, TAs>): AnyVNode;
728
950
  <TAs extends ElementType = DefaultOf<G>>(props: PolymorphicProps<G, TAs>): AnyVNode;
729
951
  /**
@@ -736,14 +958,30 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
736
958
  */
737
959
  (props: PolymorphicProps<G, DefaultOf<G>>): AnyVNode;
738
960
  /**
739
- * Type-only; never assigned at runtime. See `HasGenerics<G>` for
961
+ * Type-only; never assigned at runtime. See `HasGenerics<G>` (`@praxis-kit/contract-props`) for
740
962
  * the full rationale — kept as an inline field rather than `HasGenerics<G> & {...}` because
741
963
  * intersecting it onto this callable type changes how `PolymorphicComponent<any>` resolves
742
964
  * against concrete instantiations (confirmed for React's identical shape,
743
965
  * `adapters/react/src/shared/types/polymorphic-props.test.ts`); structurally identical to
744
- * `HasGenerics<G>` either way, which is what lets `ContractProps` constrain against it.
966
+ * `HasGenerics<G>` either way.
967
+ *
968
+ * `ContractProps` no longer reads this field directly (it projects `G` from the retained
969
+ * `__contract` below instead, mirroring React's identical Phase 4 rewire) — kept per the same
970
+ * migration policy: added alongside `__contract`, not replaced by it, until every adapter and
971
+ * test proves the new relationship out, and because other code may still constrain against
972
+ * `HasGenerics<G>` directly.
745
973
  */
746
974
  readonly __generics?: G;
975
+ /**
976
+ * Type-only; never assigned at runtime — same rationale as `__generics` above. Carries the
977
+ * *complete* contract this component was built from (`C`, the argument
978
+ * `createContractComponent<C extends PreactFactoryOptions>` was actually called with) —
979
+ * deliberately a second, separate marker from `__generics`, not `__generics` broadened to do
980
+ * both jobs: `G` answers "what does the adapter need to implement this component," `C` answers
981
+ * "what was this component configured with." Defaults to the widest `FactoryOptions` so every
982
+ * existing two-argument `PolymorphicComponent<G>` reference keeps resolving exactly as before.
983
+ */
984
+ readonly __contract?: C;
747
985
  displayName?: string;
748
986
  };
749
987
  /**
@@ -765,7 +1003,7 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
765
1003
  * type ContainerAsChildProps = ContractProps<typeof Container, 'asChild'>
766
1004
  * ```
767
1005
  */
768
- type ContractProps<T extends HasGenerics<PolymorphicGenerics>, M extends Exclude<Mode, 'render'> = 'normal'> = T extends HasGenerics<infer G extends PolymorphicGenerics> ? PickMode<M, PolymorphicProps<G, DefaultOf<G>>, PolymorphicWithAsChild<G, DefaultOf<G>>, never> : never;
1006
+ type ContractProps<T extends HasContract<FactoryOptions>, M extends Exclude<Mode, 'render'> = 'normal'> = T extends HasContract<infer C extends FactoryOptions> ? ContractGenericsOf<C> extends (infer G extends PolymorphicGenerics) ? PickMode<M, FlattenLayout<PolymorphicProps<G, DefaultOf<G>>>, FlattenLayout<PolymorphicWithAsChild<G, DefaultOf<G>>>, never> : never : never;
769
1007
  //#endregion
770
1008
  //#region ../../adapters/preact/src/create-contract-component.d.ts
771
1009
  /**
@@ -788,9 +1026,15 @@ type ContractProps<T extends HasGenerics<PolymorphicGenerics>, M extends Exclude
788
1026
  * Returns a `forwardRef` component — `ref` is forwarded to the rendered host element. Pass
789
1027
  * `subComponents` to attach named sub-components (`Card.Header`) and `onElement` to run setup
790
1028
  * once the real DOM element exists.
1029
+ *
1030
+ * `TDefault`/`Props`/`Variants`/`TPreset`/`TPlugin` are each `ContractXOf<C>`-derived *defaults*
1031
+ * on this function's own type parameter list, mirroring `@praxis-kit/react`'s identical fix — see
1032
+ * that adapter's own doc comment for why (computed as function type-parameter defaults, not inline
1033
+ * body computations, which doesn't resolve for a still-abstract `C`). No `TAllowed` here, matching
1034
+ * this adapter's pre-refactor behavior — Preact never threaded it as its own generic.
791
1035
  */
792
- 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: PreactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
1036
+ export declare function createContractComponent<C extends PreactFactoryOptions, 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 & {
793
1037
  readonly subComponents?: TSubComponents;
794
- }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>>, TSubComponents>;
1038
+ }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>, C>, TSubComponents>;
795
1039
  //#endregion
796
- export type { AnyFactoryOptions, ContractProps, ElementRef, ElementType, EmptyRecord, FactoryOptions, PolymorphicComponent, PolymorphicGenerics, PolymorphicProps, PolymorphicWithAsChild, PreactFactoryOptions };
1040
+ export type { ContractProps, ElementRef, ElementType, EmptyRecord, FactoryOptions, PolymorphicComponent, PolymorphicGenerics, PolymorphicProps, PolymorphicWithAsChild, PreactFactoryOptions };