praxis-kit 6.1.0 → 6.2.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.
@@ -3,6 +3,16 @@ function defineContractComponent(options) {
3
3
  return (factory) => factory(options);
4
4
  }
5
5
 
6
+ // ../../lib/primitive/src/tag/resolve-tag.ts
7
+ function makeResolveTag(defaultTag) {
8
+ return function tag(as) {
9
+ return as ?? defaultTag;
10
+ };
11
+ }
12
+
13
+ // ../../lib/primitive/src/rule/rule-brand.ts
14
+ var RULE_BRAND = /* @__PURE__ */ Symbol("praxis-kit/dynamic-rule");
15
+
6
16
  // ../../lib/primitive/src/guards/foundational/is-defined.ts
7
17
  function isUndefined(value) {
8
18
  return value === void 0;
@@ -12,8 +22,11 @@ function isUndefined(value) {
12
22
  function isNull(value) {
13
23
  return value === null;
14
24
  }
25
+ function isNonNull(value) {
26
+ return value != null;
27
+ }
15
28
 
16
- // ../../lib/primitive/src/utils/is-object.ts
29
+ // ../../lib/primitive/src/utils/type-guards.ts
17
30
  function isObject(value, excludeArrays = false) {
18
31
  if (value === null || typeof value !== "object") return false;
19
32
  return excludeArrays ? !Array.isArray(value) : true;
@@ -25,6 +38,244 @@ function isNumber(value) {
25
38
  return typeof value === "number";
26
39
  }
27
40
 
41
+ // ../../lib/primitive/src/rule/is-dynamic-rule.ts
42
+ function isDynamicRule(rule) {
43
+ return isObject(rule, true) && rule[RULE_BRAND] === true;
44
+ }
45
+
46
+ // ../../lib/primitive/src/rule/resolve-rule.ts
47
+ function resolveRule(rule, context) {
48
+ return isDynamicRule(rule) ? rule.resolve(context) : rule;
49
+ }
50
+
51
+ // ../../lib/primitive/src/utils/assert-never.ts
52
+ function assertNever(value) {
53
+ throw new Error(`Unexpected value: ${String(value)}`);
54
+ }
55
+
56
+ // ../../lib/primitive/src/utils/cn.ts
57
+ import { clsx } from "clsx";
58
+ function cn(...inputs) {
59
+ return clsx(...inputs);
60
+ }
61
+
62
+ // ../../lib/primitive/src/utils/iterate.ts
63
+ function find(iterable, callback) {
64
+ for (const value of iterable) {
65
+ const result = callback(value);
66
+ if (result != null) {
67
+ return result;
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ function some(iterable, predicate) {
73
+ for (const value of iterable) {
74
+ if (predicate(value)) return true;
75
+ }
76
+ return false;
77
+ }
78
+ function every(iterable, predicate) {
79
+ let index = 0;
80
+ for (const value of iterable) {
81
+ if (!predicate(value, index++)) {
82
+ return false;
83
+ }
84
+ }
85
+ return true;
86
+ }
87
+ function* filter(iterable, predicate) {
88
+ let index = 0;
89
+ for (const value of iterable) {
90
+ if (predicate(value, index++)) {
91
+ yield value;
92
+ }
93
+ }
94
+ }
95
+ function* map(iterable, callback) {
96
+ let index = 0;
97
+ for (const value of iterable) {
98
+ yield callback(value, index++);
99
+ }
100
+ }
101
+ function forEach(iterable, callback) {
102
+ let index = 0;
103
+ for (const value of iterable) {
104
+ callback(value, index++);
105
+ }
106
+ }
107
+ function reduce(iterable, initial, callback) {
108
+ let accumulator = initial;
109
+ let index = 0;
110
+ for (const value of iterable) {
111
+ accumulator = callback(accumulator, value, index++);
112
+ }
113
+ return accumulator;
114
+ }
115
+ function collect(iterable, callback) {
116
+ const result = {};
117
+ let index = 0;
118
+ for (const value of iterable) {
119
+ const entry = callback(value, index++);
120
+ if (entry === null) {
121
+ return null;
122
+ }
123
+ result[entry[0]] = entry[1];
124
+ }
125
+ return result;
126
+ }
127
+ function findLast(value, callback) {
128
+ for (let index = value.length - 1; index >= 0; index--) {
129
+ const result = callback(value[index], index);
130
+ if (result != null) {
131
+ return result;
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+ function* items(collection) {
137
+ for (let i = 0; i < collection.length; i++) {
138
+ const item = collection.item(i);
139
+ if (item !== null) {
140
+ yield item;
141
+ }
142
+ }
143
+ }
144
+ function nodeList(list) {
145
+ return {
146
+ *[Symbol.iterator]() {
147
+ for (let i = 0; i < list.length; i++) {
148
+ const node = list.item(i);
149
+ if (node !== null) {
150
+ yield node;
151
+ }
152
+ }
153
+ }
154
+ };
155
+ }
156
+ function mapEntries(m) {
157
+ return m.entries();
158
+ }
159
+ function set(s) {
160
+ return s.values();
161
+ }
162
+ function hasOwn(object, key) {
163
+ return Object.hasOwn(object, key);
164
+ }
165
+ function* entries(object) {
166
+ for (const key in object) {
167
+ if (!hasOwn(object, key)) continue;
168
+ yield [key, object[key]];
169
+ }
170
+ }
171
+ function* keys(object) {
172
+ for (const [key] of entries(object)) {
173
+ yield key;
174
+ }
175
+ }
176
+ function* values(object) {
177
+ for (const [, value] of entries(object)) {
178
+ yield value;
179
+ }
180
+ }
181
+ function mapValues(object, callback) {
182
+ const result = {};
183
+ for (const [key, value] of entries(object)) {
184
+ result[key] = callback(value, key);
185
+ }
186
+ return result;
187
+ }
188
+ function forEachEntry(object, callback) {
189
+ for (const [key, value] of entries(object)) {
190
+ callback(key, value);
191
+ }
192
+ }
193
+ function forEachKey(object, callback) {
194
+ for (const key of keys(object)) {
195
+ callback(key);
196
+ }
197
+ }
198
+ function forEachValue(object, callback) {
199
+ for (const value of values(object)) {
200
+ callback(value);
201
+ }
202
+ }
203
+ function forEachSet(s, callback) {
204
+ for (const value of s) {
205
+ callback(value);
206
+ }
207
+ }
208
+ var iterate = Object.freeze({
209
+ entries,
210
+ filter,
211
+ find,
212
+ findLast,
213
+ forEach,
214
+ forEachEntry,
215
+ forEachKey,
216
+ forEachSet,
217
+ forEachValue,
218
+ items,
219
+ keys,
220
+ map,
221
+ mapEntries,
222
+ mapValues,
223
+ nodeList,
224
+ reduce,
225
+ collect,
226
+ set,
227
+ some,
228
+ every,
229
+ values
230
+ });
231
+
232
+ // ../../lib/primitive/src/utils/lru-cache.ts
233
+ var LRUCache = class {
234
+ #maxSize;
235
+ #store = /* @__PURE__ */ new Map();
236
+ constructor(maxSize) {
237
+ if (!Number.isInteger(maxSize) || maxSize < 1) {
238
+ throw new RangeError("LRUCache maxSize must be a positive integer.");
239
+ }
240
+ this.#maxSize = maxSize;
241
+ }
242
+ get(key) {
243
+ if (!this.#store.has(key)) return void 0;
244
+ const value = this.#store.get(key);
245
+ this.#store.delete(key);
246
+ this.#store.set(key, value);
247
+ return value;
248
+ }
249
+ set(key, value) {
250
+ this.#store.delete(key);
251
+ this.#store.set(key, value);
252
+ if (this.#store.size > this.#maxSize) {
253
+ const lru = this.#store.keys().next().value;
254
+ if (lru !== void 0) this.#store.delete(lru);
255
+ }
256
+ }
257
+ has(key) {
258
+ return this.#store.has(key);
259
+ }
260
+ delete(key) {
261
+ return this.#store.delete(key);
262
+ }
263
+ get size() {
264
+ return this.#store.size;
265
+ }
266
+ clear() {
267
+ this.#store.clear();
268
+ }
269
+ };
270
+
271
+ // ../../lib/primitive/src/utils/merge-props.ts
272
+ function mergeProps(defaultProps, props) {
273
+ return {
274
+ ...defaultProps ?? {},
275
+ ...props
276
+ };
277
+ }
278
+
28
279
  // ../../lib/primitive/src/constants/aria/global-aria-attributes.ts
29
280
  var GLOBAL_ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
30
281
  "aria-atomic",
@@ -260,298 +511,39 @@ var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
260
511
  [
261
512
  "aria-valuetext",
262
513
  /* @__PURE__ */ new Set(["meter", "progressbar", "scrollbar", "separator", "slider", "spinbutton"])
263
- ]
264
- ]);
265
-
266
- // ../../lib/primitive/src/guards/aria/is-aria-attribute.ts
267
- function isGlobalAriaAttribute(attr) {
268
- return GLOBAL_ARIA_ATTRIBUTES.has(attr);
269
- }
270
- function isAriaAttributeValidForRole(attr, role) {
271
- const allowedRoles = ROLE_RESTRICTED_ATTRIBUTES.get(attr);
272
- if (isUndefined(allowedRoles)) return true;
273
- if (isUndefined(role)) return false;
274
- return allowedRoles.has(role);
275
- }
276
-
277
- // ../../lib/primitive/src/guards/aria/is-aria-role.ts
278
- function isStrongImplicitRole(tag) {
279
- if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
280
- return STRONG_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
281
- }
282
- function isStandaloneTag(tag) {
283
- if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
284
- return STANDALONE_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
285
- }
286
- function getInputImplicitRole(type) {
287
- if (!isString(type) || !(type in INPUT_TYPE_ROLE_MAP)) return void 0;
288
- return INPUT_TYPE_ROLE_MAP[type];
289
- }
290
- function getConditionalImplicitRole(tag, ariaLabel, ariaLabelledBy) {
291
- const isNamed = isString(ariaLabel) || isString(ariaLabelledBy);
292
- if (!isNamed) return void 0;
293
- if (tag === "section") return "region";
294
- if (tag === "form") return "form";
295
- return void 0;
296
- }
297
-
298
- // ../../lib/contract/src/aria/aria-role-policy.ts
299
- function getImplicitRole(tag, props) {
300
- if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
301
- if (tag === "input") return getInputImplicitRole(props?.type);
302
- if (tag === "img") return props?.alt === "" ? "none" : "img";
303
- if (tag === "section" || tag === "form") {
304
- return getConditionalImplicitRole(tag, props?.["aria-label"], props?.["aria-labelledby"]);
305
- }
306
- return void 0;
307
- }
308
-
309
- // ../../lib/primitive/src/tag/resolve-tag.ts
310
- function makeResolveTag(defaultTag) {
311
- return function tag(as) {
312
- return as ?? defaultTag;
313
- };
314
- }
315
-
316
- // ../../lib/primitive/src/rule/rule-brand.ts
317
- var RULE_BRAND = /* @__PURE__ */ Symbol("praxis-kit/dynamic-rule");
318
-
319
- // ../../lib/primitive/src/rule/is-dynamic-rule.ts
320
- function isDynamicRule(rule) {
321
- return isObject(rule, true) && rule[RULE_BRAND] === true;
322
- }
323
-
324
- // ../../lib/primitive/src/rule/resolve-rule.ts
325
- function resolveRule(rule, context) {
326
- return isDynamicRule(rule) ? rule.resolve(context) : rule;
327
- }
328
-
329
- // ../../lib/primitive/src/utils/assert-never.ts
330
- function assertNever(value) {
331
- throw new Error(`Unexpected value: ${String(value)}`);
332
- }
333
-
334
- // ../../lib/primitive/src/utils/cn.ts
335
- import { clsx } from "clsx";
336
- function cn(...inputs) {
337
- return clsx(...inputs);
338
- }
514
+ ]
515
+ ]);
339
516
 
340
- // ../../lib/primitive/src/utils/iterate.ts
341
- function find(iterable, callback) {
342
- for (const value of iterable) {
343
- const result = callback(value);
344
- if (result != null) {
345
- return result;
346
- }
347
- }
348
- return null;
349
- }
350
- function some(iterable, predicate) {
351
- for (const value of iterable) {
352
- if (predicate(value)) return true;
353
- }
354
- return false;
355
- }
356
- function every(iterable, predicate) {
357
- let index = 0;
358
- for (const value of iterable) {
359
- if (!predicate(value, index++)) {
360
- return false;
361
- }
362
- }
363
- return true;
364
- }
365
- function* filter(iterable, predicate) {
366
- let index = 0;
367
- for (const value of iterable) {
368
- if (predicate(value, index++)) {
369
- yield value;
370
- }
371
- }
372
- }
373
- function* map(iterable, callback) {
374
- let index = 0;
375
- for (const value of iterable) {
376
- yield callback(value, index++);
377
- }
378
- }
379
- function forEach(iterable, callback) {
380
- let index = 0;
381
- for (const value of iterable) {
382
- callback(value, index++);
383
- }
384
- }
385
- function reduce(iterable, initial, callback) {
386
- let accumulator = initial;
387
- let index = 0;
388
- for (const value of iterable) {
389
- accumulator = callback(accumulator, value, index++);
390
- }
391
- return accumulator;
392
- }
393
- function collect(iterable, callback) {
394
- const result = {};
395
- let index = 0;
396
- for (const value of iterable) {
397
- const entry = callback(value, index++);
398
- if (entry === null) {
399
- return null;
400
- }
401
- result[entry[0]] = entry[1];
402
- }
403
- return result;
404
- }
405
- function findLast(value, callback) {
406
- for (let index = value.length - 1; index >= 0; index--) {
407
- const result = callback(value[index], index);
408
- if (result != null) {
409
- return result;
410
- }
411
- }
412
- return null;
413
- }
414
- function* items(collection) {
415
- for (let i = 0; i < collection.length; i++) {
416
- const item = collection.item(i);
417
- if (item !== null) {
418
- yield item;
419
- }
420
- }
421
- }
422
- function nodeList(list) {
423
- return {
424
- *[Symbol.iterator]() {
425
- for (let i = 0; i < list.length; i++) {
426
- const node = list.item(i);
427
- if (node !== null) {
428
- yield node;
429
- }
430
- }
431
- }
432
- };
433
- }
434
- function mapEntries(m) {
435
- return m.entries();
436
- }
437
- function set(s) {
438
- return s.values();
439
- }
440
- function hasOwn(object, key) {
441
- return Object.hasOwn(object, key);
442
- }
443
- function* entries(object) {
444
- for (const key in object) {
445
- if (!hasOwn(object, key)) continue;
446
- yield [key, object[key]];
447
- }
448
- }
449
- function* keys(object) {
450
- for (const [key] of entries(object)) {
451
- yield key;
452
- }
453
- }
454
- function* values(object) {
455
- for (const [, value] of entries(object)) {
456
- yield value;
457
- }
458
- }
459
- function mapValues(object, callback) {
460
- const result = {};
461
- for (const [key, value] of entries(object)) {
462
- result[key] = callback(value, key);
463
- }
464
- return result;
517
+ // ../../lib/primitive/src/guards/aria/is-aria-attribute.ts
518
+ function isGlobalAriaAttribute(attr) {
519
+ return GLOBAL_ARIA_ATTRIBUTES.has(attr);
465
520
  }
466
- function forEachEntry(object, callback) {
467
- for (const [key, value] of entries(object)) {
468
- callback(key, value);
469
- }
521
+ function isAriaAttributeValidForRole(attr, role) {
522
+ const allowedRoles = ROLE_RESTRICTED_ATTRIBUTES.get(attr);
523
+ if (isUndefined(allowedRoles)) return true;
524
+ if (isUndefined(role)) return false;
525
+ return allowedRoles.has(role);
470
526
  }
471
- function forEachKey(object, callback) {
472
- for (const key of keys(object)) {
473
- callback(key);
474
- }
527
+
528
+ // ../../lib/primitive/src/guards/aria/is-aria-role.ts
529
+ function isStrongImplicitRole(tag) {
530
+ if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
531
+ return STRONG_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
475
532
  }
476
- function forEachValue(object, callback) {
477
- for (const value of values(object)) {
478
- callback(value);
479
- }
533
+ function isStandaloneTag(tag) {
534
+ if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
535
+ return STANDALONE_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
480
536
  }
481
- function forEachSet(s, callback) {
482
- for (const value of s) {
483
- callback(value);
484
- }
537
+ function getInputImplicitRole(type) {
538
+ if (!isString(type) || !(type in INPUT_TYPE_ROLE_MAP)) return void 0;
539
+ return INPUT_TYPE_ROLE_MAP[type];
485
540
  }
486
- var iterate = Object.freeze({
487
- entries,
488
- filter,
489
- find,
490
- findLast,
491
- forEach,
492
- forEachEntry,
493
- forEachKey,
494
- forEachSet,
495
- forEachValue,
496
- items,
497
- keys,
498
- map,
499
- mapEntries,
500
- mapValues,
501
- nodeList,
502
- reduce,
503
- collect,
504
- set,
505
- some,
506
- every,
507
- values
508
- });
509
-
510
- // ../../lib/primitive/src/utils/lru-cache.ts
511
- var LRUCache = class {
512
- #maxSize;
513
- #store = /* @__PURE__ */ new Map();
514
- constructor(maxSize) {
515
- if (!Number.isInteger(maxSize) || maxSize < 1) {
516
- throw new RangeError("LRUCache maxSize must be a positive integer.");
517
- }
518
- this.#maxSize = maxSize;
519
- }
520
- get(key) {
521
- if (!this.#store.has(key)) return void 0;
522
- const value = this.#store.get(key);
523
- this.#store.delete(key);
524
- this.#store.set(key, value);
525
- return value;
526
- }
527
- set(key, value) {
528
- this.#store.delete(key);
529
- this.#store.set(key, value);
530
- if (this.#store.size > this.#maxSize) {
531
- const lru = this.#store.keys().next().value;
532
- if (lru !== void 0) this.#store.delete(lru);
533
- }
534
- }
535
- has(key) {
536
- return this.#store.has(key);
537
- }
538
- delete(key) {
539
- return this.#store.delete(key);
540
- }
541
- get size() {
542
- return this.#store.size;
543
- }
544
- clear() {
545
- this.#store.clear();
546
- }
547
- };
548
-
549
- // ../../lib/primitive/src/utils/merge-props.ts
550
- function mergeProps(defaultProps, props) {
551
- return {
552
- ...defaultProps ?? {},
553
- ...props
554
- };
541
+ function getConditionalImplicitRole(tag, ariaLabel, ariaLabelledBy) {
542
+ const isNamed = isString(ariaLabel) || isString(ariaLabelledBy);
543
+ if (!isNamed) return void 0;
544
+ if (tag === "section") return "region";
545
+ if (tag === "form") return "form";
546
+ return void 0;
555
547
  }
556
548
 
557
549
  // ../../lib/primitive/src/guards/children/component-id.ts
@@ -590,6 +582,17 @@ function isTag(...args) {
590
582
  return tag !== void 0 && set2.has(tag);
591
583
  }
592
584
 
585
+ // ../../lib/contract/src/aria/aria-role-policy.ts
586
+ function getImplicitRole(tag, props) {
587
+ if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
588
+ if (tag === "input") return getInputImplicitRole(props?.type);
589
+ if (tag === "img") return props?.alt === "" ? "none" : "img";
590
+ if (tag === "section" || tag === "form") {
591
+ return getConditionalImplicitRole(tag, props?.["aria-label"], props?.["aria-labelledby"]);
592
+ }
593
+ return void 0;
594
+ }
595
+
593
596
  // ../../lib/contract/src/diagnostics/aria.ts
594
597
  import { DiagnosticCategory, DiagnosticCode } from "../_shared/diagnostics.js";
595
598
  var AriaDiagnostics = {
@@ -969,7 +972,7 @@ var InvariantBase = class {
969
972
  };
970
973
 
971
974
  // ../../lib/contract/src/aria/polymorphic-validator.ts
972
- var VALID = [{ valid: true }];
975
+ var NO_VIOLATIONS = [{ valid: true }];
973
976
  function isIntrinsicTag(tag) {
974
977
  return isString(tag);
975
978
  }
@@ -1011,17 +1014,20 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1011
1014
  static #deriveContext(tag, props) {
1012
1015
  if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
1013
1016
  const implicitRole = getImplicitRole(tag, props);
1014
- const hasRole = implicitRole != null || isString(props.role) && props.role.length > 0;
1017
+ const hasRole = isNonNull(implicitRole) || isString(props.role) && props.role.length > 0;
1015
1018
  if (!hasRole) return { proceed: false, result: { props, violations: [] } };
1016
1019
  const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
1017
- if (normalized.normalized) return { proceed: false, result: normalized.result };
1018
- const effectiveRole = props.role ?? implicitRole;
1020
+ const workingProps = normalized.normalized ? normalized.result.props : props;
1021
+ const preExistingViolations = normalized.normalized ? normalized.result.violations : [];
1022
+ const effectiveRole = workingProps.role ?? implicitRole;
1019
1023
  return {
1020
1024
  proceed: true,
1021
1025
  tag,
1022
1026
  implicitRole,
1023
1027
  effectiveRole,
1024
- context: { tag, props, implicitRole, effectiveRole }
1028
+ props: workingProps,
1029
+ preExistingViolations,
1030
+ context: { tag, props: workingProps, implicitRole, effectiveRole }
1025
1031
  };
1026
1032
  }
1027
1033
  static #runRules(rules, context) {
@@ -1036,7 +1042,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1036
1042
  } = context;
1037
1043
  const { message, attribute, severity } = result;
1038
1044
  const resolvedMessage = message ?? result.diagnostic?.message;
1039
- const fallbackDiag = resolvedMessage == null ? AriaDiagnostics.invalidRole(role, tag) : void 0;
1045
+ const fallbackDiag = isNonNull(resolvedMessage) ? void 0 : AriaDiagnostics.invalidRole(role, tag);
1040
1046
  violations.push({
1041
1047
  message: resolvedMessage ?? fallbackDiag.message,
1042
1048
  tag,
@@ -1044,8 +1050,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1044
1050
  attribute,
1045
1051
  severity,
1046
1052
  phase: "evaluate",
1047
- ...result.diagnostic != null && { diagnostic: result.diagnostic },
1048
- ...fallbackDiag != null && { diagnostic: fallbackDiag }
1053
+ ...isNonNull(result.diagnostic) && { diagnostic: result.diagnostic },
1054
+ ...isNonNull(fallbackDiag) && { diagnostic: fallbackDiag }
1049
1055
  });
1050
1056
  if (result.fixable) fixes.push(result.fix);
1051
1057
  });
@@ -1053,7 +1059,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1053
1059
  return { violations, fixes };
1054
1060
  }
1055
1061
  static #getRules(context) {
1056
- if (_AriaPolicyEngine.#hasRole(context.props) || context.effectiveRole != null && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1062
+ if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1057
1063
  return _AriaPolicyEngine.#pipeline;
1058
1064
  }
1059
1065
  return _AriaPolicyEngine.#implicitOnlyRules;
@@ -1061,24 +1067,36 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1061
1067
  static evaluate(tag, props) {
1062
1068
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1063
1069
  if (!derived.proceed) return derived.result;
1064
- const { tag: narrowedTag, implicitRole, context } = derived;
1070
+ const {
1071
+ tag: narrowedTag,
1072
+ implicitRole,
1073
+ context,
1074
+ props: workingProps,
1075
+ preExistingViolations
1076
+ } = derived;
1065
1077
  const { violations, fixes } = _AriaPolicyEngine.#runRules(
1066
1078
  _AriaPolicyEngine.#getRules(context),
1067
1079
  context
1068
1080
  );
1069
- const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
1070
- return { props: next, violations };
1081
+ const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1082
+ return { props: next, violations: [...preExistingViolations, ...violations] };
1071
1083
  }
1072
1084
  static #evaluateWithRules(tag, props, extraRules) {
1073
1085
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1074
1086
  if (!derived.proceed) return derived.result;
1075
- const { tag: narrowedTag, implicitRole, context } = derived;
1087
+ const {
1088
+ tag: narrowedTag,
1089
+ implicitRole,
1090
+ context,
1091
+ props: workingProps,
1092
+ preExistingViolations
1093
+ } = derived;
1076
1094
  const { violations, fixes } = _AriaPolicyEngine.#runRules(
1077
1095
  [..._AriaPolicyEngine.#getRules(context), ...extraRules],
1078
1096
  context
1079
1097
  );
1080
- const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
1081
- return { props: next, violations };
1098
+ const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1099
+ return { props: next, violations: [...preExistingViolations, ...violations] };
1082
1100
  }
1083
1101
  report(violations) {
1084
1102
  iterate.forEach(violations, (v) => {
@@ -1087,12 +1105,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1087
1105
  else this.warn(d);
1088
1106
  });
1089
1107
  }
1090
- // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs).
1091
- // Non-aria props (className, onClick, etc.) do not affect ARIA decisions and are excluded
1092
- // so cache hits survive re-renders that only change non-aria props.
1093
- // Note: #extraRules are NOT included in the key each engine instance has its own Map,
1094
- // so two engines with different rules never share cache entries. If caching ever becomes
1095
- // static/shared, rule identity would need to be folded into the key.
1108
+ // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs)
1109
+ // exactly what the built-in pipeline reads. Non-aria props (className, onClick, etc.) do
1110
+ // not affect built-in ARIA decisions and are excluded so cache hits survive re-renders
1111
+ // that only change non-aria props. This key alone is unsound for #extraRules, which may
1112
+ // read arbitrary props outside this set validate() extends it with #extraRulesKeySuffix,
1113
+ // or bypasses the cache, to account for that.
1096
1114
  static #createPlanKey(tag, props) {
1097
1115
  if (!isIntrinsicTag(tag)) return null;
1098
1116
  const parts = [tag];
@@ -1108,6 +1126,23 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1108
1126
  if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
1109
1127
  return parts.join("|");
1110
1128
  }
1129
+ // Extends the base cache key with the props each extra rule declares it reads (`readsProps`).
1130
+ // Returns null — meaning "don't cache" — if any extra rule omits `readsProps` (it may read
1131
+ // arbitrary props the key can't account for) or if a declared prop's value isn't a primitive
1132
+ // (object/array identity isn't stably representable in a string key).
1133
+ static #extraRulesKeySuffix(extraRules, props) {
1134
+ const parts = [];
1135
+ for (const rule of extraRules) {
1136
+ const readsProps = rule.readsProps;
1137
+ if (!isNonNull(readsProps)) return null;
1138
+ for (const propKey of readsProps) {
1139
+ const v = props[propKey];
1140
+ if (v !== void 0 && !isString(v) && !isNumber(v) && typeof v !== "boolean") return null;
1141
+ parts.push(`x:${propKey}:${String(v)}`);
1142
+ }
1143
+ }
1144
+ return parts.sort().join("|");
1145
+ }
1111
1146
  static #computePlan(inputProps, resultProps) {
1112
1147
  const removals = /* @__PURE__ */ new Set();
1113
1148
  const updates = {};
@@ -1131,7 +1166,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1131
1166
  return next;
1132
1167
  }
1133
1168
  validate(tag, props) {
1134
- const key = _AriaPolicyEngine.#createPlanKey(tag, props);
1169
+ const baseKey = _AriaPolicyEngine.#createPlanKey(tag, props);
1170
+ let key = baseKey;
1171
+ if (this.#extraRules.length > 0) {
1172
+ const suffix = _AriaPolicyEngine.#extraRulesKeySuffix(this.#extraRules, props);
1173
+ key = isNonNull(baseKey) && isNonNull(suffix) ? `${baseKey}|${suffix}` : null;
1174
+ }
1135
1175
  if (!isNull(key)) {
1136
1176
  const cached = this.#planCache.get(key);
1137
1177
  if (cached !== void 0) {
@@ -1221,7 +1261,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1221
1261
  implicitRole
1222
1262
  }) {
1223
1263
  const role = props.role;
1224
- if (!implicitRole || !role || role === implicitRole) return VALID;
1264
+ if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
1225
1265
  if (isStrongImplicitRole(tag) && role === "region") {
1226
1266
  return [
1227
1267
  {
@@ -1233,11 +1273,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1233
1273
  }
1234
1274
  ];
1235
1275
  }
1236
- return VALID;
1276
+ return NO_VIOLATIONS;
1237
1277
  }
1238
1278
  static #checkRedundantRole({ tag, props, implicitRole }) {
1239
1279
  const role = props.role;
1240
- if (!implicitRole || !role || role !== implicitRole) return VALID;
1280
+ if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
1241
1281
  return [
1242
1282
  {
1243
1283
  valid: false,
@@ -1250,8 +1290,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1250
1290
  }
1251
1291
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1252
1292
  const role = props.role;
1253
- if (role !== "region") return VALID;
1254
- if (!isStandaloneTag(tag)) return VALID;
1293
+ if (role !== "region") return NO_VIOLATIONS;
1294
+ if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
1255
1295
  return [
1256
1296
  {
1257
1297
  valid: false,
@@ -1267,7 +1307,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1267
1307
  props,
1268
1308
  effectiveRole
1269
1309
  }) {
1270
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1310
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1271
1311
  const results = [];
1272
1312
  iterate.forEachEntry(props, (key) => {
1273
1313
  if (!key.startsWith("aria-")) return;
@@ -1385,12 +1425,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1385
1425
  }
1386
1426
  }
1387
1427
  static #checkAriaAttributeValues({ props, effectiveRole }) {
1388
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1428
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1389
1429
  const results = [];
1390
1430
  iterate.forEachEntry(props, (key, value) => {
1391
1431
  if (!key.startsWith("aria-")) return;
1392
1432
  const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1393
- if (type == null) return;
1433
+ if (!isNonNull(type)) return;
1394
1434
  if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1395
1435
  results.push({
1396
1436
  valid: false,
@@ -1421,13 +1461,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1421
1461
  props,
1422
1462
  effectiveRole
1423
1463
  }) {
1424
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1464
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1425
1465
  const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1426
- if (implicitLevel == null) return VALID;
1466
+ if (!isNonNull(implicitLevel)) return NO_VIOLATIONS;
1427
1467
  const raw = props["aria-level"];
1428
- if (raw == null) return VALID;
1468
+ if (!isNonNull(raw)) return NO_VIOLATIONS;
1429
1469
  const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1430
- if (!Number.isFinite(n) || n !== implicitLevel) return VALID;
1470
+ if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS;
1431
1471
  return [
1432
1472
  {
1433
1473
  valid: false,
@@ -1450,9 +1490,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1450
1490
  props,
1451
1491
  effectiveRole
1452
1492
  }) {
1453
- if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole)) return VALID;
1454
- if ("aria-label" in props || "aria-labelledby" in props) return VALID;
1455
- if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return VALID;
1493
+ if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole))
1494
+ return NO_VIOLATIONS;
1495
+ if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS;
1496
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
1456
1497
  return [
1457
1498
  {
1458
1499
  valid: false,
@@ -1475,9 +1516,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1475
1516
  props,
1476
1517
  effectiveRole
1477
1518
  }) {
1478
- if (!effectiveRole) return VALID;
1519
+ if (!effectiveRole) return NO_VIOLATIONS;
1479
1520
  const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1480
- if (required == null) return VALID;
1521
+ if (!isNonNull(required)) return NO_VIOLATIONS;
1481
1522
  const results = [];
1482
1523
  iterate.forEach(required, (attr) => {
1483
1524
  if (attr in props) return;
@@ -1501,12 +1542,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1501
1542
  ]);
1502
1543
  // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1503
1544
  static #checkAriaHiddenOnFocusable({ tag, props }) {
1504
- if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return VALID;
1545
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS;
1505
1546
  const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1506
1547
  if (!isInteractive) {
1507
1548
  const tabindex = props.tabindex;
1508
1549
  const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1509
- if (!Number.isFinite(n) || n < 0) return VALID;
1550
+ if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS;
1510
1551
  }
1511
1552
  return [
1512
1553
  {
@@ -1525,7 +1566,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1525
1566
  props,
1526
1567
  effectiveRole
1527
1568
  }) {
1528
- if (effectiveRole !== "none" && effectiveRole !== "presentation") return VALID;
1569
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS;
1529
1570
  const results = [];
1530
1571
  iterate.forEachEntry(props, (key) => {
1531
1572
  if (!key.startsWith("aria-")) return;
@@ -1549,10 +1590,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1549
1590
  ["timer", "off"]
1550
1591
  ]);
1551
1592
  static #checkMissingLiveRegion({ effectiveRole, props }) {
1552
- if (!effectiveRole) return VALID;
1593
+ if (!effectiveRole) return NO_VIOLATIONS;
1553
1594
  const impliedLive = _AriaPolicyEngine.#LIVE_REGION_ROLES.get(effectiveRole);
1554
- if (!impliedLive) return VALID;
1555
- if ("aria-live" in props) return VALID;
1595
+ if (!impliedLive) return NO_VIOLATIONS;
1596
+ if ("aria-live" in props) return NO_VIOLATIONS;
1556
1597
  const injectLive = {
1557
1598
  kind: `injectLive:${effectiveRole}`,
1558
1599
  apply: (ctx) => ({
@@ -1572,8 +1613,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1572
1613
  ];
1573
1614
  }
1574
1615
  static #checkMissingAtomic({ effectiveRole, props }) {
1575
- if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole)) return VALID;
1576
- if ("aria-atomic" in props) return VALID;
1616
+ if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole))
1617
+ return NO_VIOLATIONS;
1618
+ if ("aria-atomic" in props) return NO_VIOLATIONS;
1577
1619
  return [
1578
1620
  {
1579
1621
  valid: false,
@@ -1597,8 +1639,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1597
1639
  };
1598
1640
  static #checkInvalidAriaRelevant({ props }) {
1599
1641
  const relevant = props["aria-relevant"];
1600
- if (relevant === void 0) return VALID;
1601
- if (typeof relevant !== "string") return VALID;
1642
+ if (relevant === void 0) return NO_VIOLATIONS;
1643
+ if (typeof relevant !== "string") return NO_VIOLATIONS;
1602
1644
  const tokens = relevant.trim().split(/\s+/);
1603
1645
  const invalid = tokens.filter((t) => !_AriaPolicyEngine.#VALID_RELEVANT_TOKENS.has(t));
1604
1646
  if (invalid.length > 0) {
@@ -1625,7 +1667,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1625
1667
  }
1626
1668
  ];
1627
1669
  }
1628
- return VALID;
1670
+ return NO_VIOLATIONS;
1629
1671
  }
1630
1672
  };
1631
1673
 
@@ -2326,7 +2368,7 @@ function createClassPipeline(resolved) {
2326
2368
  const staticClasses = staticResolver.resolve(tag, recipe !== void 0);
2327
2369
  const variantClasses = variantResolver.resolve({ props, recipe });
2328
2370
  if (!className)
2329
- return staticClasses && variantClasses ? `${staticClasses} ${variantClasses}` : staticClasses || variantClasses;
2371
+ return (staticClasses && variantClasses ? `${staticClasses} ${variantClasses}` : staticClasses || variantClasses) || void 0;
2330
2372
  return cn(staticClasses, variantClasses, className);
2331
2373
  };
2332
2374
  }
@@ -2366,7 +2408,10 @@ function resolveFactoryOptions(options = {}) {
2366
2408
  const variantKeys = styling?.variants === void 0 ? EMPTY_VARIANT_KEYS : new Set(Object.keys(styling.variants));
2367
2409
  return Object.freeze({
2368
2410
  defaultTag: options.tag ?? "div",
2369
- diagnostics: resolveDiagnostics(enforcement?.diagnostics, silentDiagnostics),
2411
+ diagnostics: resolveDiagnostics(
2412
+ enforcement?.diagnostics,
2413
+ options.diagnostics ?? silentDiagnostics
2414
+ ),
2370
2415
  variantKeys,
2371
2416
  ...whenDefined("displayName", options.name),
2372
2417
  ...whenDefined("defaultProps", options.defaults),
@@ -2537,7 +2582,7 @@ function createPolymorphic2(options = {}) {
2537
2582
  if (process.env.NODE_ENV !== "production") {
2538
2583
  validateRenderProps(resolved.diagnostics, resolved, props, recipe);
2539
2584
  }
2540
- return classPipeline(tag, props, className, recipe);
2585
+ return classPipeline(tag, props, className, recipe) || void 0;
2541
2586
  },
2542
2587
  resolveAria(tag, props) {
2543
2588
  return resolveAriaFn(tag, props);