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.
@@ -32,13 +32,6 @@ type SubComponentMap = Readonly<AnyRecord>;
32
32
  * editor hovers remain self-descriptive.
33
33
  */
34
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
35
  /**
43
36
  * Fallback for `ExtractPluginProps<TPlugin>` when a plugin contributes no
44
37
  * props, including the no-plugin case.
@@ -534,16 +527,6 @@ type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPre
534
527
  type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
535
528
  normalize(props: Readonly<Props & IntrinsicProps>): Props & IntrinsicProps;
536
529
  }['normalize'];
537
- /**
538
- * The type-erased shape of {@link FactoryOptions} — every generic parameter widened to its bound.
539
- *
540
- * Use it for a value that must hold *any* factory config (a registry, a generic wrapper). It
541
- * cannot check `styling.compounds` conditions against the real variant keys/values, because it
542
- * has forgotten what they are — for that, annotate against `FactoryOptions<...>` with the concrete
543
- * generics (or `satisfies FactoryOptions<'button', Props, typeof variants>`), which keeps an
544
- * invalid compound condition a type error rather than a silent no-op.
545
- */
546
- type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, RecipeMap<VariantMap>, AnyClassPluginFactory>;
547
530
  /**
548
531
  * The framework-neutral component-authoring config passed to `createContractComponent` in every
549
532
  * adapter: default tag + name, own-prop defaults, a `normalize` transform, `styling` (variants,
@@ -553,7 +536,9 @@ type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, Reci
553
536
  * `satisfies FactoryOptions<TDefault, Props, typeof variants, ...>` on a config object narrows
554
537
  * `styling.compounds` conditions to the real per-variant-key shape — including resolving a
555
538
  * boolean-shaped axis (`{ true, false }`) to a real `boolean` — so a condition naming a variant or
556
- * value that does not exist is a compile error. `AnyFactoryOptions` cannot do this.
539
+ * value that does not exist is a compile error. Leaving `V` at the bare `VariantMap` instead
540
+ * (whether via `FactoryOptions`'s own default or an explicit erased instantiation) forgets that
541
+ * shape entirely, so the same invalid condition becomes a silent no-op instead.
557
542
  */
558
543
  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> = {
559
544
  /** The intrinsic tag the component renders by default. Overridable per instance via `as`. */
@@ -562,6 +547,33 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
562
547
  readonly name?: string;
563
548
  /** Values used for the component's own (non-variant) props when the consumer omits them. */
564
549
  readonly defaults?: Partial<NoInfer<Props>>;
550
+ /**
551
+ * Optional, type-only declaration of this component's complete own-prop shape — present purely
552
+ * for type recovery, never read at runtime (see `declareProps` in `@praxis-kit/adapter-utils`).
553
+ *
554
+ * `defaults` alone can only prove a prop *has a default*, not that it's the complete prop model
555
+ * a component accepts — a `defaults: { size: 'md' }` component may still take `onClick`,
556
+ * `disabled`, and other props with no default at all, none of which a `defaults`-only recovery
557
+ * can see (see `ContractPropsFrom`'s own doc comment for the general shape of this problem).
558
+ * `props` closes that gap: when present, `ContractPropsOf<C>` / `ContractProps<typeof Component>`
559
+ * recover this declared type directly and exactly, in place of the necessarily-partial,
560
+ * literal-widened recovery `defaults` alone allows.
561
+ *
562
+ * Typed as `object | undefined`, not `NoInfer<Props> | undefined` like `defaults`/`onElement` —
563
+ * deliberately decoupled from this interface's own `Props` generic, unlike those two fields.
564
+ * `defaults`/`onElement` are tied to `Props` because real runtime code reads them against a
565
+ * concretely-resolved `Props` for a real call; `props` is never read at runtime at all (see
566
+ * above), so it has no such need, and tying it to `Props extends AnyRecord` would force every
567
+ * hand-declared prop `interface`/`type` an author passes through `declareProps<Props>()` to
568
+ * structurally satisfy `Record<string, unknown>` — a real TypeScript limitation (a named type
569
+ * without an index signature never satisfies that, even via plain assignment, only a fresh
570
+ * object literal does) that would make this field far more awkward to use for its one real job:
571
+ * carrying an author's own already-precise prop type through untouched. `ContractPropsFrom`
572
+ * (`contract-model.ts`) recovers the real value here structurally, straight off `O`'s own
573
+ * literal type — independent of this field's declared type, same as every other `Contract*From`
574
+ * derivation in that file.
575
+ */
576
+ readonly props?: object | undefined;
565
577
  /**
566
578
  * A pure `(props) => props` transform run on every render, after `enforcement.props`'s
567
579
  * normalizers see the same input. Use this for component-specific prop shaping — anything
@@ -616,9 +628,159 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
616
628
  *
617
629
  * Return a cleanup function to run when the instance unmounts.
618
630
  */
619
- readonly onElement?: (element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>) => void | (() => void);
631
+ readonly onElement?: {
632
+ onElement(element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>): void | (() => void);
633
+ }['onElement'];
620
634
  };
621
635
  //#endregion
636
+ //#region ../../lib/primitive/src/types/factory/contract-model.d.ts
637
+ /**
638
+ * The pipeline this file implements:
639
+ *
640
+ * ```text
641
+ * O (a raw contract literal)
642
+ * → Contract*From<O> one derivation per dimension, straight off O's own shape
643
+ * → ContractDimensions<O> the six derivations, assembled
644
+ * → ContractModel<...> the same six values, as a required-field carrier
645
+ * → ContractModelOf<C> resolves an already-`defineContract`-ed C's real model,
646
+ * or reconstructs one from a raw C via ContractModelFrom
647
+ * ```
648
+ *
649
+ * See `DECISIONS.md`'s `defineContract` entry for the TypeScript limitations that shaped the
650
+ * derivations below.
651
+ */
652
+ /**
653
+ * Canonical type-level representation of a contract's dimensions — the contract-level counterpart
654
+ * to `PolymorphicGenerics`. Every field is required, so a field's presence is never ambiguous the
655
+ * way it is on an optional `FactoryOptions` field.
656
+ */
657
+ 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> {
658
+ readonly tag: TDefault;
659
+ readonly props: Props;
660
+ readonly variants: V;
661
+ readonly preset: TPreset;
662
+ readonly plugin: TPlugin;
663
+ readonly allowed: TAllowed;
664
+ }
665
+ /**
666
+ * The phantom-marker shape read back via `T extends HasContractModel<infer M> ? M : ...` — the
667
+ * contract-level counterpart to `lib/contract-props`'s `HasGenerics<G>`. Type-only: never assigned
668
+ * at runtime.
669
+ *
670
+ * `__model` is **required**, not optional like `HasGenerics<G>`'s `__generics?` — an optional field
671
+ * would make `C extends HasContractModel<infer M>` trivially succeed for any `C`, defeating the
672
+ * one thing this marker exists to answer: did `C` really go through `defineContract`?
673
+ */
674
+ interface HasContractModel<M extends ContractModel = ContractModel> {
675
+ readonly __model: M;
676
+ }
677
+ type ContractTagFrom<O> = O extends {
678
+ tag: infer TDefault extends ElementType;
679
+ } ? TDefault : ElementType;
680
+ /** `'img'` → `string`, `1` → `number`, `true` → `boolean` — undoes `defineContract`'s `const O`
681
+ * literal narrowing so a `defaults` value doesn't become the only value a caller may pass. */
682
+ type WidenLiteral<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
683
+ type WidenShallow<T> = { [K in keyof T]: WidenLiteral<T[K]>; };
684
+ /** What the author explicitly declared, via `props: declareProps<Props>()`
685
+ * (`@praxis-kit/adapter-utils`) — used exactly as declared, no `Partial`/widening/`data-*`
686
+ * stripping (see `DefaultPropsFrom` for why those exist there but not here). */
687
+ type DeclaredPropsFrom<O> = O extends {
688
+ props: infer Props extends object | undefined;
689
+ } ? [NonNullable<Props>] extends [never] ? never : NonNullable<Props> & AnyRecord : never;
690
+ /** What can be inferred from `defaults` — partial at best, since a default only proves a prop
691
+ * *has* one, not that it's the complete prop set. `Partial`: a defaulted prop is optional to the
692
+ * caller by definition. `data-*` keys are dropped — every adapter has its own passthrough for
693
+ * those already (finding #43). */
694
+ type DefaultPropsFrom<O> = O extends {
695
+ defaults: infer Props extends AnyRecord;
696
+ } ? Partial<WidenShallow<Omit<Props, Extract<keyof Props, `data-${string}`>>>> : never;
697
+ /** Declared beats defaulted beats nothing. `[X] extends [never]`, not bare `X extends never` —
698
+ * tuple-wrapped so the check doesn't distribute if `X` is ever a union containing `never`. */
699
+ type ContractPropsFrom<O> = [DeclaredPropsFrom<O>] extends [never] ? [DefaultPropsFrom<O>] extends [never] ? EmptyRecord : DefaultPropsFrom<O> : DeclaredPropsFrom<O>;
700
+ type ContractVariantsFrom<O> = O extends {
701
+ styling: {
702
+ variants: infer V extends Readonly<VariantMap>;
703
+ };
704
+ } ? V : Readonly<EmptyRecord>;
705
+ type ContractPresetFrom<O> = O extends {
706
+ styling: {
707
+ presets: infer TPreset extends RecipeMap<VariantMap>;
708
+ };
709
+ } ? TPreset : Readonly<EmptyRecord>;
710
+ type ContractPluginFrom<O> = O extends {
711
+ styling: {
712
+ plugin: infer TPlugin extends AnyClassPluginFactory;
713
+ };
714
+ } ? TPlugin : AnyClassPluginFactory;
715
+ type ContractAllowedFrom<O> = O extends {
716
+ enforcement: {
717
+ allowedAs: readonly (infer TAllowed extends ElementType)[];
718
+ };
719
+ } ? TAllowed : ElementType;
720
+ /** The six derivations above, assembled — the direct input to `ContractModel`. */
721
+ type ContractDimensions<O> = {
722
+ readonly tag: ContractTagFrom<O>;
723
+ readonly props: ContractPropsFrom<O>;
724
+ readonly variants: ContractVariantsFrom<O>;
725
+ readonly preset: ContractPresetFrom<O>;
726
+ readonly plugin: ContractPluginFrom<O>;
727
+ readonly allowed: ContractAllowedFrom<O>;
728
+ };
729
+ /**
730
+ * Builds a `ContractModel` from a raw literal `O` — the fallback path `ContractModelOf<C>` uses
731
+ * when `C` never went through `defineContract` (no `__model` marker to read directly), and what
732
+ * `defineContract` itself attaches as that marker for a `C` that did.
733
+ */
734
+ 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;
735
+ /**
736
+ * Resolves "does `C` carry a real `ContractModel` already, or do we need to build one from its raw
737
+ * shape" — every `Contract*Of` accessor (`contract-of.ts`) is a one-line projection off this.
738
+ */
739
+ type ContractModelOf<C> = C extends HasContractModel<infer M> ? M : ContractModelFrom<C>;
740
+ //#endregion
741
+ //#region ../../lib/primitive/src/types/factory/contract-of.d.ts
742
+ /**
743
+ * `FactoryOptions`-level accessor family, mirroring `polymorphic-generics.ts`'s `*Of<T>`
744
+ * convention (`DefaultOf<G>`, `PropsOf<G>`, etc.) but applied one layer up, to a contract itself
745
+ * rather than to the `PolymorphicGenerics` an adapter derives from it.
746
+ *
747
+ * Named with a `Contract` prefix specifically to avoid colliding with `polymorphic-generics.ts`'s
748
+ * own `PropsOf`/`VariantsOf`/`RecipeOf`/`AllowedOf`/`DefaultOf` — both families are re-exported
749
+ * from `@praxis-kit/core`, so a name clash would be a real conflict, not a style nit.
750
+ *
751
+ * Each accessor is a trivial projection off `ContractModelOf<C>` (`contract-model.ts`) — the one
752
+ * place that resolves "does `C` carry a real `ContractModel` (via `defineContract`) or does one
753
+ * need reconstructing from `C`'s raw shape," so every accessor shares one implementation of that
754
+ * resolution rather than repeating it. See `contract-model.ts`'s own doc comments for the
755
+ * `ContractXFrom<O>` derivation each of these ultimately reads through, and `DECISIONS.md`'s
756
+ * `defineContract` entry for the design history behind the required-pattern-match technique.
757
+ */
758
+ type ContractTagOf<C extends FactoryOptions> = ContractModelOf<C>['tag'];
759
+ /** See this file's own doc comment. Best-effort when `C` has no `ContractModel` marker — see
760
+ * `ContractPropsFrom`'s own doc comment for why. */
761
+ type ContractPropsOf<C extends FactoryOptions> = ContractModelOf<C>['props'];
762
+ /** See this file's own doc comment. */
763
+ type ContractVariantsOf<C extends FactoryOptions> = ContractModelOf<C>['variants'];
764
+ /** See this file's own doc comment. */
765
+ type ContractPresetOf<C extends FactoryOptions> = ContractModelOf<C>['preset'];
766
+ /** See this file's own doc comment. */
767
+ type ContractPluginOf<C extends FactoryOptions> = ContractModelOf<C>['plugin'];
768
+ //#endregion
769
+ //#region ../../lib/primitive/src/types/factory/contract-generics.d.ts
770
+ /**
771
+ * The canonical projection from a defined contract `C` to the `PolymorphicGenerics` shape every
772
+ * adapter's prop types are built from — the one place "G" gets computed, so it can no longer
773
+ * silently drift per adapter the way today's hand-assembled `PolymorphicGenerics<...>` instantiation
774
+ * at each `createContractComponent` call site can (see `ContractGenericsWithAllowedOf` below for
775
+ * the one confirmed instance of that drift).
776
+ *
777
+ * Folds the class-resolution plugin's own contributed props (`ExtractPluginProps<TPlugin>`) into
778
+ * `props` here, once, rather than leaving each adapter's `ContractProps` to re-derive that merge
779
+ * via its own distributive conditional type on every read — the root cause of the ~22-member
780
+ * layout-union bug PR #95 patched per-adapter (see `DECISIONS.md`'s `defineContract` entry).
781
+ */
782
+ type ContractGenericsOf<C extends FactoryOptions> = PolymorphicGenerics<ContractTagOf<C>, MergeRecords<ContractPropsOf<C>, ExtractPluginProps<ContractPluginOf<C>>>, ContractVariantsOf<C>, ContractPresetOf<C>>;
783
+ //#endregion
622
784
  //#region ../../lib/adapter-utils/src/types/filter-predicate.d.ts
623
785
  /**
624
786
  * Determines whether a prop should be stripped before forwarding to the
@@ -634,9 +796,6 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
634
796
  */
635
797
  type FilterPredicate = (key: string, variantKeys: ReadonlySet<string>) => boolean;
636
798
  //#endregion
637
- //#region ../../lib/adapter-utils/src/runtime/define-component.d.ts
638
- export declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
639
- //#endregion
640
799
  //#region ../../adapters/lit/src/types/lit-options.d.ts
641
800
  /**
642
801
  * Options accepted by createContractComponent in the Lit adapter.
@@ -648,14 +807,17 @@ export declare function defineContractComponent<O extends FactoryOptions>(option
648
807
  *
649
808
  * Note: this adapter targets Light DOM composition only. Shadow DOM slot
650
809
  * protocol is intentionally out of scope.
810
+ *
811
+ * Every generic parameter has a default (widened to that parameter's own *bound*, matching
812
+ * a wide-bound, type-erased philosophy, not `FactoryOptions`'s own narrower `EmptyRecord`-style
813
+ * defaults) so `LitFactoryOptions` can be used bare, as `createContractComponent`'s single
814
+ * `C extends LitFactoryOptions` constraint — see `ReactFactoryOptions`'s identical fix for the
815
+ * same reason.
651
816
  */
652
- type LitFactoryOptions<TDefault extends ElementType = ElementType, TProps extends AnyRecord = EmptyRecord, TVariants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<TVariants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, TProps, TVariants, TPreset, TPlugin> & {
817
+ type LitFactoryOptions<TDefault extends ElementType = ElementType, TProps extends AnyRecord = AnyRecord, TVariants extends Readonly<VariantMap> = Readonly<VariantMap>, TPreset extends RecipeMap<TVariants> = RecipeMap<TVariants>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, TProps, TVariants, TPreset, TPlugin> & {
653
818
  readonly filterProps?: FilterPredicate;
654
819
  };
655
820
  //#endregion
656
- //#region ../../adapters/lit/src/types/generics.d.ts
657
- type RuntimeG<TDefault extends ElementType, Props extends AnyRecord, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants>> = PolymorphicGenerics<TDefault, Props, Variants, TPreset>;
658
- //#endregion
659
821
  //#region ../../adapters/lit/src/types/primitives.d.ts
660
822
  type UnknownProps = AnyRecord;
661
823
  /**
@@ -668,8 +830,12 @@ type UnknownProps = AnyRecord;
668
830
  * `G` is a phantom marker only — see `__generics` below — and defaults to the
669
831
  * widest `PolymorphicGenerics` so existing two-argument usages of this type
670
832
  * (every call site inside this adapter) keep resolving exactly as before.
833
+ *
834
+ * `C` is a second phantom marker — see `__contract` below — added alongside `G`, not replacing
835
+ * it (Phase 3 of the `defineContract` refactor), defaulting to the widest `FactoryOptions` for the
836
+ * same reason.
671
837
  */
672
- type LitContractComponent<TVariants extends Readonly<VariantMap> = NoVariants, TPluginProps extends AnyRecord = EmptyRecord, G extends PolymorphicGenerics = PolymorphicGenerics> = {
838
+ type LitContractComponent<TVariants extends Readonly<VariantMap> = NoVariants, TPluginProps extends AnyRecord = EmptyRecord, G extends PolymorphicGenerics = PolymorphicGenerics, C extends FactoryOptions = FactoryOptions> = {
673
839
  new (): MergeRecords<LitElement & {
674
840
  recipe: string | undefined;
675
841
  praxisClass: string | undefined;
@@ -689,45 +855,70 @@ type LitContractComponent<TVariants extends Readonly<VariantMap> = NoVariants, T
689
855
  * set, but kept consistent with the established shape regardless.
690
856
  */
691
857
  readonly __generics?: G;
858
+ /**
859
+ * Type-only; never assigned at runtime — same rationale as `__generics` above and
860
+ * React's/Preact's `__contract` (see `HasContract<C>`, `@praxis-kit/contract-props`). Carries
861
+ * the *complete* contract this component was built from (`C`, the argument
862
+ * `createContractComponent<C extends LitFactoryOptions>` was actually called with), not just
863
+ * its `PolymorphicGenerics` projection. `GenericsOf`/`ContractProps` (./contract-props) recover
864
+ * `G` *through* this field now (`ContractGenericsOf<C>`), rather than from a separately-computed
865
+ * `G` that never carried plugin-contributed props — see that file's doc comment for the bug this
866
+ * closes.
867
+ */
868
+ readonly __contract?: C;
692
869
  };
693
870
  //#endregion
694
- //#region ../../lib/contract-props/src/has-generics.d.ts
871
+ //#region ../../lib/contract-props/src/has-contract.d.ts
695
872
  /**
696
- * The phantom-marker shape read back via `T extends HasGenerics<infer G> ? G : never`. See the
697
- * README for why this exists.
873
+ * The phantom-marker shape read back via `T extends HasContract<infer C> ? C : never` — the
874
+ * contract-retention counterpart to `HasGenerics<G>` in this same package. Where `HasGenerics<G>`
875
+ * lets a built component recover the `PolymorphicGenerics` an adapter derived for it,
876
+ * `HasContract<C>` lets it recover the *complete, authoritative contract* (`C`, the argument
877
+ * `createContractComponent<C extends XFactoryOptions>` was actually called with) it was derived
878
+ * from — the two are deliberately separate markers, not one broadened to do both jobs: `G` is
879
+ * "what the adapter needs to implement the component," `C` is "what the component was configured
880
+ * with" (see `DECISIONS.md`'s `defineContract` entry). A component carries both.
881
+ *
882
+ * Type-only: never assigned at runtime, same rationale as `HasGenerics<G>` (see that type's own
883
+ * doc comment) — a `createContractComponent` return value gets this shape via a type assertion,
884
+ * not a real property write.
698
885
  *
699
- * Type-only: never assigned at runtime. In React's `PolymorphicComponent<G>`, declaring `readonly
700
- * __generics?: G` inline (structurally matching this shape) rather than writing `HasGenerics<G> &
701
- * { ...call signatures... }` was necessary to keep `PolymorphicComponent<any>`-typed test helpers
702
- * assignable — confirmed directly against that adapter's own real component type (see
703
- * `adapters/react/src/shared/types/polymorphic-props.test.ts`), not reproducible in an isolated
704
- * minimal mock (see this file's own `has-generics.test.ts`), so treat "inline, not intersected"
705
- * as an adapter-level implementation detail this package's marker shape must stay compatible
706
- * with, not a property `HasGenerics<G>` itself enforces.
886
+ * Unconstrained (no `C extends FactoryOptions` bound), matching `HasGenerics<G>`'s own choice —
887
+ * this package has no dependency on `@praxis-kit/core`/`@praxis-kit/primitive`, and adding one
888
+ * just to write a bound here isn't worth it: the accessor types that actually consume `C`
889
+ * (`ContractTagOf<C>`, etc., in `@praxis-kit/primitive`) already declare their own constraint.
707
890
  *
708
- * Do **not** "harden" this with a `unique symbol` or other nominal brand: the whole point is that
709
- * an adapter's real callable component type structurally satisfies this shape by declaring the
710
- * same optional string-keyed field inline. Nominal purity here would break that compatibility.
891
+ * Do **not** "harden" this with a `unique symbol` or other nominal brand, for the same reason
892
+ * `HasGenerics<G>` doesn't: a real component's callable type needs to structurally satisfy this
893
+ * shape by declaring the same inline optional field, not by importing a nominal brand.
711
894
  */
712
- interface HasGenerics<G> {
713
- readonly __generics?: G;
895
+ interface HasContract<C> {
896
+ readonly __contract?: C;
714
897
  }
715
898
  //#endregion
716
899
  //#region ../../adapters/lit/src/types/contract-props.d.ts
717
900
  /**
718
901
  * Recovers a `LitContractComponent`'s `PolymorphicGenerics` descriptor from its own value type —
719
- * the Lit analog of React's/Preact's `__generics` marker recovery.
720
- * Needs the marker (unlike Svelte's `GenericsOf<T>`,
902
+ * the Lit analog of React's/Preact's `ContractGenericsOf`-via-`__contract` recovery.
903
+ * Needs a marker (unlike Svelte's `GenericsOf<T>`,
721
904
  * `adapters/svelte/src/types/resolved-slot-props.ts`) because `createContractComponent` here
722
- * returns `LitContractComponent<TVariants, TPluginProps, G>`, not `BuiltRuntime<G, TOptions>`
905
+ * returns `LitContractComponent<TVariants, TPluginProps, G, C>`, not `BuiltRuntime<G, TOptions>`
723
906
  * directly — `TDefault`/`Props`/`TPreset` are genuinely erased from the return type, not merely
724
907
  * hidden, so there is no ordinary type parameter left to `infer` them back out of.
725
- * `LitContractComponent`'s own `__generics` field (`./primitives`) exists purely to make this
726
- * recovery possible. Falls back to the widest `PolymorphicGenerics` for any non-praxis-kit value,
727
- * the same "no marker, nothing to recover" case `HasGenerics<G>`'s own `never` branch covers for
728
- * React/Preact.
908
+ *
909
+ * Re-pointed at `__contract` (Phase 4 of the `defineContract` refactor) rather than the
910
+ * separately-computed `__generics` field it used to read: `__generics`'s own `G` was assembled
911
+ * from `TDefault`/`Props`/`TVariants`/`TPreset` alone, with **no plugin-contributed props folded
912
+ * in** — a real, previously-open gap (`findings.md` #44 — Lit/Web's `ContractProps` silently
913
+ * missing plugin props entirely, not just unionized like React/Preact's old bug). `ContractGenericsOf<C>`
914
+ * folds `ExtractPluginProps<TPlugin>` into `props` once, at the canonical projection point, so this
915
+ * fix comes for free from routing through it — no separate merge needed here. `__generics` itself
916
+ * is untouched (still assembled, still readable) — this file just no longer reads it.
917
+ *
918
+ * Falls back to the widest `PolymorphicGenerics` for any non-praxis-kit value, the same "no
919
+ * marker, nothing to recover" case `HasContract<C>`'s own `never` branch covers for React/Preact.
729
920
  */
730
- type GenericsOf<T extends HasGenerics<PolymorphicGenerics>> = T extends HasGenerics<infer G extends PolymorphicGenerics> ? G : PolymorphicGenerics;
921
+ type GenericsOf<T extends HasContract<FactoryOptions>> = T extends HasContract<infer C extends FactoryOptions> ? ContractGenericsOf<C> extends (infer G extends PolymorphicGenerics) ? G : PolymorphicGenerics : PolymorphicGenerics;
731
922
  /**
732
923
  * A component's full prop contract — the attributes a caller can set on the custom element,
733
924
  * recovered from outside the file that built it. Lit has exactly one render mode (no
@@ -757,7 +948,7 @@ type GenericsOf<T extends HasGenerics<PolymorphicGenerics>> = T extends HasGener
757
948
  * type ButtonProps = ContractProps<typeof Button>
758
949
  * ```
759
950
  */
760
- type ContractProps<T extends HasGenerics<PolymorphicGenerics>> = Simplify<OmitIndexSignature<PropsOf<GenericsOf<T>>> & OmitIndexSignature<VariantProps<VariantsOf<GenericsOf<T>>>> & DataAttributes & {
951
+ type ContractProps<T extends HasContract<FactoryOptions>> = Simplify<OmitIndexSignature<PropsOf<GenericsOf<T>>> & OmitIndexSignature<VariantProps<VariantsOf<GenericsOf<T>>>> & DataAttributes & {
761
952
  as?: never;
762
953
  recipe?: keyof RecipeOf<GenericsOf<T>>;
763
954
  }>;
@@ -839,10 +1030,15 @@ type DataAttributes = {
839
1030
  * tag polymorphism, but SSR quietly provided a fake, DOM-inconsistent form of it until now. Need
840
1031
  * different semantics for one instance? Register a second component with a different `tag`, or
841
1032
  * set `role` directly — both already work today, unaffected by this.
1033
+ *
1034
+ * `TDefault`/`TProps`/`TVariants`/`TPreset`/`TPlugin` are each `ContractXOf<C>`-derived *defaults*
1035
+ * on this function's own type parameter list, mirroring every other adapter's identical fix — see
1036
+ * `@praxis-kit/react`'s own doc comment for why (computed as function type-parameter defaults, not
1037
+ * inline body computations, which doesn't resolve for a still-abstract `C`).
842
1038
  */
843
- export declare function createContractComponent<TDefault extends ElementType, TProps extends UnknownProps = EmptyRecord, TVariants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<TVariants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: LitFactoryOptions<TDefault, TProps, TVariants, TPreset, TPlugin> & {
1039
+ export declare function createContractComponent<C extends LitFactoryOptions, TDefault extends ElementType = ContractTagOf<C>, TProps extends UnknownProps = ContractPropsOf<C>, TVariants extends Readonly<VariantMap> = ContractVariantsOf<C>, TPreset extends RecipeMap<VariantMap> = ContractPresetOf<C>, TPlugin extends AnyClassPluginFactory = ContractPluginOf<C>, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: C & {
844
1040
  readonly subComponents?: TSubComponents;
845
- }): MergeRecords<LitContractComponent<TVariants, ExtractPluginProps<TPlugin>, RuntimeG<TDefault, TProps, TVariants, TPreset>>, TSubComponents>;
1041
+ }): MergeRecords<LitContractComponent<TVariants, ExtractPluginProps<TPlugin>, ContractGenericsOf<C>, C>, TSubComponents>;
846
1042
  //#endregion
847
1043
  //#region ../../adapters/lit/src/render-to-string.d.ts
848
1044
  /**
@@ -875,4 +1071,4 @@ export declare function createContractComponent<TDefault extends ElementType, TP
875
1071
  */
876
1072
  export declare function renderContractToString(component: LitContractComponent, props?: UnknownProps, innerHTML?: string): string;
877
1073
  //#endregion
878
- export type { AnyFactoryOptions, ContractProps, ElementType, EmptyRecord, FactoryOptions, GenericsOf, LitContractComponent, LitFactoryOptions, PolymorphicGenerics };
1074
+ export type { ContractProps, ElementType, EmptyRecord, FactoryOptions, GenericsOf, LitContractComponent, LitFactoryOptions, PolymorphicGenerics };