praxis-kit 6.1.0 → 6.1.1

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.
@@ -12,8 +12,11 @@ function isUndefined(value) {
12
12
  function isNull(value) {
13
13
  return value === null;
14
14
  }
15
+ function isNonNull(value) {
16
+ return value != null;
17
+ }
15
18
 
16
- // ../../lib/primitive/src/utils/is-object.ts
19
+ // ../../lib/primitive/src/utils/type-guards.ts
17
20
  function isObject(value, excludeArrays = false) {
18
21
  if (value === null || typeof value !== "object") return false;
19
22
  return excludeArrays ? !Array.isArray(value) : true;
@@ -24,16 +27,11 @@ function isString(value) {
24
27
  function isNumber(value) {
25
28
  return typeof value === "number";
26
29
  }
27
-
28
- // ../../lib/primitive/src/merge/constants.ts
29
- var EVENT_HANDLER_RE = /^on[A-Z]/;
30
-
31
- // ../../lib/primitive/src/merge/predicates.ts
32
30
  function isFunction(value) {
33
31
  return typeof value === "function";
34
32
  }
35
33
  function isPlainObject(value) {
36
- if (!isObject(value)) return false;
34
+ if (!isObject(value, true)) return false;
37
35
  const proto = Object.getPrototypeOf(value);
38
36
  return proto === Object.prototype || proto === null;
39
37
  }
@@ -420,6 +418,9 @@ function makeResolveTag(defaultTag) {
420
418
  };
421
419
  }
422
420
 
421
+ // ../../lib/primitive/src/merge/constants.ts
422
+ var EVENT_HANDLER_RE = /^on[A-Z]/;
423
+
423
424
  // ../../lib/primitive/src/rule/rule-brand.ts
424
425
  var RULE_BRAND = /* @__PURE__ */ Symbol("praxis-kit/dynamic-rule");
425
426
 
@@ -668,12 +669,12 @@ var LRUCache = class {
668
669
 
669
670
  // ../../lib/primitive/src/utils/merge-refs.ts
670
671
  function mergeRefsCore(...refs) {
671
- const active = refs.filter((r) => r != null);
672
- if (active.length === 0) return null;
673
- if (active.length === 1) return active[0];
672
+ const resolvedRefs = refs.filter((r) => r != null);
673
+ if (resolvedRefs.length === 0) return null;
674
+ if (resolvedRefs.length === 1) return resolvedRefs[0] ?? null;
674
675
  return (value) => {
675
- iterate.forEach(active, (ref) => {
676
- if (typeof ref === "function") {
676
+ iterate.forEach(resolvedRefs, (ref) => {
677
+ if (isFunction(ref)) {
677
678
  ref(value);
678
679
  } else {
679
680
  ref.current = value;
@@ -1106,7 +1107,7 @@ var InvariantBase = class {
1106
1107
  };
1107
1108
 
1108
1109
  // ../../lib/contract/src/aria/polymorphic-validator.ts
1109
- var VALID = [{ valid: true }];
1110
+ var NO_VIOLATIONS = [{ valid: true }];
1110
1111
  function isIntrinsicTag(tag) {
1111
1112
  return isString(tag);
1112
1113
  }
@@ -1148,17 +1149,20 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1148
1149
  static #deriveContext(tag, props) {
1149
1150
  if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
1150
1151
  const implicitRole = getImplicitRole(tag, props);
1151
- const hasRole2 = implicitRole != null || isString(props.role) && props.role.length > 0;
1152
+ const hasRole2 = isNonNull(implicitRole) || isString(props.role) && props.role.length > 0;
1152
1153
  if (!hasRole2) return { proceed: false, result: { props, violations: [] } };
1153
1154
  const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
1154
- if (normalized.normalized) return { proceed: false, result: normalized.result };
1155
- const effectiveRole = props.role ?? implicitRole;
1155
+ const workingProps = normalized.normalized ? normalized.result.props : props;
1156
+ const preExistingViolations = normalized.normalized ? normalized.result.violations : [];
1157
+ const effectiveRole = workingProps.role ?? implicitRole;
1156
1158
  return {
1157
1159
  proceed: true,
1158
1160
  tag,
1159
1161
  implicitRole,
1160
1162
  effectiveRole,
1161
- context: { tag, props, implicitRole, effectiveRole }
1163
+ props: workingProps,
1164
+ preExistingViolations,
1165
+ context: { tag, props: workingProps, implicitRole, effectiveRole }
1162
1166
  };
1163
1167
  }
1164
1168
  static #runRules(rules, context) {
@@ -1173,7 +1177,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1173
1177
  } = context;
1174
1178
  const { message, attribute, severity } = result;
1175
1179
  const resolvedMessage = message ?? result.diagnostic?.message;
1176
- const fallbackDiag = resolvedMessage == null ? AriaDiagnostics.invalidRole(role, tag) : void 0;
1180
+ const fallbackDiag = isNonNull(resolvedMessage) ? void 0 : AriaDiagnostics.invalidRole(role, tag);
1177
1181
  violations.push({
1178
1182
  message: resolvedMessage ?? fallbackDiag.message,
1179
1183
  tag,
@@ -1181,8 +1185,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1181
1185
  attribute,
1182
1186
  severity,
1183
1187
  phase: "evaluate",
1184
- ...result.diagnostic != null && { diagnostic: result.diagnostic },
1185
- ...fallbackDiag != null && { diagnostic: fallbackDiag }
1188
+ ...isNonNull(result.diagnostic) && { diagnostic: result.diagnostic },
1189
+ ...isNonNull(fallbackDiag) && { diagnostic: fallbackDiag }
1186
1190
  });
1187
1191
  if (result.fixable) fixes.push(result.fix);
1188
1192
  });
@@ -1190,7 +1194,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1190
1194
  return { violations, fixes };
1191
1195
  }
1192
1196
  static #getRules(context) {
1193
- if (_AriaPolicyEngine.#hasRole(context.props) || context.effectiveRole != null && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1197
+ if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1194
1198
  return _AriaPolicyEngine.#pipeline;
1195
1199
  }
1196
1200
  return _AriaPolicyEngine.#implicitOnlyRules;
@@ -1198,24 +1202,36 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1198
1202
  static evaluate(tag, props) {
1199
1203
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1200
1204
  if (!derived.proceed) return derived.result;
1201
- const { tag: narrowedTag, implicitRole, context } = derived;
1205
+ const {
1206
+ tag: narrowedTag,
1207
+ implicitRole,
1208
+ context,
1209
+ props: workingProps,
1210
+ preExistingViolations
1211
+ } = derived;
1202
1212
  const { violations, fixes } = _AriaPolicyEngine.#runRules(
1203
1213
  _AriaPolicyEngine.#getRules(context),
1204
1214
  context
1205
1215
  );
1206
- const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
1207
- return { props: next, violations };
1216
+ const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1217
+ return { props: next, violations: [...preExistingViolations, ...violations] };
1208
1218
  }
1209
1219
  static #evaluateWithRules(tag, props, extraRules) {
1210
1220
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1211
1221
  if (!derived.proceed) return derived.result;
1212
- const { tag: narrowedTag, implicitRole, context } = derived;
1222
+ const {
1223
+ tag: narrowedTag,
1224
+ implicitRole,
1225
+ context,
1226
+ props: workingProps,
1227
+ preExistingViolations
1228
+ } = derived;
1213
1229
  const { violations, fixes } = _AriaPolicyEngine.#runRules(
1214
1230
  [..._AriaPolicyEngine.#getRules(context), ...extraRules],
1215
1231
  context
1216
1232
  );
1217
- const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
1218
- return { props: next, violations };
1233
+ const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1234
+ return { props: next, violations: [...preExistingViolations, ...violations] };
1219
1235
  }
1220
1236
  report(violations) {
1221
1237
  iterate.forEach(violations, (v) => {
@@ -1224,12 +1240,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1224
1240
  else this.warn(d);
1225
1241
  });
1226
1242
  }
1227
- // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs).
1228
- // Non-aria props (className, onClick, etc.) do not affect ARIA decisions and are excluded
1229
- // so cache hits survive re-renders that only change non-aria props.
1230
- // Note: #extraRules are NOT included in the key each engine instance has its own Map,
1231
- // so two engines with different rules never share cache entries. If caching ever becomes
1232
- // static/shared, rule identity would need to be folded into the key.
1243
+ // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs)
1244
+ // exactly what the built-in pipeline reads. Non-aria props (className, onClick, etc.) do
1245
+ // not affect built-in ARIA decisions and are excluded so cache hits survive re-renders
1246
+ // that only change non-aria props. This key alone is unsound for #extraRules, which may
1247
+ // read arbitrary props outside this set validate() extends it with #extraRulesKeySuffix,
1248
+ // or bypasses the cache, to account for that.
1233
1249
  static #createPlanKey(tag, props) {
1234
1250
  if (!isIntrinsicTag(tag)) return null;
1235
1251
  const parts = [tag];
@@ -1245,6 +1261,23 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1245
1261
  if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
1246
1262
  return parts.join("|");
1247
1263
  }
1264
+ // Extends the base cache key with the props each extra rule declares it reads (`readsProps`).
1265
+ // Returns null — meaning "don't cache" — if any extra rule omits `readsProps` (it may read
1266
+ // arbitrary props the key can't account for) or if a declared prop's value isn't a primitive
1267
+ // (object/array identity isn't stably representable in a string key).
1268
+ static #extraRulesKeySuffix(extraRules, props) {
1269
+ const parts = [];
1270
+ for (const rule of extraRules) {
1271
+ const readsProps = rule.readsProps;
1272
+ if (!isNonNull(readsProps)) return null;
1273
+ for (const propKey of readsProps) {
1274
+ const v = props[propKey];
1275
+ if (v !== void 0 && !isString(v) && !isNumber(v) && typeof v !== "boolean") return null;
1276
+ parts.push(`x:${propKey}:${String(v)}`);
1277
+ }
1278
+ }
1279
+ return parts.sort().join("|");
1280
+ }
1248
1281
  static #computePlan(inputProps, resultProps) {
1249
1282
  const removals = /* @__PURE__ */ new Set();
1250
1283
  const updates = {};
@@ -1268,7 +1301,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1268
1301
  return next;
1269
1302
  }
1270
1303
  validate(tag, props) {
1271
- const key = _AriaPolicyEngine.#createPlanKey(tag, props);
1304
+ const baseKey = _AriaPolicyEngine.#createPlanKey(tag, props);
1305
+ let key = baseKey;
1306
+ if (this.#extraRules.length > 0) {
1307
+ const suffix = _AriaPolicyEngine.#extraRulesKeySuffix(this.#extraRules, props);
1308
+ key = isNonNull(baseKey) && isNonNull(suffix) ? `${baseKey}|${suffix}` : null;
1309
+ }
1272
1310
  if (!isNull(key)) {
1273
1311
  const cached = this.#planCache.get(key);
1274
1312
  if (cached !== void 0) {
@@ -1358,7 +1396,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1358
1396
  implicitRole
1359
1397
  }) {
1360
1398
  const role = props.role;
1361
- if (!implicitRole || !role || role === implicitRole) return VALID;
1399
+ if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
1362
1400
  if (isStrongImplicitRole(tag) && role === "region") {
1363
1401
  return [
1364
1402
  {
@@ -1370,11 +1408,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1370
1408
  }
1371
1409
  ];
1372
1410
  }
1373
- return VALID;
1411
+ return NO_VIOLATIONS;
1374
1412
  }
1375
1413
  static #checkRedundantRole({ tag, props, implicitRole }) {
1376
1414
  const role = props.role;
1377
- if (!implicitRole || !role || role !== implicitRole) return VALID;
1415
+ if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
1378
1416
  return [
1379
1417
  {
1380
1418
  valid: false,
@@ -1387,8 +1425,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1387
1425
  }
1388
1426
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1389
1427
  const role = props.role;
1390
- if (role !== "region") return VALID;
1391
- if (!isStandaloneTag(tag)) return VALID;
1428
+ if (role !== "region") return NO_VIOLATIONS;
1429
+ if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
1392
1430
  return [
1393
1431
  {
1394
1432
  valid: false,
@@ -1404,7 +1442,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1404
1442
  props,
1405
1443
  effectiveRole
1406
1444
  }) {
1407
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1445
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1408
1446
  const results = [];
1409
1447
  iterate.forEachEntry(props, (key) => {
1410
1448
  if (!key.startsWith("aria-")) return;
@@ -1522,12 +1560,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1522
1560
  }
1523
1561
  }
1524
1562
  static #checkAriaAttributeValues({ props, effectiveRole }) {
1525
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1563
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1526
1564
  const results = [];
1527
1565
  iterate.forEachEntry(props, (key, value) => {
1528
1566
  if (!key.startsWith("aria-")) return;
1529
1567
  const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1530
- if (type == null) return;
1568
+ if (!isNonNull(type)) return;
1531
1569
  if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1532
1570
  results.push({
1533
1571
  valid: false,
@@ -1558,13 +1596,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1558
1596
  props,
1559
1597
  effectiveRole
1560
1598
  }) {
1561
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1599
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1562
1600
  const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1563
- if (implicitLevel == null) return VALID;
1601
+ if (!isNonNull(implicitLevel)) return NO_VIOLATIONS;
1564
1602
  const raw = props["aria-level"];
1565
- if (raw == null) return VALID;
1603
+ if (!isNonNull(raw)) return NO_VIOLATIONS;
1566
1604
  const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1567
- if (!Number.isFinite(n) || n !== implicitLevel) return VALID;
1605
+ if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS;
1568
1606
  return [
1569
1607
  {
1570
1608
  valid: false,
@@ -1587,9 +1625,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1587
1625
  props,
1588
1626
  effectiveRole
1589
1627
  }) {
1590
- if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole)) return VALID;
1591
- if ("aria-label" in props || "aria-labelledby" in props) return VALID;
1592
- if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return VALID;
1628
+ if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole))
1629
+ return NO_VIOLATIONS;
1630
+ if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS;
1631
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
1593
1632
  return [
1594
1633
  {
1595
1634
  valid: false,
@@ -1612,9 +1651,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1612
1651
  props,
1613
1652
  effectiveRole
1614
1653
  }) {
1615
- if (!effectiveRole) return VALID;
1654
+ if (!effectiveRole) return NO_VIOLATIONS;
1616
1655
  const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1617
- if (required == null) return VALID;
1656
+ if (!isNonNull(required)) return NO_VIOLATIONS;
1618
1657
  const results = [];
1619
1658
  iterate.forEach(required, (attr) => {
1620
1659
  if (attr in props) return;
@@ -1638,12 +1677,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1638
1677
  ]);
1639
1678
  // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1640
1679
  static #checkAriaHiddenOnFocusable({ tag, props }) {
1641
- if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return VALID;
1680
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS;
1642
1681
  const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1643
1682
  if (!isInteractive) {
1644
1683
  const tabindex = props.tabindex;
1645
1684
  const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1646
- if (!Number.isFinite(n) || n < 0) return VALID;
1685
+ if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS;
1647
1686
  }
1648
1687
  return [
1649
1688
  {
@@ -1662,7 +1701,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1662
1701
  props,
1663
1702
  effectiveRole
1664
1703
  }) {
1665
- if (effectiveRole !== "none" && effectiveRole !== "presentation") return VALID;
1704
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS;
1666
1705
  const results = [];
1667
1706
  iterate.forEachEntry(props, (key) => {
1668
1707
  if (!key.startsWith("aria-")) return;
@@ -1686,10 +1725,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1686
1725
  ["timer", "off"]
1687
1726
  ]);
1688
1727
  static #checkMissingLiveRegion({ effectiveRole, props }) {
1689
- if (!effectiveRole) return VALID;
1728
+ if (!effectiveRole) return NO_VIOLATIONS;
1690
1729
  const impliedLive = _AriaPolicyEngine.#LIVE_REGION_ROLES.get(effectiveRole);
1691
- if (!impliedLive) return VALID;
1692
- if ("aria-live" in props) return VALID;
1730
+ if (!impliedLive) return NO_VIOLATIONS;
1731
+ if ("aria-live" in props) return NO_VIOLATIONS;
1693
1732
  const injectLive = {
1694
1733
  kind: `injectLive:${effectiveRole}`,
1695
1734
  apply: (ctx) => ({
@@ -1709,8 +1748,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1709
1748
  ];
1710
1749
  }
1711
1750
  static #checkMissingAtomic({ effectiveRole, props }) {
1712
- if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole)) return VALID;
1713
- if ("aria-atomic" in props) return VALID;
1751
+ if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole))
1752
+ return NO_VIOLATIONS;
1753
+ if ("aria-atomic" in props) return NO_VIOLATIONS;
1714
1754
  return [
1715
1755
  {
1716
1756
  valid: false,
@@ -1734,8 +1774,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1734
1774
  };
1735
1775
  static #checkInvalidAriaRelevant({ props }) {
1736
1776
  const relevant = props["aria-relevant"];
1737
- if (relevant === void 0) return VALID;
1738
- if (typeof relevant !== "string") return VALID;
1777
+ if (relevant === void 0) return NO_VIOLATIONS;
1778
+ if (typeof relevant !== "string") return NO_VIOLATIONS;
1739
1779
  const tokens = relevant.trim().split(/\s+/);
1740
1780
  const invalid = tokens.filter((t) => !_AriaPolicyEngine.#VALID_RELEVANT_TOKENS.has(t));
1741
1781
  if (invalid.length > 0) {
@@ -1762,7 +1802,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1762
1802
  }
1763
1803
  ];
1764
1804
  }
1765
- return VALID;
1805
+ return NO_VIOLATIONS;
1766
1806
  }
1767
1807
  };
1768
1808
 
@@ -2463,7 +2503,7 @@ function createClassPipeline(resolved) {
2463
2503
  const staticClasses = staticResolver.resolve(tag, recipe !== void 0);
2464
2504
  const variantClasses = variantResolver.resolve({ props, recipe });
2465
2505
  if (!className)
2466
- return staticClasses && variantClasses ? `${staticClasses} ${variantClasses}` : staticClasses || variantClasses;
2506
+ return (staticClasses && variantClasses ? `${staticClasses} ${variantClasses}` : staticClasses || variantClasses) || void 0;
2467
2507
  return cn(staticClasses, variantClasses, className);
2468
2508
  };
2469
2509
  }
@@ -2674,7 +2714,7 @@ function createPolymorphic2(options = {}) {
2674
2714
  if (process.env.NODE_ENV !== "production") {
2675
2715
  validateRenderProps(resolved.diagnostics, resolved, props, recipe);
2676
2716
  }
2677
- return classPipeline(tag, props, className, recipe);
2717
+ return classPipeline(tag, props, className, recipe) || void 0;
2678
2718
  },
2679
2719
  resolveAria(tag, props) {
2680
2720
  return resolveAriaFn(tag, props);
@@ -138,7 +138,7 @@ interface BaseClassOptions {
138
138
  baseClassName?: ClassName;
139
139
  }
140
140
 
141
- type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string;
141
+ type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
142
142
 
143
143
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
144
144
  recipeMap?: Record<string, RecipeTarget<TVariants>>;
@@ -215,7 +215,9 @@ type AriaInvalidWithoutFix<M extends string = string> = AriaInvalidBase<M> & {
215
215
  type AriaInvalidResult<M extends string = string> = AriaInvalidWithFix<M> | AriaInvalidWithoutFix<M>;
216
216
  type AriaResult = ValidResult | AriaInvalidResult;
217
217
 
218
- type AriaRule<C extends AriaContext = AriaContext> = (context: C) => readonly AriaResult[];
218
+ type AriaRule<C extends AriaContext = AriaContext> = ((context: C) => readonly AriaResult[]) & {
219
+ readonly readsProps?: readonly string[];
220
+ };
219
221
 
220
222
  type PropNormalizer = (props: Readonly<AnyRecord & IntrinsicProps>) => Partial<AnyRecord & IntrinsicProps>;
221
223
 
@@ -244,7 +244,7 @@ var require_eslint_utils = __commonJS({
244
244
  // ../../plugins/eslint/src/rules/no-dead-compound.ts
245
245
  var import_eslint_utils = __toESM(require_eslint_utils(), 1);
246
246
 
247
- // ../../lib/primitive/src/utils/is-object.ts
247
+ // ../../lib/primitive/src/utils/type-guards.ts
248
248
  function isString(value) {
249
249
  return typeof value === "string";
250
250
  }
@@ -0,0 +1,36 @@
1
+ type StringMap<T = unknown> = Record<string, T>;
2
+ type AnyRecord = StringMap<unknown>;
3
+
4
+ /**
5
+ * Resolves the effective HTML tag for a vnode:
6
+ * - native element: returns its type string directly
7
+ * - praxis-kit component: resolves `as ?? defaultTag`, mirroring render-time logic
8
+ * - anything else: returns undefined
9
+ */
10
+ declare function getTag(child: unknown): string | undefined;
11
+ /**
12
+ * Checks whether a vnode resolves to one of the given intrinsic tag names.
13
+ * Matches native elements directly, and praxis-kit components by resolving
14
+ * `as ?? defaultTag` — the same logic used at render time.
15
+ *
16
+ * Two call forms are supported:
17
+ * isTag('img')(child) — curried, composable with Array#filter
18
+ * isTag(child, 'img') — direct, reads naturally in if-blocks
19
+ */
20
+ type TagChild = {
21
+ type: unknown;
22
+ };
23
+ declare function isTag(tag: string, ...tags: readonly string[]): (child: unknown) => child is TagChild;
24
+ declare function isTag(child: unknown, tag: string, ...tags: readonly string[]): boolean;
25
+ /**
26
+ * Checks whether a vnode is flow content per the HTML content model: text nodes
27
+ * (string/number) always qualify, and elements/components qualify unless their
28
+ * resolved tag is in the blocked set.
29
+ */
30
+ declare function isFlowContent(...blockedTags: readonly string[]): (child: unknown) => boolean;
31
+
32
+ declare function isObject(value: unknown, excludeArrays: true): value is AnyRecord;
33
+ declare function isObject(value: unknown, excludeArrays?: false): value is object;
34
+ declare function isString(value: unknown): value is string;
35
+
36
+ export { getTag, isFlowContent, isObject, isString, isTag };
@@ -0,0 +1,62 @@
1
+ // ../../lib/primitive/src/utils/type-guards.ts
2
+ function isObject(value, excludeArrays = false) {
3
+ if (value === null || typeof value !== "object") return false;
4
+ return excludeArrays ? !Array.isArray(value) : true;
5
+ }
6
+ function isString(value) {
7
+ return typeof value === "string";
8
+ }
9
+ function isNumber(value) {
10
+ return typeof value === "number";
11
+ }
12
+
13
+ // ../../lib/primitive/src/guards/children/component-id.ts
14
+ var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default-tag");
15
+
16
+ // ../../lib/primitive/src/guards/children/is-tag.ts
17
+ function getAsProp(child) {
18
+ if (!isObject(child) || !("props" in child)) return void 0;
19
+ const props = child.props;
20
+ if (!isObject(props)) return void 0;
21
+ const as = props.as;
22
+ return isString(as) && as !== "" ? as : void 0;
23
+ }
24
+ function getTag(child) {
25
+ if (!isObject(child) || !("type" in child)) return void 0;
26
+ const t = child.type;
27
+ if (isString(t)) return t;
28
+ if (typeof t === "function" || isObject(t)) {
29
+ const defaultTag = t[COMPONENT_DEFAULT_TAG];
30
+ if (!isString(defaultTag)) return void 0;
31
+ return getAsProp(child) ?? defaultTag;
32
+ }
33
+ return void 0;
34
+ }
35
+ function isTag(...args) {
36
+ if (isString(args[0])) {
37
+ const set2 = new Set(args);
38
+ return (child2) => {
39
+ const tag2 = getTag(child2);
40
+ return tag2 !== void 0 && set2.has(tag2);
41
+ };
42
+ }
43
+ const [child, ...tags] = args;
44
+ const set = new Set(tags);
45
+ const tag = getTag(child);
46
+ return tag !== void 0 && set.has(tag);
47
+ }
48
+ function isFlowContent(...blockedTags) {
49
+ const set = new Set(blockedTags);
50
+ return (child) => {
51
+ if (isString(child) || isNumber(child)) return true;
52
+ const tag = getTag(child);
53
+ return tag === void 0 || !set.has(tag);
54
+ };
55
+ }
56
+ export {
57
+ getTag,
58
+ isFlowContent,
59
+ isObject,
60
+ isString,
61
+ isTag
62
+ };
@@ -139,7 +139,7 @@ interface BaseClassOptions {
139
139
  baseClassName?: ClassName;
140
140
  }
141
141
 
142
- type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string;
142
+ type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
143
143
 
144
144
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
145
145
  recipeMap?: Record<string, RecipeTarget<TVariants>>;
@@ -217,7 +217,9 @@ type AriaInvalidWithoutFix<M extends string = string> = AriaInvalidBase<M> & {
217
217
  type AriaInvalidResult<M extends string = string> = AriaInvalidWithFix<M> | AriaInvalidWithoutFix<M>;
218
218
  type AriaResult = ValidResult | AriaInvalidResult;
219
219
 
220
- type AriaRule<C extends AriaContext = AriaContext> = (context: C) => readonly AriaResult[];
220
+ type AriaRule<C extends AriaContext = AriaContext> = ((context: C) => readonly AriaResult[]) & {
221
+ readonly readsProps?: readonly string[];
222
+ };
221
223
 
222
224
  type PropNormalizer = (props: Readonly<AnyRecord & IntrinsicProps>) => Partial<AnyRecord & IntrinsicProps>;
223
225