praxis-kit 5.0.0 → 6.1.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.
@@ -420,6 +420,19 @@ function makeResolveTag(defaultTag) {
420
420
  };
421
421
  }
422
422
 
423
+ // ../../lib/primitive/src/rule/rule-brand.ts
424
+ var RULE_BRAND = /* @__PURE__ */ Symbol("praxis-kit/dynamic-rule");
425
+
426
+ // ../../lib/primitive/src/rule/is-dynamic-rule.ts
427
+ function isDynamicRule(rule) {
428
+ return isObject(rule, true) && rule[RULE_BRAND] === true;
429
+ }
430
+
431
+ // ../../lib/primitive/src/rule/resolve-rule.ts
432
+ function resolveRule(rule, context) {
433
+ return isDynamicRule(rule) ? rule.resolve(context) : rule;
434
+ }
435
+
423
436
  // ../../lib/primitive/src/utils/assert-never.ts
424
437
  function assertNever(value) {
425
438
  throw new Error(`Unexpected value: ${String(value)}`);
@@ -601,6 +614,58 @@ var iterate = Object.freeze({
601
614
  values
602
615
  });
603
616
 
617
+ // ../../lib/primitive/src/utils/lazy.ts
618
+ function lazy(factory) {
619
+ let initialized = false;
620
+ let value;
621
+ return () => {
622
+ if (!initialized) {
623
+ value = factory();
624
+ initialized = true;
625
+ }
626
+ return value;
627
+ };
628
+ }
629
+
630
+ // ../../lib/primitive/src/utils/lru-cache.ts
631
+ var LRUCache = class {
632
+ #maxSize;
633
+ #store = /* @__PURE__ */ new Map();
634
+ constructor(maxSize) {
635
+ if (!Number.isInteger(maxSize) || maxSize < 1) {
636
+ throw new RangeError("LRUCache maxSize must be a positive integer.");
637
+ }
638
+ this.#maxSize = maxSize;
639
+ }
640
+ get(key) {
641
+ if (!this.#store.has(key)) return void 0;
642
+ const value = this.#store.get(key);
643
+ this.#store.delete(key);
644
+ this.#store.set(key, value);
645
+ return value;
646
+ }
647
+ set(key, value) {
648
+ this.#store.delete(key);
649
+ this.#store.set(key, value);
650
+ if (this.#store.size > this.#maxSize) {
651
+ const lru = this.#store.keys().next().value;
652
+ if (lru !== void 0) this.#store.delete(lru);
653
+ }
654
+ }
655
+ has(key) {
656
+ return this.#store.has(key);
657
+ }
658
+ delete(key) {
659
+ return this.#store.delete(key);
660
+ }
661
+ get size() {
662
+ return this.#store.size;
663
+ }
664
+ clear() {
665
+ this.#store.clear();
666
+ }
667
+ };
668
+
604
669
  // ../../lib/primitive/src/utils/merge-refs.ts
605
670
  function mergeRefsCore(...refs) {
606
671
  const active = refs.filter((r) => r != null);
@@ -662,28 +727,6 @@ function isTag(...args) {
662
727
  return tag !== void 0 && set2.has(tag);
663
728
  }
664
729
 
665
- // ../../lib/contract/src/strict/invariant-base.ts
666
- var InvariantBase = class {
667
- diagnostics;
668
- constructor(diagnostics) {
669
- this.diagnostics = diagnostics;
670
- }
671
- get active() {
672
- return this.diagnostics.active;
673
- }
674
- violate(input) {
675
- this.diagnostics.error(input);
676
- }
677
- // Always caps at warn — never throws. ARIA 'warning' violations route here
678
- // so they surface even in strict='throw' mode without aborting a render.
679
- warn(input) {
680
- this.diagnostics.warn(input);
681
- }
682
- invariant(condition, input) {
683
- if (!condition) this.violate(input);
684
- }
685
- };
686
-
687
730
  // ../../lib/contract/src/diagnostics/aria.ts
688
731
  import { DiagnosticCategory, DiagnosticCode } from "./_shared/diagnostics.js";
689
732
  var AriaDiagnostics = {
@@ -1040,6 +1083,28 @@ var SlotDiagnostics = {
1040
1083
  }
1041
1084
  };
1042
1085
 
1086
+ // ../../lib/contract/src/strict/invariant-base.ts
1087
+ var InvariantBase = class {
1088
+ diagnostics;
1089
+ constructor(diagnostics) {
1090
+ this.diagnostics = diagnostics;
1091
+ }
1092
+ get active() {
1093
+ return this.diagnostics.active;
1094
+ }
1095
+ violate(input) {
1096
+ this.diagnostics.error(input);
1097
+ }
1098
+ // Always caps at warn — never throws. ARIA 'warning' violations route here
1099
+ // so they surface even in strict='throw' mode without aborting a render.
1100
+ warn(input) {
1101
+ this.diagnostics.warn(input);
1102
+ }
1103
+ invariant(condition, input) {
1104
+ if (!condition) this.violate(input);
1105
+ }
1106
+ };
1107
+
1043
1108
  // ../../lib/contract/src/aria/polymorphic-validator.ts
1044
1109
  var VALID = [{ valid: true }];
1045
1110
  function isIntrinsicTag(tag) {
@@ -1051,8 +1116,7 @@ function omitProp(obj, key) {
1051
1116
  }
1052
1117
  var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1053
1118
  #extraRules;
1054
- #planCache = /* @__PURE__ */ new Map();
1055
- static #MAX_CACHE = 100;
1119
+ #planCache = new LRUCache(100);
1056
1120
  // Memoized AriaFix objects keyed by attribute name — the ARIA attribute set is
1057
1121
  // finite so this Map is bounded and avoids recreating closures on every cache miss.
1058
1122
  static #removeAttributeFixCache = /* @__PURE__ */ new Map();
@@ -1208,8 +1272,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1208
1272
  if (!isNull(key)) {
1209
1273
  const cached = this.#planCache.get(key);
1210
1274
  if (cached !== void 0) {
1211
- this.#planCache.delete(key);
1212
- this.#planCache.set(key, cached);
1213
1275
  if (cached.violations.length > 0) this.report(cached.violations);
1214
1276
  return {
1215
1277
  props: _AriaPolicyEngine.#applyPlan(props, cached.removals, cached.updates),
@@ -1226,10 +1288,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1226
1288
  );
1227
1289
  const plan = { removals, updates, violations: result.violations };
1228
1290
  this.#planCache.set(key, plan);
1229
- if (this.#planCache.size > _AriaPolicyEngine.#MAX_CACHE) {
1230
- const lru = this.#planCache.keys().next().value;
1231
- if (lru !== void 0) this.#planCache.delete(lru);
1232
- }
1233
1291
  }
1234
1292
  return result;
1235
1293
  }
@@ -1885,10 +1943,22 @@ var RuleValidator = class _RuleValidator extends InvariantBase {
1885
1943
 
1886
1944
  // ../../lib/contract/src/children/children-evaluator.ts
1887
1945
  var isTextLike = (child) => isString(child) || isNumber(child);
1946
+ function checkPositionCardinalityInvariant(rules, context) {
1947
+ iterate.forEach(rules, (rule) => {
1948
+ const { name, position, cardinality } = rule;
1949
+ if ((position === "first" || position === "last") && (cardinality.kind === "unbounded" || cardinality.max > 1)) {
1950
+ throw new RangeError(
1951
+ `ChildrenEvaluator [${context}]: rule "${name}" sets position="${position}" with an unbound or >1 max. position="first|last" implies max=1.`
1952
+ );
1953
+ }
1954
+ });
1955
+ }
1888
1956
  var ChildrenEvaluator = class extends InvariantBase {
1889
1957
  #context;
1890
- #rules;
1891
- #ruleNames;
1958
+ /** Rules with a static cardinality — normalized once, reused on every evaluate() call. */
1959
+ #staticRules;
1960
+ /** Rules whose cardinality is `dynamic(...)` — re-resolved against a ChildRuleContext per call. */
1961
+ #dynamicRuleInputs;
1892
1962
  #matcher;
1893
1963
  #ruleValidator;
1894
1964
  #exclusiveChildren;
@@ -1896,29 +1966,39 @@ var ChildrenEvaluator = class extends InvariantBase {
1896
1966
  constructor(rules, diagnostics, context = "Component", options = {}) {
1897
1967
  super(diagnostics);
1898
1968
  this.#context = context;
1899
- this.#rules = rules.map((r) => normalizeChildRule(r));
1900
- this.#ruleNames = this.#rules.map((r) => r.name);
1901
1969
  this.#exclusiveChildren = options.exclusiveChildren ?? false;
1902
1970
  this.#allowText = options.allowText ?? true;
1903
- iterate.forEach(this.#rules, (rule) => {
1904
- const { name, position, cardinality } = rule;
1905
- if ((position === "first" || position === "last") && (cardinality.kind === "unbounded" || cardinality.max > 1)) {
1906
- throw new RangeError(
1907
- `ChildrenEvaluator [${context}]: rule "${name}" sets position="${position}" with an unbound or >1 max. position="first|last" implies max=1.`
1908
- );
1909
- }
1971
+ const staticRuleInputs = [];
1972
+ const dynamicRuleInputs = [];
1973
+ iterate.forEach(rules, (rule) => {
1974
+ if (isDynamicRule(rule.cardinality)) dynamicRuleInputs.push(rule);
1975
+ else staticRuleInputs.push(rule);
1910
1976
  });
1911
- this.#matcher = new RuleMatcher(this.#rules);
1977
+ this.#dynamicRuleInputs = dynamicRuleInputs;
1978
+ this.#staticRules = staticRuleInputs.map(
1979
+ (r) => normalizeChildRule(r)
1980
+ );
1912
1981
  this.#ruleValidator = new RuleValidator(context, diagnostics);
1982
+ if (this.#dynamicRuleInputs.length === 0) {
1983
+ checkPositionCardinalityInvariant(this.#staticRules, context);
1984
+ this.#matcher = new RuleMatcher(this.#staticRules);
1985
+ } else {
1986
+ this.#matcher = void 0;
1987
+ }
1913
1988
  }
1914
- evaluate(children) {
1989
+ /**
1990
+ * @param context Required when any rule has a `dynamic(...)` cardinality —
1991
+ * supplies the resolved tag/props those rules are evaluated against.
1992
+ */
1993
+ evaluate(children, context) {
1915
1994
  if (!this.active) return;
1995
+ const { rules, matcher } = this.#resolveRules(context);
1916
1996
  const {
1917
1997
  matrix,
1918
1998
  unexpectedIndices: rawUnexpectedIndices,
1919
1999
  ambiguousIndices
1920
- } = this.#matcher.match(children);
1921
- this.#ruleValidator.validate(this.#rules, matrix, children.length);
2000
+ } = matcher.match(children);
2001
+ this.#ruleValidator.validate(rules, matrix, children.length);
1922
2002
  const unexpectedIndices = new Set(
1923
2003
  [...rawUnexpectedIndices].filter(
1924
2004
  (ci) => isTextLike(children[ci]) ? this.#allowText === false : this.#exclusiveChildren
@@ -1932,11 +2012,28 @@ var ChildrenEvaluator = class extends InvariantBase {
1932
2012
  this.violate(ContractDiagnostics.unexpectedChild(typeName, ci, this.#context));
1933
2013
  } else {
1934
2014
  const matches = matrix.childToRules.forward.get(ci);
1935
- const names = [...matches].map((ri) => this.#ruleNames[ri] ?? `#${ri}`);
2015
+ const names = [...matches].map((ri) => rules[ri]?.name ?? `#${ri}`);
1936
2016
  this.violate(ContractDiagnostics.ambiguousChild(typeName, ci, names, this.#context));
1937
2017
  }
1938
2018
  });
1939
2019
  }
2020
+ #resolveRules(context) {
2021
+ if (this.#dynamicRuleInputs.length === 0) {
2022
+ return { rules: this.#staticRules, matcher: this.#matcher };
2023
+ }
2024
+ if (context === void 0) {
2025
+ throw new RangeError(
2026
+ `ChildrenEvaluator [${this.#context}]: rule(s) have a dynamic(...) cardinality \u2014 evaluate() requires a context argument to resolve them.`
2027
+ );
2028
+ }
2029
+ const resolvedDynamicRules = this.#dynamicRuleInputs.map((r) => {
2030
+ const cardinality = resolveRule(r.cardinality, context);
2031
+ return normalizeChildRule({ ...r, cardinality });
2032
+ });
2033
+ checkPositionCardinalityInvariant(resolvedDynamicRules, this.#context);
2034
+ const rules = [...this.#staticRules, ...resolvedDynamicRules];
2035
+ return { rules, matcher: new RuleMatcher(rules) };
2036
+ }
1940
2037
  };
1941
2038
 
1942
2039
  // ../../lib/contract/src/props/get-disabled-props.ts
@@ -2265,7 +2362,7 @@ function cva2(base, config) {
2265
2362
  // ../../lib/styling/src/static-class-resolver.ts
2266
2363
  var StaticClassResolver = class {
2267
2364
  #baseClass;
2268
- #cache = /* @__PURE__ */ new Map();
2365
+ #cache = new LRUCache(200);
2269
2366
  #resolveTag;
2270
2367
  constructor(baseClass, tagMap) {
2271
2368
  this.#baseClass = Array.isArray(baseClass) ? baseClass.join(" ") : baseClass;
@@ -2279,17 +2376,9 @@ var StaticClassResolver = class {
2279
2376
  resolve(tag, skipTagMap = false) {
2280
2377
  if (typeof tag !== "string" || skipTagMap) return this.#baseClass;
2281
2378
  const cached = this.#cache.get(tag);
2282
- if (cached !== void 0) {
2283
- this.#cache.delete(tag);
2284
- this.#cache.set(tag, cached);
2285
- return cached;
2286
- }
2379
+ if (cached !== void 0) return cached;
2287
2380
  const result = this.#resolveTag(tag);
2288
2381
  this.#cache.set(tag, result);
2289
- if (this.#cache.size > 200) {
2290
- const lru = this.#cache.keys().next().value;
2291
- if (lru !== void 0) this.#cache.delete(lru);
2292
- }
2293
2382
  return result;
2294
2383
  }
2295
2384
  };
@@ -2300,7 +2389,7 @@ var VariantClassResolver = class _VariantClassResolver {
2300
2389
  #recipeMap;
2301
2390
  #variantKeys;
2302
2391
  #precomputedClasses;
2303
- #cache = /* @__PURE__ */ new Map();
2392
+ #cache = new LRUCache(1e3);
2304
2393
  constructor(cvaFn, recipeMap, variantKeys, precomputedClasses) {
2305
2394
  this.#cvaFn = cvaFn ?? null;
2306
2395
  this.#recipeMap = Object.freeze(recipeMap ?? {});
@@ -2315,17 +2404,9 @@ var VariantClassResolver = class _VariantClassResolver {
2315
2404
  if (precomputed !== void 0) return precomputed;
2316
2405
  }
2317
2406
  const cached = this.#cache.get(cacheKey);
2318
- if (cached !== void 0) {
2319
- this.#cache.delete(cacheKey);
2320
- this.#cache.set(cacheKey, cached);
2321
- return cached;
2322
- }
2407
+ if (cached !== void 0) return cached;
2323
2408
  const result = this.#compute(props, recipe);
2324
2409
  this.#cache.set(cacheKey, result);
2325
- if (this.#cache.size > 1e3) {
2326
- const lru = this.#cache.keys().next().value;
2327
- if (lru !== void 0) this.#cache.delete(lru);
2328
- }
2329
2410
  return result;
2330
2411
  }
2331
2412
  #compute(props, recipe) {
@@ -2846,8 +2927,14 @@ function applyDisplayName(component, name) {
2846
2927
  // ../../adapters/react/src/shared/render.ts
2847
2928
  import { createElement as createElement2 } from "react";
2848
2929
  import { jsx as jsx2 } from "react/jsx-runtime";
2849
- function buildRenderState(tag, directives, props, className, children) {
2850
- const state = { tag, directives, props, className };
2930
+ function buildRenderState(tag, directives, props, normalizedProps, className, children) {
2931
+ const state = {
2932
+ tag,
2933
+ directives,
2934
+ props,
2935
+ normalizedProps,
2936
+ className
2937
+ };
2851
2938
  if (children !== void 0) state.children = children;
2852
2939
  return state;
2853
2940
  }
@@ -2872,7 +2959,7 @@ function prepareRenderState(runtime, props, filterProps) {
2872
2959
  ...as !== void 0 && { as },
2873
2960
  ...asChild !== void 0 && { asChild }
2874
2961
  };
2875
- return buildRenderState(tag, directives, filteredProps, resolvedClass, children);
2962
+ return buildRenderState(tag, directives, filteredProps, normalizedProps, resolvedClass, children);
2876
2963
  }
2877
2964
  function warnDiscardedChildren(originalChildren, normalizedChildren, validator) {
2878
2965
  if (!Array.isArray(originalChildren)) return;
@@ -2947,10 +3034,12 @@ function render({
2947
3034
  childrenEvaluator
2948
3035
  }) {
2949
3036
  const state = prepareRenderState(runtime, props, filterProps);
2950
- let cached;
2951
- const getNormalizedChildren = () => cached ??= normalizeChildren(state.children);
3037
+ const getNormalizedChildren = lazy(() => normalizeChildren(state.children));
2952
3038
  if (process.env.NODE_ENV !== "production") {
2953
- childrenEvaluator?.evaluate(getNormalizedChildren());
3039
+ childrenEvaluator?.evaluate(getNormalizedChildren(), {
3040
+ tag: state.tag,
3041
+ props: state.normalizedProps
3042
+ });
2954
3043
  runtime.options.htmlChildrenEvaluatorFn?.(state.tag)?.evaluate(getNormalizedChildren());
2955
3044
  }
2956
3045
  if (typeof props.render === "function") {
@@ -29,6 +29,26 @@ type MinMax = {
29
29
  };
30
30
  type CardinalityInput = Partial<MinMax>;
31
31
 
32
+ /**
33
+ * Resolved per-instance state available to a dynamic (`dynamic(...)`) child
34
+ * rule field — the same tag/props every adapter already computes before
35
+ * evaluating children, exposed so a rule can vary by them (e.g. cardinality
36
+ * that depends on the resolved `as` tag).
37
+ */
38
+ type ChildRuleContext = {
39
+ readonly tag: unknown;
40
+ readonly props: Readonly<AnyRecord>;
41
+ };
42
+
43
+ declare const RULE_BRAND: unique symbol;
44
+
45
+ type DynamicRule<T, C = unknown> = {
46
+ readonly [RULE_BRAND]: true;
47
+ resolve(context: C): T;
48
+ };
49
+
50
+ type Rule<T, C = unknown> = T | DynamicRule<T, C>;
51
+
32
52
  type ChildRuleMatch<T, U extends T = T> = (child: T) => child is U;
33
53
 
34
54
  type ChildRulePosition = 'first' | 'last' | 'any';
@@ -36,7 +56,13 @@ type ChildRulePosition = 'first' | 'last' | 'any';
36
56
  type ChildRuleInput<T = unknown, U extends T = T> = {
37
57
  name: string;
38
58
  match: ChildRuleMatch<T, U>;
39
- cardinality?: CardinalityInput;
59
+ /**
60
+ * Either a static cardinality, or `dynamic((ctx) => ...)` to derive it from
61
+ * the resolved tag/props (e.g. a different max depending on `as`). `match`
62
+ * stays static-only — it's already a function, so a dynamic wrapper would
63
+ * be indistinguishable from the predicate itself without one.
64
+ */
65
+ cardinality?: Rule<CardinalityInput, ChildRuleContext>;
40
66
  position?: ChildRulePosition;
41
67
  /**
42
68
  * Optional component-type reference for O(1) dispatch index.
@@ -30,6 +30,26 @@ type MinMax = {
30
30
  };
31
31
  type CardinalityInput = Partial<MinMax>;
32
32
 
33
+ /**
34
+ * Resolved per-instance state available to a dynamic (`dynamic(...)`) child
35
+ * rule field — the same tag/props every adapter already computes before
36
+ * evaluating children, exposed so a rule can vary by them (e.g. cardinality
37
+ * that depends on the resolved `as` tag).
38
+ */
39
+ type ChildRuleContext = {
40
+ readonly tag: unknown;
41
+ readonly props: Readonly<AnyRecord>;
42
+ };
43
+
44
+ declare const RULE_BRAND: unique symbol;
45
+
46
+ type DynamicRule<T, C = unknown> = {
47
+ readonly [RULE_BRAND]: true;
48
+ resolve(context: C): T;
49
+ };
50
+
51
+ type Rule<T, C = unknown> = T | DynamicRule<T, C>;
52
+
33
53
  type ChildRuleMatch<T, U extends T = T> = (child: T) => child is U;
34
54
 
35
55
  type ChildRulePosition = 'first' | 'last' | 'any';
@@ -37,7 +57,13 @@ type ChildRulePosition = 'first' | 'last' | 'any';
37
57
  type ChildRuleInput<T = unknown, U extends T = T> = {
38
58
  name: string;
39
59
  match: ChildRuleMatch<T, U>;
40
- cardinality?: CardinalityInput;
60
+ /**
61
+ * Either a static cardinality, or `dynamic((ctx) => ...)` to derive it from
62
+ * the resolved tag/props (e.g. a different max depending on `as`). `match`
63
+ * stays static-only — it's already a function, so a dynamic wrapper would
64
+ * be indistinguishable from the predicate itself without one.
65
+ */
66
+ cardinality?: Rule<CardinalityInput, ChildRuleContext>;
41
67
  position?: ChildRulePosition;
42
68
  /**
43
69
  * Optional component-type reference for O(1) dispatch index.