praxis-kit 7.0.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vue/index.js CHANGED
@@ -1,6 +1,30 @@
1
1
  // ../../adapters/vue/src/create-contract-component.ts
2
2
  import { computed, defineComponent as defineComponent2 } from "vue";
3
3
 
4
+ // ../../lib/adapter-utils/src/invariant.ts
5
+ function panic(message) {
6
+ throw new Error(message);
7
+ }
8
+ function invariant(condition, message) {
9
+ if (!condition) panic(message);
10
+ }
11
+
12
+ // ../../lib/adapter-utils/src/runtime/apply-display-name.ts
13
+ function applyDisplayName(component, name) {
14
+ Object.assign(component, { displayName: name ?? "PolymorphicComponent" });
15
+ }
16
+
17
+ // ../../lib/adapter-utils/src/runtime/define-component.ts
18
+ function defineContractComponent(options) {
19
+ return (factory) => factory(options);
20
+ }
21
+
22
+ // ../../lib/adapter-utils/src/runtime/assemble-compound-component.ts
23
+ function assembleCompoundComponent(root, subComponents) {
24
+ if (!subComponents) return root;
25
+ return Object.assign(root, subComponents);
26
+ }
27
+
4
28
  // ../../lib/primitive/src/tag/resolve-tag.ts
5
29
  function makeResolveTag(defaultTag) {
6
30
  return function tag(as) {
@@ -43,10 +67,13 @@ function isString(value) {
43
67
  function isNumber(value) {
44
68
  return typeof value === "number";
45
69
  }
70
+ function isFunction(value) {
71
+ return typeof value === "function";
72
+ }
46
73
 
47
74
  // ../../lib/primitive/src/rule/is-dynamic-rule.ts
48
75
  function isDynamicRule(rule) {
49
- return isObject(rule, true) && rule[RULE_BRAND] === true;
76
+ return isObject(rule, true) && Reflect.get(rule, RULE_BRAND) === true;
50
77
  }
51
78
 
52
79
  // ../../lib/primitive/src/rule/resolve-rule.ts
@@ -676,17 +703,17 @@ var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default
676
703
  // ../../lib/primitive/src/guards/children/is-tag.ts
677
704
  function getAsProp(child) {
678
705
  if (!isObject(child) || !("props" in child)) return void 0;
679
- const props = child.props;
706
+ const { props } = child;
680
707
  if (!isObject(props)) return void 0;
681
- const as = props.as;
708
+ const as = Reflect.get(props, "as");
682
709
  return isString(as) && as !== "" ? as : void 0;
683
710
  }
684
711
  function getTag(child) {
685
712
  if (!isObject(child) || !("type" in child)) return void 0;
686
- const t = child.type;
713
+ const { type: t } = child;
687
714
  if (isString(t)) return t;
688
715
  if (typeof t === "function" || isObject(t)) {
689
- const defaultTag = t[COMPONENT_DEFAULT_TAG];
716
+ const defaultTag = Reflect.get(t, COMPONENT_DEFAULT_TAG);
690
717
  if (!isString(defaultTag)) return void 0;
691
718
  return getAsProp(child) ?? defaultTag;
692
719
  }
@@ -706,22 +733,34 @@ function isTag(...args) {
706
733
  return tag !== void 0 && set2.has(tag);
707
734
  }
708
735
 
709
- // ../../lib/adapter-utils/src/invariant.ts
710
- function panic(message) {
711
- throw new Error(message);
712
- }
713
- function invariant(condition, message) {
714
- if (!condition) panic(message);
715
- }
716
-
717
- // ../../lib/adapter-utils/src/runtime/apply-display-name.ts
718
- function applyDisplayName(component, name) {
719
- Object.assign(component, { displayName: name ?? "PolymorphicComponent" });
720
- }
721
-
722
- // ../../lib/adapter-utils/src/runtime/define-component.ts
723
- function defineContractComponent(options) {
724
- return (factory) => factory(options);
736
+ // ../../lib/adapter-utils/src/runtime/finalize-component.ts
737
+ function finalizeComponent(component, defaultTag, subComponents) {
738
+ if (typeof defaultTag === "string") {
739
+ Object.assign(component, { [COMPONENT_DEFAULT_TAG]: defaultTag });
740
+ }
741
+ return assembleCompoundComponent(component, subComponents);
742
+ }
743
+
744
+ // ../../lib/adapter-utils/src/runtime/is-factory-options-like.ts
745
+ var FACTORY_OPTIONS_FIELD_VALIDATORS = {
746
+ tag: (v) => v === void 0 || isString(v),
747
+ name: (v) => v === void 0 || isString(v),
748
+ defaults: (v) => v === void 0 || isObject(v),
749
+ normalize: (v) => v === void 0 || isFunction(v),
750
+ styling: (v) => v === void 0 || isObject(v),
751
+ enforcement: (v) => v === void 0 || isObject(v),
752
+ diagnostics: (v) => v === void 0 || isObject(v),
753
+ subComponents: (v) => v === void 0 || isObject(v),
754
+ onElement: (v) => v === void 0 || isFunction(v)
755
+ };
756
+ function isFactoryOptionsLike(options, extraFieldValidators) {
757
+ if (!isObject(options)) return false;
758
+ const validators = { ...FACTORY_OPTIONS_FIELD_VALIDATORS, ...extraFieldValidators };
759
+ for (const [key, value] of Object.entries(options)) {
760
+ const validate = validators[key];
761
+ if (!validate || !validate(value)) return false;
762
+ }
763
+ return true;
725
764
  }
726
765
 
727
766
  // ../../lib/contract/src/aria/aria-role-policy.ts
@@ -2874,7 +2913,7 @@ function getChildProp(child, key) {
2874
2913
  if (!isVNodeLike(child)) return void 0;
2875
2914
  const { props } = child;
2876
2915
  if (!isObject(props)) return void 0;
2877
- return props[key];
2916
+ return Reflect.get(props, key);
2878
2917
  }
2879
2918
 
2880
2919
  // ../core/src/html/contracts/aria/landmarks.ts
@@ -3571,6 +3610,13 @@ function buildRuntime(options) {
3571
3610
  return built;
3572
3611
  }
3573
3612
 
3613
+ // ../../adapters/vue/src/is-polymorphic-component.ts
3614
+ function isPolymorphicComponent(value) {
3615
+ if (!isObject(value)) return false;
3616
+ if (!("displayName" in value)) return true;
3617
+ return value.displayName === void 0 || isString(value.displayName);
3618
+ }
3619
+
3574
3620
  // ../../adapters/vue/src/render.ts
3575
3621
  import { cloneVNode, h as h3 } from "vue";
3576
3622
 
@@ -3669,7 +3715,7 @@ function prepareRenderState(runtime, attrs, filterProps) {
3669
3715
  className
3670
3716
  };
3671
3717
  }
3672
- function buildElementProps(props, className) {
3718
+ function buildElementProps(props, className, elementRef) {
3673
3719
  const { role, ...rest } = props;
3674
3720
  return {
3675
3721
  ...normalizeListenerKeys(rest),
@@ -3677,11 +3723,17 @@ function buildElementProps(props, className) {
3677
3723
  // normalizeClass turns it into '' and still emits class="", while the client patcher
3678
3724
  // removes the attribute outright. Omitting the key entirely keeps both paths consistent.
3679
3725
  ...className !== void 0 && { class: className },
3680
- ...isKnownAriaRole(role) && { role }
3726
+ ...isKnownAriaRole(role) && { role },
3727
+ // Vue calls a function-ref with the element on mount and with null on unmount, same
3728
+ // contract as React/Preact's callback refs — a natural fit for FactoryOptions.onElement.
3729
+ // Cast: Vue's own VNodeRef type also accepts a resolved *component* instance, since refs
3730
+ // can target components as well as elements — irrelevant here, this is only ever attached
3731
+ // to an intrinsic host tag, which always resolves to a real Element.
3732
+ ...elementRef !== void 0 && { ref: elementRef }
3681
3733
  };
3682
3734
  }
3683
- function renderIntrinsic(state, runtime, slots) {
3684
- const elementProps = buildElementProps(state.props, state.className);
3735
+ function renderIntrinsic(state, runtime, slots, elementRef) {
3736
+ const elementProps = buildElementProps(state.props, state.className, elementRef);
3685
3737
  const domProps = runtime.resolveAria(state.tag, elementProps).props;
3686
3738
  return h3(state.tag, domProps, slots.default ? { default: slots.default } : void 0);
3687
3739
  }
@@ -3694,12 +3746,13 @@ function validateSlotDirectives(directives, validator) {
3694
3746
  }
3695
3747
  return true;
3696
3748
  }
3697
- function tryRenderAsChild(state, children, discarded, validator) {
3749
+ function tryRenderAsChild(state, children, discarded, validator, elementRef) {
3698
3750
  if (!validateSlotDirectives(state.directives, validator)) return null;
3699
3751
  if (discarded > 0) validator.warnDiscardedChildren(discarded);
3700
3752
  const attrs = {
3701
3753
  ...pickAttributes(state.props),
3702
- ...state.className !== void 0 && { class: state.className }
3754
+ ...state.className !== void 0 && { class: state.className },
3755
+ ...elementRef !== void 0 && { ref: elementRef }
3703
3756
  };
3704
3757
  const slottable = extractSlottable(children);
3705
3758
  if (slottable) return slottable.rebuild(cloneVNode(slottable.child, attrs));
@@ -3714,20 +3767,31 @@ function render({
3714
3767
  state,
3715
3768
  slots,
3716
3769
  slotValidator,
3717
- childrenEvaluator
3770
+ childrenEvaluator,
3771
+ elementRef
3718
3772
  }) {
3719
3773
  const { vnodes: children, discarded } = normalizeChildren(slots);
3720
3774
  if (process.env.NODE_ENV !== "production") {
3721
3775
  childrenEvaluator?.evaluate(children, { tag: state.tag, props: state.normalizedProps });
3722
3776
  runtime.options.htmlChildrenEvaluatorFn?.(state.tag)?.evaluate(children, { tag: state.tag, props: state.normalizedProps });
3723
3777
  }
3724
- const slotResult = tryRenderAsChild(state, children, discarded, slotValidator);
3725
- return slotResult ?? renderIntrinsic(state, runtime, slots);
3778
+ const slotResult = tryRenderAsChild(state, children, discarded, slotValidator, elementRef);
3779
+ return slotResult ?? renderIntrinsic(state, runtime, slots, elementRef);
3780
+ }
3781
+
3782
+ // ../../adapters/vue/src/to-vue-factory-options.ts
3783
+ var VUE_FIELD_VALIDATORS = {
3784
+ filterProps: (v) => v === void 0 || isFunction(v)
3785
+ };
3786
+ function isVueFactoryOptions(options) {
3787
+ return isFactoryOptionsLike(options, VUE_FIELD_VALIDATORS);
3726
3788
  }
3727
3789
 
3728
3790
  // ../../adapters/vue/src/create-contract-component.ts
3729
3791
  function createContractComponent(options) {
3792
+ invariant(isVueFactoryOptions(options), "options is not a valid VueFactoryOptions object");
3730
3793
  const bundle = buildRuntime(options);
3794
+ const { onElement } = options;
3731
3795
  const Component = defineComponent2({
3732
3796
  // normalizeOptions always supplies `name`, so displayName is always defined here —
3733
3797
  // the fallback only satisfies the type, which allows it to be absent in general.
@@ -3737,15 +3801,33 @@ function createContractComponent(options) {
3737
3801
  const state = computed(
3738
3802
  () => prepareRenderState(bundle.runtime, attrs, bundle.filterProps)
3739
3803
  );
3740
- return () => render({ ...bundle, state: state.value, slots });
3804
+ let cleanup;
3805
+ let boundElement = null;
3806
+ const onElementRef = onElement ? (element) => {
3807
+ if (element === boundElement) return;
3808
+ if (boundElement) {
3809
+ cleanup?.();
3810
+ cleanup = void 0;
3811
+ }
3812
+ boundElement = element;
3813
+ if (element) {
3814
+ cleanup = onElement(element, () => attrs) ?? void 0;
3815
+ }
3816
+ } : void 0;
3817
+ return () => render({ ...bundle, state: state.value, slots, elementRef: onElementRef });
3741
3818
  }
3742
3819
  });
3743
3820
  applyDisplayName(Component, options.name);
3744
- const defaultTag = bundle.runtime.options.defaultTag;
3745
- if (typeof defaultTag === "string") {
3746
- Object.assign(Component, { [COMPONENT_DEFAULT_TAG]: defaultTag });
3747
- }
3748
- return Component;
3821
+ const assembled = finalizeComponent(
3822
+ Component,
3823
+ bundle.runtime.options.defaultTag,
3824
+ options.subComponents
3825
+ );
3826
+ invariant(
3827
+ isPolymorphicComponent(assembled),
3828
+ "Generated component failed to satisfy the PolymorphicComponent shape"
3829
+ );
3830
+ return assembled;
3749
3831
  }
3750
3832
  export {
3751
3833
  Slottable,
@@ -4,6 +4,8 @@ import { Diagnostics, DiagnosticInput, DiagnosticsMode } from '../_shared/diagno
4
4
  type StringMap<T = unknown> = Record<string, T>;
5
5
  type AnyRecord = StringMap<unknown>;
6
6
  type EmptyRecord = Record<never, never>;
7
+ /** A compound component's named sub-components, e.g. `{ Header, Content, Footer }`. */
8
+ type SubComponentMap = Readonly<AnyRecord>;
7
9
 
8
10
  type IntrinsicTag = keyof HTMLElementTagNameMap;
9
11
 
@@ -288,6 +290,30 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
288
290
  * be set directly by component authors — use `enforcement.diagnostics` to override per component.
289
291
  */
290
292
  readonly diagnostics?: Diagnostics;
293
+ /**
294
+ * Sub-components to attach to the generated root component, producing a
295
+ * compound component API (for example, `Card.Header`, `Card.Content`,
296
+ * and `Card.Footer`). Purely additive — has no effect on
297
+ * `enforcement.children`; author child rules explicitly if the component
298
+ * needs to validate its children.
299
+ */
300
+ readonly subComponents?: SubComponentMap;
301
+ /**
302
+ * Called once per instance, when the real underlying DOM element first
303
+ * exists, in every adapter — via that adapter's own native mount
304
+ * lifecycle, never through the props/attribute pipeline. Use this for
305
+ * wiring that needs the actual element (native imperative methods like
306
+ * `dialogEl.showModal()`, native events like `close`/`cancel` that have
307
+ * no prop-based equivalent), not for anything expressible as a plain
308
+ * prop.
309
+ *
310
+ * `getProps` returns the instance's *current* resolved props at call
311
+ * time — read it from inside a listener registered once at mount, rather
312
+ * than re-subscribing on every prop change.
313
+ *
314
+ * Return a cleanup function to run when the instance unmounts.
315
+ */
316
+ readonly onElement?: (element: Element, getProps: () => Readonly<Props>) => void | (() => void);
291
317
  };
292
318
 
293
319
  type FilterPredicate = (key: string, variantKeys: ReadonlySet<string>) => boolean;
@@ -352,7 +378,9 @@ type WebContractComponent<TVariants extends Readonly<VariantMap> = Readonly<Empt
352
378
  * For non-reactive attributes (`aria-*`, `role`, `data-*`) call `element.update()`
353
379
  * after setting them to trigger an explicit pipeline re-run.
354
380
  */
355
- declare function createContractComponent<TDefault extends ElementType, TProps extends UnknownProps = EmptyRecord, TVariants extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<TVariants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory>(options: WebFactoryOptions<TDefault, TProps, TVariants, TPreset, TPlugin>): WebContractComponent<TVariants, ExtractPluginProps<TPlugin>>;
381
+ declare function createContractComponent<TDefault extends ElementType, TProps extends UnknownProps = EmptyRecord, TVariants extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<TVariants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: WebFactoryOptions<TDefault, TProps, TVariants, TPreset, TPlugin> & {
382
+ readonly subComponents?: TSubComponents;
383
+ }): WebContractComponent<TVariants, ExtractPluginProps<TPlugin>> & TSubComponents;
356
384
 
357
385
  /**
358
386
  * Renders a praxis-kit web component to an HTML string without requiring a DOM.
package/dist/web/index.js CHANGED
@@ -1,8 +1,22 @@
1
+ // ../../lib/adapter-utils/src/invariant.ts
2
+ function panic(message) {
3
+ throw new Error(message);
4
+ }
5
+ function invariant(condition, message) {
6
+ if (!condition) panic(message);
7
+ }
8
+
1
9
  // ../../lib/adapter-utils/src/runtime/define-component.ts
2
10
  function defineContractComponent(options) {
3
11
  return (factory) => factory(options);
4
12
  }
5
13
 
14
+ // ../../lib/adapter-utils/src/runtime/assemble-compound-component.ts
15
+ function assembleCompoundComponent(root, subComponents) {
16
+ if (!subComponents) return root;
17
+ return Object.assign(root, subComponents);
18
+ }
19
+
6
20
  // ../../lib/primitive/src/tag/resolve-tag.ts
7
21
  function makeResolveTag(defaultTag) {
8
22
  return function tag(as) {
@@ -45,10 +59,13 @@ function isString(value) {
45
59
  function isNumber(value) {
46
60
  return typeof value === "number";
47
61
  }
62
+ function isFunction(value) {
63
+ return typeof value === "function";
64
+ }
48
65
 
49
66
  // ../../lib/primitive/src/rule/is-dynamic-rule.ts
50
67
  function isDynamicRule(rule) {
51
- return isObject(rule, true) && rule[RULE_BRAND] === true;
68
+ return isObject(rule, true) && Reflect.get(rule, RULE_BRAND) === true;
52
69
  }
53
70
 
54
71
  // ../../lib/primitive/src/rule/resolve-rule.ts
@@ -587,17 +604,17 @@ var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default
587
604
  // ../../lib/primitive/src/guards/children/is-tag.ts
588
605
  function getAsProp(child) {
589
606
  if (!isObject(child) || !("props" in child)) return void 0;
590
- const props = child.props;
607
+ const { props } = child;
591
608
  if (!isObject(props)) return void 0;
592
- const as = props.as;
609
+ const as = Reflect.get(props, "as");
593
610
  return isString(as) && as !== "" ? as : void 0;
594
611
  }
595
612
  function getTag(child) {
596
613
  if (!isObject(child) || !("type" in child)) return void 0;
597
- const t = child.type;
614
+ const { type: t } = child;
598
615
  if (isString(t)) return t;
599
616
  if (typeof t === "function" || isObject(t)) {
600
- const defaultTag = t[COMPONENT_DEFAULT_TAG];
617
+ const defaultTag = Reflect.get(t, COMPONENT_DEFAULT_TAG);
601
618
  if (!isString(defaultTag)) return void 0;
602
619
  return getAsProp(child) ?? defaultTag;
603
620
  }
@@ -617,6 +634,28 @@ function isTag(...args) {
617
634
  return tag !== void 0 && set2.has(tag);
618
635
  }
619
636
 
637
+ // ../../lib/adapter-utils/src/runtime/is-factory-options-like.ts
638
+ var FACTORY_OPTIONS_FIELD_VALIDATORS = {
639
+ tag: (v) => v === void 0 || isString(v),
640
+ name: (v) => v === void 0 || isString(v),
641
+ defaults: (v) => v === void 0 || isObject(v),
642
+ normalize: (v) => v === void 0 || isFunction(v),
643
+ styling: (v) => v === void 0 || isObject(v),
644
+ enforcement: (v) => v === void 0 || isObject(v),
645
+ diagnostics: (v) => v === void 0 || isObject(v),
646
+ subComponents: (v) => v === void 0 || isObject(v),
647
+ onElement: (v) => v === void 0 || isFunction(v)
648
+ };
649
+ function isFactoryOptionsLike(options, extraFieldValidators) {
650
+ if (!isObject(options)) return false;
651
+ const validators = { ...FACTORY_OPTIONS_FIELD_VALIDATORS, ...extraFieldValidators };
652
+ for (const [key, value] of Object.entries(options)) {
653
+ const validate = validators[key];
654
+ if (!validate || !validate(value)) return false;
655
+ }
656
+ return true;
657
+ }
658
+
620
659
  // ../../lib/contract/src/aria/aria-role-policy.ts
621
660
  function getImplicitRole(tag, props) {
622
661
  if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
@@ -2721,7 +2760,7 @@ function getChildProp(child, key) {
2721
2760
  if (!isVNodeLike(child)) return void 0;
2722
2761
  const { props } = child;
2723
2762
  if (!isObject(props)) return void 0;
2724
- return props[key];
2763
+ return Reflect.get(props, key);
2725
2764
  }
2726
2765
 
2727
2766
  // ../core/src/html/contracts/aria/landmarks.ts
@@ -3526,6 +3565,14 @@ function buildRuntime(options) {
3526
3565
  };
3527
3566
  }
3528
3567
 
3568
+ // ../../adapters/web/src/is-web-contract-component.ts
3569
+ function isWebContractComponent(value) {
3570
+ if (!isFunction(value)) return false;
3571
+ if (typeof HTMLElement !== "undefined" && !(value.prototype instanceof HTMLElement)) return false;
3572
+ if (!("diagnostics" in value)) return false;
3573
+ return isObject(value.diagnostics);
3574
+ }
3575
+
3529
3576
  // ../../adapters/web/src/render-to-string.ts
3530
3577
  var ssrRegistry = /* @__PURE__ */ new WeakMap();
3531
3578
  function registerForSsr(cls, bundle) {
@@ -3542,8 +3589,17 @@ function renderToString(component, props = {}, innerHTML = "") {
3542
3589
  return renderBundleToString(entry.bundle, props, innerHTML);
3543
3590
  }
3544
3591
 
3592
+ // ../../adapters/web/src/to-web-factory-options.ts
3593
+ var WEB_FIELD_VALIDATORS = {
3594
+ filterProps: (v) => v === void 0 || isFunction(v)
3595
+ };
3596
+ function isWebFactoryOptions(options) {
3597
+ return isFactoryOptionsLike(options, WEB_FIELD_VALIDATORS);
3598
+ }
3599
+
3545
3600
  // ../../adapters/web/src/create-contract-component.ts
3546
3601
  function createContractComponent(options) {
3602
+ invariant(isWebFactoryOptions(options), "options is not a valid WebFactoryOptions object");
3547
3603
  const bundle = buildRuntime(options);
3548
3604
  const looseBundle = toLooseBundle(bundle);
3549
3605
  const variantKeys = options.styling?.variants ? Object.keys(options.styling.variants) : [];
@@ -3557,8 +3613,20 @@ function createContractComponent(options) {
3557
3613
  static get observedAttributes() {
3558
3614
  return observedAttrNames;
3559
3615
  }
3616
+ // Tag registered by the consumer's own customElements.define() call is never literally
3617
+ // `dialog` (custom-element names must be hyphenated), so native `<dialog>`-specific methods
3618
+ // like showModal() won't exist on this class unless the consumer opts into "customized
3619
+ // built-ins" (`{ extends: 'dialog' }`) themselves — not something this adapter special-cases.
3620
+ _onElementCleanup;
3560
3621
  connectedCallback() {
3561
3622
  this._applyPraxis();
3623
+ if (options.onElement) {
3624
+ this._onElementCleanup = options.onElement(this, () => this._buildProps()) ?? void 0;
3625
+ }
3626
+ }
3627
+ disconnectedCallback() {
3628
+ this._onElementCleanup?.();
3629
+ this._onElementCleanup = void 0;
3562
3630
  }
3563
3631
  // Fires synchronously for every observed attribute change — no microtask
3564
3632
  // scheduling needed. The guard is implicit: this only fires for
@@ -3575,12 +3643,11 @@ function createContractComponent(options) {
3575
3643
  get _self() {
3576
3644
  return this;
3577
3645
  }
3578
- _applyPraxis() {
3646
+ // Shared by _applyPraxis and the onElement getProps accessor — both need the same
3647
+ // attribute-derived prop snapshot, just for different purposes (pipeline input vs.
3648
+ // exposing "current props" to author-supplied element wiring).
3649
+ _buildProps() {
3579
3650
  const self = this._self;
3580
- const {
3581
- childrenEvaluator,
3582
- runtime: { options: options2 }
3583
- } = bundle;
3584
3651
  const observedSet = new Set(
3585
3652
  this.constructor.observedAttributes ?? []
3586
3653
  );
@@ -3596,6 +3663,14 @@ function createContractComponent(options) {
3596
3663
  const val = self[key] ?? this.getAttribute(key);
3597
3664
  if (val != null) props[key] = val;
3598
3665
  }
3666
+ return props;
3667
+ }
3668
+ _applyPraxis() {
3669
+ const {
3670
+ childrenEvaluator,
3671
+ runtime: { options: options2 }
3672
+ } = bundle;
3673
+ const props = this._buildProps();
3599
3674
  const hostState = resolveHostState(looseBundle, props);
3600
3675
  const children = Array.from(this.childNodes);
3601
3676
  if (childrenEvaluator) {
@@ -3612,8 +3687,14 @@ function createContractComponent(options) {
3612
3687
  Object.defineProperty(PolymorphicWebElement, "name", { value: options.name });
3613
3688
  }
3614
3689
  Object.defineProperty(PolymorphicWebElement, "diagnostics", { value: bundle.diagnostics });
3615
- registerForSsr(PolymorphicWebElement, looseBundle);
3616
- return PolymorphicWebElement;
3690
+ const contractClass = PolymorphicWebElement;
3691
+ invariant(
3692
+ isWebContractComponent(contractClass),
3693
+ "Generated class failed to satisfy the WebContractComponent shape"
3694
+ );
3695
+ registerForSsr(contractClass, looseBundle);
3696
+ const assembled = assembleCompoundComponent(contractClass, options.subComponents);
3697
+ return assembled;
3617
3698
  }
3618
3699
  export {
3619
3700
  createContractComponent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxis-kit",
3
- "version": "7.0.0",
3
+ "version": "7.3.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./react": {
@@ -187,7 +187,7 @@
187
187
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
188
188
  "esbuild": "^0.28.1",
189
189
  "esbuild-plugin-solid": "^0.6.0",
190
- "@types/node": "^26.1.1",
190
+ "@types/node": "^26.1.2",
191
191
  "@types/react": "^19.2.17",
192
192
  "@types/react-dom": "^19.0.0",
193
193
  "@typescript-eslint/utils": "8.65.0",
@@ -204,8 +204,8 @@
204
204
  "vite": "^8.1.5",
205
205
  "vue": "^3.5.40",
206
206
  "@praxis-kit/adapter-utils": "0.0.0",
207
- "@praxis-kit/diagnostics": "0.0.0",
208
207
  "@praxis-kit/core": "0.0.0",
208
+ "@praxis-kit/diagnostics": "0.0.0",
209
209
  "@praxis-kit/pipeline": "0.0.0",
210
210
  "@praxis-kit/primitive": "0.0.0",
211
211
  "@praxis-kit/vite-plugin": "0.0.0"