praxis-kit 3.1.0 → 4.0.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/lit/index.js CHANGED
@@ -1,58 +1,17 @@
1
1
  // ../../adapters/lit/src/create-contract-component.ts
2
2
  import { LitElement, html } from "lit";
3
3
 
4
- // ../../lib/adapter-utils/src/apply-filter.ts
5
- function applyFilter(props, filterProps, variantKeys) {
6
- const out = {};
7
- for (const k in props) {
8
- if (!Object.hasOwn(props, k)) continue;
9
- if (filterProps(k, variantKeys)) continue;
10
- out[k] = props[k];
11
- }
12
- return out;
13
- }
14
-
15
- // ../../lib/adapter-utils/src/define-component.ts
16
- function defineContractComponent(options) {
17
- return (factory) => factory(options);
18
- }
19
-
20
- // ../core/dist/chunk-KKSHJDE7.js
4
+ // ../../lib/primitive/src/tag/resolve-tag.ts
21
5
  function makeResolveTag(defaultTag) {
22
6
  return function tag(as) {
23
7
  return as ?? defaultTag;
24
8
  };
25
9
  }
26
- function assertNever(value) {
27
- throw new Error(`Unexpected value: ${String(value)}`);
28
- }
29
- function r(e) {
30
- var t, f, n = "";
31
- if ("string" == typeof e || "number" == typeof e) n += e;
32
- else if ("object" == typeof e) if (Array.isArray(e)) {
33
- var o = e.length;
34
- for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
35
- } else for (f in e) e[f] && (n && (n += " "), n += f);
36
- return n;
37
- }
38
- function clsx() {
39
- for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
40
- return n;
41
- }
42
- function cn(...inputs) {
43
- return clsx(...inputs);
44
- }
45
- function mergeProps(defaultProps, props) {
46
- return {
47
- ...defaultProps ?? {},
48
- ...props
49
- };
50
- }
51
- function isNull(value) {
52
- return value === null;
53
- }
54
- function isObject(value) {
55
- return !isNull(value) && typeof value === "object";
10
+
11
+ // ../../lib/primitive/src/utils/is-object.ts
12
+ function isObject(value, excludeArrays = false) {
13
+ if (value === null || typeof value !== "object") return false;
14
+ return excludeArrays ? !Array.isArray(value) : true;
56
15
  }
57
16
  function isString(value) {
58
17
  return typeof value === "string";
@@ -60,308 +19,197 @@ function isString(value) {
60
19
  function isNumber(value) {
61
20
  return typeof value === "number";
62
21
  }
63
- var EMPTY_VARIANT_KEYS = /* @__PURE__ */ new Set();
64
- function composeNormalizers(normalizers, fn) {
65
- if (!normalizers?.length) return fn;
66
- return ((props) => {
67
- const patched = normalizers.reduce(
68
- (acc, normalizer) => ({ ...acc, ...normalizer(acc) }),
69
- props
70
- );
71
- return fn ? fn(patched) : patched;
72
- });
22
+
23
+ // ../../lib/primitive/src/utils/assert-never.ts
24
+ function assertNever(value) {
25
+ throw new Error(`Unexpected value: ${String(value)}`);
73
26
  }
74
- function whenDefined(key, value) {
75
- return value === void 0 ? {} : { [key]: value };
27
+
28
+ // ../../lib/primitive/src/utils/cn.ts
29
+ import { clsx } from "clsx";
30
+ function cn(...inputs) {
31
+ return clsx(...inputs);
76
32
  }
77
- function resolveFactoryOptions(options = {}) {
78
- const { styling, enforcement } = options;
79
- const composedNormalizeFn = composeNormalizers(enforcement?.props, options.normalize);
80
- const variantKeys = styling?.variants === void 0 ? EMPTY_VARIANT_KEYS : new Set(Object.keys(styling.variants));
81
- return Object.freeze({
82
- defaultTag: options.tag ?? "div",
83
- strict: enforcement?.strict ?? false,
84
- variantKeys,
85
- ...whenDefined("displayName", options.name),
86
- ...whenDefined("defaultProps", options.defaults),
87
- ...whenDefined("baseClassName", styling?.base),
88
- ...whenDefined("tagMap", styling?.tags),
89
- ...whenDefined("recipeMap", styling?.presets),
90
- ...whenDefined("variants", styling?.variants),
91
- ...whenDefined("defaultVariants", styling?.defaults),
92
- ...whenDefined("compoundVariants", styling?.compounds),
93
- ...whenDefined("normalizeFn", composedNormalizeFn),
94
- ...whenDefined("ariaRules", enforcement?.aria),
95
- ...whenDefined("childRules", enforcement?.children),
96
- ...whenDefined("allowedAs", enforcement?.allowedAs),
97
- ...whenDefined("precomputedClasses", styling?.precomputedClasses)
98
- });
33
+
34
+ // ../../lib/primitive/src/utils/iterate.ts
35
+ function find(iterable, callback) {
36
+ for (const value of iterable) {
37
+ const result = callback(value);
38
+ if (result != null) {
39
+ return result;
40
+ }
41
+ }
42
+ return null;
99
43
  }
100
- function report(strict, message) {
101
- if (strict === false) return;
102
- const mode = strict === true ? "throw" : strict;
103
- if (mode === "throw") throw new Error(message);
104
- console.warn(message);
44
+ function some(iterable, predicate) {
45
+ for (const value of iterable) {
46
+ if (predicate(value)) return true;
47
+ }
48
+ return false;
105
49
  }
106
- function validateFactoryOptions(resolved) {
107
- const { strict } = resolved;
108
- if (strict === false) return;
109
- const name = resolved.displayName ?? "Component";
110
- const { variants } = resolved;
111
- const checkSelection = (label2, selection) => {
112
- const record = selection;
113
- for (const dim in record) {
114
- const value = record[dim];
115
- if (value === void 0 || value === null) continue;
116
- if (!variants || !Object.hasOwn(variants, dim)) {
117
- report(strict, `${name}: ${label2} references unknown variant "${dim}".`);
118
- continue;
119
- }
120
- const states = variants[dim];
121
- const stateKey = String(value);
122
- if (!Object.hasOwn(states, stateKey)) {
123
- report(
124
- strict,
125
- `${name}: ${label2} sets "${dim}" to unknown value "${stateKey}" (valid: ${Object.keys(states).join(", ")}).`
126
- );
127
- }
50
+ function every(iterable, predicate) {
51
+ let index = 0;
52
+ for (const value of iterable) {
53
+ if (!predicate(value, index++)) {
54
+ return false;
128
55
  }
129
- };
130
- const { recipeMap } = resolved;
131
- if (recipeMap) {
132
- for (const recipeKey in recipeMap) {
133
- checkSelection(`preset "${recipeKey}"`, recipeMap[recipeKey]);
56
+ }
57
+ return true;
58
+ }
59
+ function* filter(iterable, predicate) {
60
+ let index = 0;
61
+ for (const value of iterable) {
62
+ if (predicate(value, index++)) {
63
+ yield value;
134
64
  }
135
65
  }
136
- if (resolved.defaultVariants) checkSelection("defaults", resolved.defaultVariants);
137
66
  }
138
- var warned = /* @__PURE__ */ new Set();
139
- var pendingAsyncWarns = /* @__PURE__ */ new Set();
140
- var asyncWarnScheduled = false;
141
- function flushAsyncWarns() {
142
- asyncWarnScheduled = false;
143
- const messages = [...pendingAsyncWarns];
144
- pendingAsyncWarns.clear();
145
- for (const msg of messages) {
146
- console.warn(msg);
147
- }
148
- }
149
- function report2(strict, message) {
150
- if (!strict) return;
151
- const mode = strict === true ? "throw" : strict;
152
- if (mode === "throw") throw new Error(message);
153
- if (mode === "async-warn") {
154
- if (pendingAsyncWarns.has(message)) return;
155
- pendingAsyncWarns.add(message);
156
- if (!asyncWarnScheduled) {
157
- asyncWarnScheduled = true;
158
- queueMicrotask(flushAsyncWarns);
67
+ function* map(iterable, callback) {
68
+ let index = 0;
69
+ for (const value of iterable) {
70
+ yield callback(value, index++);
71
+ }
72
+ }
73
+ function forEach(iterable, callback) {
74
+ let index = 0;
75
+ for (const value of iterable) {
76
+ callback(value, index++);
77
+ }
78
+ }
79
+ function reduce(iterable, initial, callback) {
80
+ let accumulator = initial;
81
+ let index = 0;
82
+ for (const value of iterable) {
83
+ accumulator = callback(accumulator, value, index++);
84
+ }
85
+ return accumulator;
86
+ }
87
+ function collect(iterable, callback) {
88
+ const result = {};
89
+ let index = 0;
90
+ for (const value of iterable) {
91
+ const entry = callback(value, index++);
92
+ if (entry === null) {
93
+ return null;
159
94
  }
160
- return;
95
+ result[entry[0]] = entry[1];
161
96
  }
162
- if (warned.has(message)) return;
163
- warned.add(message);
164
- console.warn(message);
97
+ return result;
165
98
  }
166
- function label(name) {
167
- return name ? `[${name}]` : "[createContractComponent]";
99
+ function findLast(value, callback) {
100
+ for (let index = value.length - 1; index >= 0; index--) {
101
+ const result = callback(value[index], index);
102
+ if (result != null) {
103
+ return result;
104
+ }
105
+ }
106
+ return null;
168
107
  }
169
- function validateRenderProps(options, props, recipeKey) {
170
- const { strict, recipeMap, variants, displayName } = options;
171
- if (!strict) return;
172
- const tag = label(displayName);
173
- if (recipeKey !== void 0 && (!recipeMap || !Object.hasOwn(recipeMap, recipeKey))) {
174
- report2(strict, `${tag} Unknown recipeKey "${recipeKey}" \u2014 no preset with that name exists.`);
108
+ function* items(collection) {
109
+ for (let i = 0; i < collection.length; i++) {
110
+ const item = collection.item(i);
111
+ if (item !== null) {
112
+ yield item;
113
+ }
175
114
  }
176
- if (variants) {
177
- for (const key in variants) {
178
- if (!Object.hasOwn(props, key)) continue;
179
- const value = props[key];
180
- if (value === void 0 || value === null) continue;
181
- const dim = variants[key];
182
- if (dim && !Object.hasOwn(dim, String(value))) {
183
- report2(
184
- strict,
185
- `${tag} Variant "${key}=${String(value)}" is not a defined value for the "${key}" dimension.`
186
- );
115
+ }
116
+ function nodeList(list) {
117
+ return {
118
+ *[Symbol.iterator]() {
119
+ for (let i = 0; i < list.length; i++) {
120
+ const node = list.item(i);
121
+ if (node !== null) {
122
+ yield node;
123
+ }
187
124
  }
188
125
  }
126
+ };
127
+ }
128
+ function mapEntries(m) {
129
+ return m.entries();
130
+ }
131
+ function set(s) {
132
+ return s.values();
133
+ }
134
+ function hasOwn(object, key) {
135
+ return Object.hasOwn(object, key);
136
+ }
137
+ function* entries(object) {
138
+ for (const key in object) {
139
+ if (!hasOwn(object, key)) continue;
140
+ yield [key, object[key]];
189
141
  }
190
142
  }
191
- function panic(message) {
192
- throw new Error(message);
143
+ function* keys(object) {
144
+ for (const [key] of entries(object)) {
145
+ yield key;
146
+ }
193
147
  }
194
- function describe(value) {
195
- if (value === null) return "null";
196
- if (Array.isArray(value)) return "array";
197
- return typeof value;
148
+ function* values(object) {
149
+ for (const [, value] of entries(object)) {
150
+ yield value;
151
+ }
198
152
  }
199
- function assertPluginShape(result) {
200
- if (result === null || typeof result !== "object")
201
- panic(
202
- `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${result === null ? "null" : typeof result}.`
203
- );
204
- const plugin = result;
205
- if (typeof plugin.pipeline !== "function")
206
- panic(
207
- `[praxis-kit] Plugin factory return value is missing a 'pipeline' function. Got pipeline: ${typeof plugin.pipeline}.`
208
- );
153
+ function mapValues(object, callback) {
154
+ const result = {};
155
+ for (const [key, value] of entries(object)) {
156
+ result[key] = callback(value, key);
157
+ }
158
+ return result;
209
159
  }
210
- function guardPipeline(pipeline) {
211
- if (process.env.NODE_ENV === "production") return pipeline;
212
- return function guardedPipeline(tag, props, className, recipe) {
213
- const result = pipeline(tag, props, className, recipe);
214
- if (typeof result !== "string")
215
- panic(`[praxis-kit] Plugin pipeline must return a string. Got: ${describe(result)}.`);
216
- return result;
217
- };
160
+ function forEachEntry(object, callback) {
161
+ for (const [key, value] of entries(object)) {
162
+ callback(key, value);
163
+ }
218
164
  }
219
- var NOOP_CLASS_PIPELINE = (_tag, _props, className) => Array.isArray(className) ? className.join(" ") : className ?? "";
220
- function resolveClassPipeline(options, resolved, strict, capabilities) {
221
- const factory = options.styling?.plugin;
222
- if (!factory) {
223
- const createClassPipeline2 = capabilities?.createClassPipeline;
224
- const classPipeline2 = createClassPipeline2 ? createClassPipeline2(resolved) : NOOP_CLASS_PIPELINE;
225
- return { pluginResult: void 0, classPipeline: classPipeline2 };
165
+ function forEachKey(object, callback) {
166
+ for (const key of keys(object)) {
167
+ callback(key);
226
168
  }
227
- const pluginResult = factory(resolved, strict);
228
- assertPluginShape(pluginResult);
229
- const classPipeline = guardPipeline(pluginResult.pipeline);
230
- return { pluginResult, classPipeline };
231
169
  }
232
- function createRuntimeMethods(resolved, classPipeline, engine) {
233
- return {
234
- resolveTag: makeResolveTag(resolved.defaultTag),
235
- resolveProps(props) {
236
- return mergeProps(resolved.defaultProps, props);
237
- },
238
- resolveClasses(tag, props, className, recipe) {
239
- if (process.env.NODE_ENV !== "production") {
240
- validateRenderProps(resolved, props, recipe);
241
- }
242
- return classPipeline(tag, props, className, recipe);
243
- },
244
- resolveAria(tag, props) {
245
- if (!engine) return { props };
246
- const result = engine.validate(tag, props);
247
- return { props: result.props };
248
- }
249
- };
170
+ function forEachValue(object, callback) {
171
+ for (const value of values(object)) {
172
+ callback(value);
173
+ }
250
174
  }
251
- function createRuntimeObject(methods, resolved, pluginResult) {
252
- return pluginResult ? { ...methods, options: resolved, hasStyling: true, classPlugin: pluginResult } : { ...methods, options: resolved };
175
+ function forEachSet(s, callback) {
176
+ for (const value of s) {
177
+ callback(value);
178
+ }
253
179
  }
254
- function createPolymorphic(options = {}, capabilities) {
255
- const baseResolved = resolveFactoryOptions(options);
256
- const resolved = capabilities?.htmlPropNormalizersFn !== void 0 ? Object.freeze({
257
- ...baseResolved,
258
- htmlPropNormalizersFn: capabilities.htmlPropNormalizersFn
259
- }) : baseResolved;
260
- if (process.env.NODE_ENV !== "production") validateFactoryOptions(resolved);
261
- const { pluginResult, classPipeline } = resolveClassPipeline(
262
- options,
263
- resolved,
264
- resolved.strict,
265
- capabilities
266
- );
267
- const allAriaRules = [
268
- .../* @__PURE__ */ new Set([...capabilities?.htmlAriaRules ?? [], ...resolved.ariaRules ?? []])
269
- ];
270
- const engine = options.enforcement !== void 0 && capabilities?.AriaEngine ? new capabilities.AriaEngine(
271
- resolved.strict,
272
- allAriaRules.length ? { rules: allAriaRules } : void 0
273
- ) : null;
274
- const methods = createRuntimeMethods(resolved, classPipeline, engine);
275
- return createRuntimeObject(
276
- methods,
277
- resolved,
278
- pluginResult
279
- );
180
+ var iterate = Object.freeze({
181
+ entries,
182
+ filter,
183
+ find,
184
+ findLast,
185
+ forEach,
186
+ forEachEntry,
187
+ forEachKey,
188
+ forEachSet,
189
+ forEachValue,
190
+ items,
191
+ keys,
192
+ map,
193
+ mapEntries,
194
+ mapValues,
195
+ nodeList,
196
+ reduce,
197
+ collect,
198
+ set,
199
+ some,
200
+ every,
201
+ values
202
+ });
203
+
204
+ // ../../lib/primitive/src/utils/merge-props.ts
205
+ function mergeProps(defaultProps, props) {
206
+ return {
207
+ ...defaultProps ?? {},
208
+ ...props
209
+ };
280
210
  }
281
- var KNOWN_ARIA_ROLES = [
282
- "alert",
283
- "alertdialog",
284
- "application",
285
- "article",
286
- "banner",
287
- "blockquote",
288
- "button",
289
- "caption",
290
- "cell",
291
- "checkbox",
292
- "code",
293
- "columnheader",
294
- "combobox",
295
- "complementary",
296
- "contentinfo",
297
- "definition",
298
- "deletion",
299
- "dialog",
300
- "document",
301
- "emphasis",
302
- "feed",
303
- "figure",
304
- "form",
305
- "generic",
306
- "grid",
307
- "gridcell",
308
- "group",
309
- "heading",
310
- "img",
311
- "insertion",
312
- "link",
313
- "list",
314
- "listbox",
315
- "listitem",
316
- "log",
317
- "main",
318
- "marquee",
319
- "math",
320
- "menu",
321
- "menubar",
322
- "menuitem",
323
- "menuitemcheckbox",
324
- "menuitemradio",
325
- "meter",
326
- "navigation",
327
- "none",
328
- "note",
329
- "option",
330
- "paragraph",
331
- "presentation",
332
- "progressbar",
333
- "radio",
334
- "radiogroup",
335
- "region",
336
- "row",
337
- "rowgroup",
338
- "rowheader",
339
- "scrollbar",
340
- "search",
341
- "searchbox",
342
- "separator",
343
- "slider",
344
- "spinbutton",
345
- "status",
346
- "strong",
347
- "subscript",
348
- "superscript",
349
- "switch",
350
- "tab",
351
- "table",
352
- "tablist",
353
- "tabpanel",
354
- "term",
355
- "textbox",
356
- "time",
357
- "timer",
358
- "toolbar",
359
- "tooltip",
360
- "tree",
361
- "treegrid",
362
- "treeitem"
363
- ];
364
- var KNOWN_ARIA_ROLES_SET = new Set(KNOWN_ARIA_ROLES);
211
+
212
+ // ../../lib/primitive/src/constants/aria/global-aria-attributes.ts
365
213
  var GLOBAL_ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
366
214
  "aria-atomic",
367
215
  "aria-busy",
@@ -381,29 +229,59 @@ var GLOBAL_ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
381
229
  "aria-relevant",
382
230
  "aria-roledescription"
383
231
  ]);
232
+
233
+ // ../../lib/primitive/src/constants/aria/implicit-role-record.ts
384
234
  var IMPLICIT_ROLE_RECORD = {
235
+ // Landmarks
385
236
  article: "article",
386
237
  aside: "complementary",
387
238
  footer: "contentinfo",
388
239
  header: "banner",
389
240
  main: "main",
390
241
  nav: "navigation",
391
- button: "button",
242
+ // Interactive
392
243
  a: "link",
244
+ button: "button",
393
245
  select: "listbox",
246
+ textarea: "textbox",
247
+ // Headings
394
248
  h1: "heading",
395
249
  h2: "heading",
396
250
  h3: "heading",
397
251
  h4: "heading",
398
252
  h5: "heading",
399
253
  h6: "heading",
254
+ // Lists
400
255
  ul: "list",
401
256
  ol: "list",
402
257
  li: "listitem",
258
+ // Tables
403
259
  table: "table",
404
260
  tr: "row",
405
261
  td: "cell",
406
- th: "columnheader"
262
+ th: "columnheader",
263
+ // Structural / semantic
264
+ dialog: "dialog",
265
+ fieldset: "group",
266
+ figure: "figure",
267
+ meter: "meter",
268
+ output: "status",
269
+ progress: "progressbar"
270
+ };
271
+ var INPUT_TYPE_ROLE_MAP = {
272
+ checkbox: "checkbox",
273
+ radio: "radio",
274
+ range: "slider",
275
+ number: "spinbutton",
276
+ search: "searchbox",
277
+ text: "textbox",
278
+ email: "textbox",
279
+ tel: "textbox",
280
+ url: "textbox",
281
+ button: "button",
282
+ submit: "button",
283
+ reset: "button",
284
+ image: "button"
407
285
  };
408
286
  var STRONG_ROLES = [
409
287
  "main",
@@ -415,6 +293,8 @@ var STRONG_ROLES = [
415
293
  var STANDALONE_ROLES = ["article"];
416
294
  var STRONG_ROLES_SET = new Set(STRONG_ROLES);
417
295
  var STANDALONE_ROLES_SET = new Set(STANDALONE_ROLES);
296
+
297
+ // ../../lib/primitive/src/constants/aria/role-restricted-attributes.ts
418
298
  var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
419
299
  [
420
300
  "aria-activedescendant",
@@ -564,9 +444,18 @@ var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
564
444
  /* @__PURE__ */ new Set(["meter", "progressbar", "scrollbar", "separator", "slider", "spinbutton"])
565
445
  ]
566
446
  ]);
447
+
448
+ // ../../lib/primitive/src/guards/foundational/is-defined.ts
567
449
  function isUndefined(value) {
568
450
  return value === void 0;
569
451
  }
452
+
453
+ // ../../lib/primitive/src/guards/foundational/is-null.ts
454
+ function isNull(value) {
455
+ return value === null;
456
+ }
457
+
458
+ // ../../lib/primitive/src/guards/aria/is-aria-attribute.ts
570
459
  function isGlobalAriaAttribute(attr) {
571
460
  return GLOBAL_ARIA_ATTRIBUTES.has(attr);
572
461
  }
@@ -576,6 +465,8 @@ function isAriaAttributeValidForRole(attr, role) {
576
465
  if (isUndefined(role)) return false;
577
466
  return allowedRoles.has(role);
578
467
  }
468
+
469
+ // ../../lib/primitive/src/guards/aria/is-aria-role.ts
579
470
  function isStrongImplicitRole(tag) {
580
471
  if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
581
472
  return STRONG_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
@@ -584,116 +475,509 @@ function isStandaloneTag(tag) {
584
475
  if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
585
476
  return STANDALONE_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
586
477
  }
587
-
588
- // ../core/dist/chunk-2NJ5XLOA.js
589
- var IMPLICIT_ROLE_RECORD2 = {
590
- article: "article",
591
- aside: "complementary",
592
- footer: "contentinfo",
593
- header: "banner",
594
- main: "main",
595
- nav: "navigation",
596
- button: "button",
597
- a: "link",
598
- select: "listbox",
599
- h1: "heading",
600
- h2: "heading",
601
- h3: "heading",
602
- h4: "heading",
603
- h5: "heading",
604
- h6: "heading",
605
- ul: "list",
606
- ol: "list",
607
- li: "listitem",
608
- table: "table",
609
- tr: "row",
610
- td: "cell",
611
- th: "columnheader"
612
- };
613
- function getImplicitRole(tag) {
614
- if (tag in IMPLICIT_ROLE_RECORD2) return IMPLICIT_ROLE_RECORD2[tag];
478
+ function getInputImplicitRole(type) {
479
+ if (type == null || !(type in INPUT_TYPE_ROLE_MAP)) return void 0;
480
+ return INPUT_TYPE_ROLE_MAP[type];
481
+ }
482
+ function getConditionalImplicitRole(tag, ariaLabel, ariaLabelledBy) {
483
+ const isNamed = typeof ariaLabel === "string" || typeof ariaLabelledBy === "string";
484
+ if (!isNamed) return void 0;
485
+ if (tag === "section") return "region";
486
+ if (tag === "form") return "form";
615
487
  return void 0;
616
488
  }
617
- var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default-tag");
618
- function getAsProp(child) {
619
- if (!isObject(child) || !("props" in child)) return void 0;
620
- const props = child.props;
621
- if (!isObject(props)) return void 0;
622
- const as = props.as;
623
- return isString(as) && as !== "" ? as : void 0;
489
+
490
+ // ../../lib/adapter-utils/src/apply-filter.ts
491
+ function applyFilter(props, filterProps, variantKeys) {
492
+ const out = {};
493
+ iterate.forEachEntry(props, (k) => {
494
+ if (!Object.hasOwn(props, k)) return;
495
+ if (filterProps(k, variantKeys)) return;
496
+ out[k] = props[k];
497
+ });
498
+ return out;
624
499
  }
625
- function getTag(child) {
626
- if (!isObject(child) || !("type" in child)) return void 0;
627
- const t = child.type;
628
- if (isString(t)) return t;
629
- if (typeof t === "function" || isObject(t)) {
630
- const defaultTag = t[COMPONENT_DEFAULT_TAG];
631
- if (!isString(defaultTag)) return void 0;
632
- return getAsProp(child) ?? defaultTag;
500
+
501
+ // ../../lib/contract/src/aria/aria-role-policy.ts
502
+ function getImplicitRole(tag, props) {
503
+ if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
504
+ if (tag === "input") return getInputImplicitRole(props?.type);
505
+ if (tag === "img") return props?.alt === "" ? "none" : "img";
506
+ if (tag === "section" || tag === "form") {
507
+ return getConditionalImplicitRole(tag, props?.["aria-label"], props?.["aria-labelledby"]);
633
508
  }
634
509
  return void 0;
635
510
  }
636
- function isTag(...args) {
637
- if (isString(args[0])) {
638
- const set2 = new Set(args);
639
- return (child2) => {
640
- const tag2 = getTag(child2);
641
- return tag2 !== void 0 && set2.has(tag2);
642
- };
511
+
512
+ // ../../lib/contract/src/strict/invariant-base.ts
513
+ var InvariantBase = class {
514
+ diagnostics;
515
+ constructor(diagnostics) {
516
+ this.diagnostics = diagnostics;
643
517
  }
644
- const [child, ...tags] = args;
645
- const set = new Set(tags);
646
- const tag = getTag(child);
647
- return tag !== void 0 && set.has(tag);
648
- }
649
- var pendingAsyncWarns2 = /* @__PURE__ */ new Set();
650
- var asyncWarnScheduled2 = false;
651
- function flushAsyncWarns2() {
652
- asyncWarnScheduled2 = false;
653
- const messages = [...pendingAsyncWarns2];
654
- pendingAsyncWarns2.clear();
655
- for (const msg of messages) {
656
- console.warn(msg);
657
- }
658
- }
659
- function scheduleAsyncWarn(message) {
660
- if (pendingAsyncWarns2.has(message)) return;
661
- pendingAsyncWarns2.add(message);
662
- if (!asyncWarnScheduled2) {
663
- asyncWarnScheduled2 = true;
664
- queueMicrotask(flushAsyncWarns2);
665
- }
666
- }
667
- var StrictBase = class {
668
- strict;
669
- constructor(strict) {
670
- this.strict = strict;
671
- }
672
- violate(message) {
673
- if (this.strict === true || this.strict === "throw") {
674
- throw new Error(message);
675
- }
676
- this.warn(message);
518
+ get active() {
519
+ return this.diagnostics.active;
520
+ }
521
+ violate(input) {
522
+ this.diagnostics.error(input);
677
523
  }
678
- // Always caps at console.warn — never throws. ARIA 'warning' violations route here
524
+ // Always caps at warn — never throws. ARIA 'warning' violations route here
679
525
  // so they surface even in strict='throw' mode without aborting a render.
680
- warn(message) {
681
- if (!this.strict) return;
682
- if (this.strict === "async-warn") {
683
- scheduleAsyncWarn(message);
684
- return;
685
- }
686
- console.warn(message);
526
+ warn(input) {
527
+ this.diagnostics.warn(input);
687
528
  }
688
- invariant(condition, message) {
689
- if (!condition) {
690
- this.violate(message);
691
- }
529
+ invariant(condition, input) {
530
+ if (!condition) this.violate(input);
692
531
  }
693
532
  };
694
- function isNull2(value) {
695
- return value === null;
533
+
534
+ // ../../lib/diagnostics/src/category.ts
535
+ var DiagnosticCategory = /* @__PURE__ */ ((DiagnosticCategory2) => {
536
+ DiagnosticCategory2[DiagnosticCategory2["Contract"] = 0] = "Contract";
537
+ DiagnosticCategory2[DiagnosticCategory2["HTML"] = 1] = "HTML";
538
+ DiagnosticCategory2[DiagnosticCategory2["ARIA"] = 2] = "ARIA";
539
+ DiagnosticCategory2[DiagnosticCategory2["Composition"] = 3] = "Composition";
540
+ DiagnosticCategory2[DiagnosticCategory2["Rendering"] = 4] = "Rendering";
541
+ DiagnosticCategory2[DiagnosticCategory2["Accessibility"] = 5] = "Accessibility";
542
+ DiagnosticCategory2[DiagnosticCategory2["Performance"] = 6] = "Performance";
543
+ DiagnosticCategory2[DiagnosticCategory2["Internal"] = 7] = "Internal";
544
+ DiagnosticCategory2[DiagnosticCategory2["Deprecation"] = 8] = "Deprecation";
545
+ DiagnosticCategory2[DiagnosticCategory2["Lint"] = 9] = "Lint";
546
+ return DiagnosticCategory2;
547
+ })(DiagnosticCategory || {});
548
+
549
+ // ../../lib/diagnostics/src/error.ts
550
+ var PraxisError = class extends Error {
551
+ diagnostic;
552
+ constructor(diagnostic) {
553
+ super(diagnostic.message);
554
+ this.name = "PraxisError";
555
+ this.diagnostic = diagnostic;
556
+ }
557
+ };
558
+
559
+ // ../../lib/diagnostics/src/severity.ts
560
+ var Severity = /* @__PURE__ */ ((Severity2) => {
561
+ Severity2[Severity2["Debug"] = 0] = "Debug";
562
+ Severity2[Severity2["Info"] = 1] = "Info";
563
+ Severity2[Severity2["Warning"] = 2] = "Warning";
564
+ Severity2[Severity2["Error"] = 3] = "Error";
565
+ Severity2[Severity2["Fatal"] = 4] = "Fatal";
566
+ return Severity2;
567
+ })(Severity || {});
568
+
569
+ // ../../lib/diagnostics/src/policy.ts
570
+ var DefaultPolicy = class {
571
+ reportThreshold;
572
+ throwThreshold;
573
+ constructor({
574
+ reportThreshold = 1 /* Info */,
575
+ throwThreshold = 4 /* Fatal */
576
+ } = {}) {
577
+ this.reportThreshold = reportThreshold;
578
+ this.throwThreshold = throwThreshold;
579
+ }
580
+ resolve(diagnostic) {
581
+ if (diagnostic.severity >= this.throwThreshold) return 2 /* Throw */;
582
+ if (diagnostic.severity >= this.reportThreshold) return 1 /* Report */;
583
+ return 0 /* Ignore */;
584
+ }
585
+ };
586
+
587
+ // ../../lib/diagnostics/src/diagnostics.ts
588
+ var Diagnostics = class {
589
+ reporter;
590
+ policy;
591
+ // Pre-computed at construction time: true if Warning-level diagnostics are not ignored.
592
+ active;
593
+ constructor(reporter, policy = new DefaultPolicy()) {
594
+ this.reporter = reporter;
595
+ this.policy = policy;
596
+ this.active = policy.resolve({ severity: 2 /* Warning */ }) !== 0 /* Ignore */;
597
+ }
598
+ report(diagnostic) {
599
+ const enforcement = this.policy.resolve(diagnostic);
600
+ if (enforcement === 0 /* Ignore */) return diagnostic;
601
+ if (enforcement === 2 /* Throw */) throw new PraxisError(diagnostic);
602
+ this.reporter.report(diagnostic);
603
+ return diagnostic;
604
+ }
605
+ warn(input) {
606
+ return this.report({ ...input, severity: 2 /* Warning */ });
607
+ }
608
+ error(input) {
609
+ return this.report({ ...input, severity: 3 /* Error */ });
610
+ }
611
+ info(input) {
612
+ return this.report({ ...input, severity: 1 /* Info */ });
613
+ }
614
+ };
615
+
616
+ // ../../lib/diagnostics/src/formatter.ts
617
+ function formatDiagnostic(diagnostic) {
618
+ const level = Severity[diagnostic.severity];
619
+ const category = DiagnosticCategory[diagnostic.category];
620
+ const prefix = category !== void 0 ? `[${category}] ` : "";
621
+ return `${level} ${diagnostic.code}: ${prefix}${diagnostic.message}`;
696
622
  }
623
+
624
+ // ../../lib/diagnostics/src/console-reporter.ts
625
+ var ConsoleReporter = class {
626
+ report(diagnostic) {
627
+ const message = formatDiagnostic(diagnostic);
628
+ switch (diagnostic.severity) {
629
+ case 0 /* Debug */:
630
+ console.debug(message);
631
+ break;
632
+ case 1 /* Info */:
633
+ console.info(message);
634
+ break;
635
+ case 2 /* Warning */:
636
+ console.warn(message);
637
+ break;
638
+ case 3 /* Error */:
639
+ case 4 /* Fatal */:
640
+ console.error(message);
641
+ break;
642
+ }
643
+ }
644
+ };
645
+
646
+ // ../../lib/diagnostics/src/null-reporter.ts
647
+ var nullReporter = {
648
+ report() {
649
+ }
650
+ };
651
+
652
+ // ../../lib/diagnostics/src/presets.ts
653
+ var ignoreAllPolicy = {
654
+ resolve(_) {
655
+ return 0 /* Ignore */;
656
+ }
657
+ };
658
+ var warnOnlyReporter = {
659
+ report(diagnostic) {
660
+ console.warn(formatDiagnostic(diagnostic));
661
+ }
662
+ };
663
+ var silentDiagnostics = new Diagnostics(nullReporter, ignoreAllPolicy);
664
+ var warnDiagnostics = new Diagnostics(
665
+ warnOnlyReporter,
666
+ new DefaultPolicy({ reportThreshold: 2 /* Warning */, throwThreshold: 4 /* Fatal */ })
667
+ );
668
+ var throwDiagnostics = new Diagnostics(
669
+ new ConsoleReporter(),
670
+ new DefaultPolicy({ reportThreshold: 2 /* Warning */, throwThreshold: 3 /* Error */ })
671
+ );
672
+
673
+ // ../../lib/contract/src/diagnostics/aria.ts
674
+ var AriaDiagnostics = {
675
+ /** Generic bridge for violations produced by external AriaRule functions. */
676
+ fromViolation(v) {
677
+ return {
678
+ code: "ARIA2002" /* AriaViolation */,
679
+ category: 2 /* ARIA */,
680
+ message: v.message
681
+ };
682
+ },
683
+ attributeInvalid(key, role) {
684
+ return {
685
+ code: "ARIA2003" /* AriaAttributeInvalid */,
686
+ category: 2 /* ARIA */,
687
+ message: `"${key}" is not valid on role="${role}". It will be removed.`,
688
+ rationale: "Invalid ARIA attributes are ignored by assistive technology and may trigger accessibility-tree warnings in browser devtools.",
689
+ suggestions: [
690
+ {
691
+ title: "Remove the attribute",
692
+ description: `"${key}" is not in the allowed attribute set for role="${role}".`
693
+ }
694
+ ]
695
+ };
696
+ },
697
+ missingLiveRegion(role, impliedLive) {
698
+ return {
699
+ code: "ARIA2004" /* AriaMissingLiveRegion */,
700
+ category: 2 /* ARIA */,
701
+ message: `role="${role}" implies aria-live="${impliedLive}" but it is missing. It has been injected.`,
702
+ rationale: "Live-region roles announce dynamic content changes to screen readers. Without aria-live the politeness level is unspecified and announcements may be silent.",
703
+ suggestions: [
704
+ {
705
+ title: `Add aria-live="${impliedLive}"`,
706
+ description: `role="${role}" conventionally implies aria-live="${impliedLive}".`,
707
+ fix: `aria-live="${impliedLive}"`
708
+ }
709
+ ]
710
+ };
711
+ },
712
+ missingAtomic(role) {
713
+ return {
714
+ code: "ARIA2005" /* AriaMissingAtomic */,
715
+ category: 2 /* ARIA */,
716
+ message: `role="${role}" is a live region. Consider setting aria-atomic="true" if the full region should be announced as a unit, or aria-atomic="false" if only changed nodes should be read.`,
717
+ rationale: "aria-atomic controls whether assistive technology announces the entire live region or only the changed nodes. Omitting it leaves the behaviour browser-defined."
718
+ };
719
+ },
720
+ relevantInvalidTokens(invalid) {
721
+ const quoted = invalid.map((t) => `"${t}"`).join(", ");
722
+ return {
723
+ code: "ARIA2006" /* AriaRelevantInvalidToken */,
724
+ category: 2 /* ARIA */,
725
+ message: `aria-relevant contains invalid token(s): ${quoted}. Valid tokens are: additions, removals, text, all.`,
726
+ rationale: "aria-relevant accepts a space-separated list of change types. Unrecognised tokens are silently ignored by assistive technology, making the attribute ineffective.",
727
+ suggestions: [
728
+ {
729
+ title: "Use only valid tokens",
730
+ description: "Valid values are: additions, removals, text, all (or a space-separated combination)."
731
+ }
732
+ ]
733
+ };
734
+ },
735
+ relevantSuperseded() {
736
+ return {
737
+ code: "ARIA2007" /* AriaRelevantSuperseded */,
738
+ category: 2 /* ARIA */,
739
+ message: 'aria-relevant includes "all" alongside other tokens. "all" supersedes additions, removals, and text \u2014 use aria-relevant="all" alone.',
740
+ rationale: '"all" is equivalent to "additions removals text". Combining it with other tokens is redundant and may confuse readers of the markup.',
741
+ suggestions: [
742
+ {
743
+ title: 'Use aria-relevant="all"',
744
+ fix: 'aria-relevant="all"'
745
+ }
746
+ ]
747
+ };
748
+ },
749
+ missingAccessibleName(tag) {
750
+ return {
751
+ code: "ARIA2009" /* AriaMissingAccessibleName */,
752
+ category: 2 /* ARIA */,
753
+ message: `<${tag}> has no accessible name. Add aria-label or aria-labelledby.`,
754
+ rationale: "Elements with a landmark or interactive role must have an accessible name so that assistive technology can identify them when presenting the page outline.",
755
+ suggestions: [
756
+ {
757
+ title: "Add aria-label",
758
+ description: `Add aria-label="\u2026" directly to the <${tag}> element.`
759
+ },
760
+ {
761
+ title: "Add aria-labelledby",
762
+ description: "Point aria-labelledby at the id of an existing heading or label element."
763
+ }
764
+ ]
765
+ };
766
+ },
767
+ attributeOnPresentational(attr, tag) {
768
+ return {
769
+ code: "ARIA2010" /* AriaAttributeOnPresentational */,
770
+ category: 2 /* ARIA */,
771
+ message: `"${attr}" is not allowed on a presentational <${tag}>. Presentational elements are invisible to assistive technology.`,
772
+ rationale: 'role="none" and role="presentation" (including <img alt="">) remove an element from the accessibility tree. ARIA attributes on such elements are ignored by assistive technology.',
773
+ suggestions: [
774
+ {
775
+ title: "Remove the attribute",
776
+ description: `"${attr}" has no effect when the element has role="none" or role="presentation".`
777
+ }
778
+ ]
779
+ };
780
+ },
781
+ ariaHiddenOnFocusable(tag) {
782
+ return {
783
+ code: "ARIA2011" /* AriaHiddenOnFocusable */,
784
+ category: 2 /* ARIA */,
785
+ message: `aria-hidden="true" must not be used on focusable <${tag}> elements. Screen reader users who navigate by keyboard will encounter the element but receive no information about it.`,
786
+ rationale: 'aria-hidden removes an element from the accessibility tree while leaving it keyboard-reachable. This creates a "ghost" \u2014 a focusable element assistive technology cannot describe.',
787
+ suggestions: [
788
+ {
789
+ title: "Remove aria-hidden",
790
+ description: "If the element should be hidden from all users, use the HTML hidden attribute or CSS display:none instead."
791
+ },
792
+ {
793
+ title: "Make the element non-focusable",
794
+ description: 'If the element is intentionally decorative, add tabindex="-1" and disable it so it is not reachable by keyboard.'
795
+ }
796
+ ]
797
+ };
798
+ },
799
+ invalidAttributeValue(attr, value, expected) {
800
+ const got = value === null ? "null" : value === void 0 ? "undefined" : typeof value === "string" ? `"${value}"` : String(value);
801
+ return {
802
+ code: "ARIA2013" /* AriaInvalidAttributeValue */,
803
+ category: 2 /* ARIA */,
804
+ message: `"${attr}" has an invalid value (${got}). Expected: ${expected}.`,
805
+ rationale: "ARIA attributes with invalid values are silently ignored by assistive technology, making the markup semantically inert.",
806
+ suggestions: [
807
+ {
808
+ title: `Use a valid value for ${attr}`,
809
+ description: `Valid values are: ${expected}.`
810
+ }
811
+ ]
812
+ };
813
+ },
814
+ redundantAriaLevel(tag, level) {
815
+ return {
816
+ code: "ARIA2014" /* AriaRedundantLevelAttribute */,
817
+ category: 2 /* ARIA */,
818
+ message: `aria-level="${level}" is redundant on <${tag}>: the element already has an implicit heading level of ${level}. Remove the attribute.`,
819
+ rationale: 'Restating the implicit aria-level adds noise without semantic value. Use aria-level only to override the native heading level (e.g. aria-level="3" on <h2>).',
820
+ suggestions: [
821
+ {
822
+ title: "Remove aria-level",
823
+ description: `<${tag}> already implies aria-level="${level}".`
824
+ }
825
+ ]
826
+ };
827
+ },
828
+ requiredProperty(attr, role) {
829
+ return {
830
+ code: "ARIA2012" /* AriaRequiredProperty */,
831
+ category: 2 /* ARIA */,
832
+ message: `"${attr}" is required for role="${role}" but is missing.`,
833
+ rationale: `WAI-ARIA 1.2 specifies required states and properties for certain roles. Without "${attr}", assistive technology cannot correctly communicate the element's state to users.`,
834
+ suggestions: [
835
+ {
836
+ title: `Add ${attr}`,
837
+ description: `role="${role}" requires "${attr}" to be present.`
838
+ }
839
+ ]
840
+ };
841
+ },
842
+ invalidRole(role, tag) {
843
+ return {
844
+ code: "ARIA2008" /* AriaInvalidRole */,
845
+ category: 2 /* ARIA */,
846
+ message: `Invalid role "${role ?? ""}" on <${tag}>.`,
847
+ rationale: "An unrecognised or misapplied ARIA role is ignored by assistive technology and may degrade the accessibility of the element."
848
+ };
849
+ }
850
+ };
851
+
852
+ // ../../lib/contract/src/diagnostics/contract.ts
853
+ var ContractDiagnostics = {
854
+ unexpectedChild(typeName, index, context) {
855
+ return {
856
+ code: "COMP1004" /* UnexpectedChild */,
857
+ category: 0 /* Contract */,
858
+ component: context,
859
+ message: `${context}: unexpected child "${typeName}" at index ${index}.`
860
+ };
861
+ },
862
+ ambiguousChild(typeName, index, ruleNames, context) {
863
+ const quoted = ruleNames.map((n) => `"${n}"`).join(" and ");
864
+ return {
865
+ code: "COMP1005" /* AmbiguousChild */,
866
+ category: 0 /* Contract */,
867
+ component: context,
868
+ message: `${context}: child "${typeName}" at index ${index} matches multiple child rules: ${quoted}.`
869
+ };
870
+ },
871
+ cardinalityMin(ruleName, min, context) {
872
+ return {
873
+ code: "COMP1006" /* CardinalityMin */,
874
+ category: 0 /* Contract */,
875
+ component: context,
876
+ message: `${context}: "${ruleName}" requires at least ${min}.`
877
+ };
878
+ },
879
+ cardinalityMax(ruleName, max, context) {
880
+ return {
881
+ code: "COMP1007" /* CardinalityMax */,
882
+ category: 0 /* Contract */,
883
+ component: context,
884
+ message: `${context}: "${ruleName}" allows at most ${max}.`
885
+ };
886
+ },
887
+ positionViolation(ruleName, position, index, context) {
888
+ return {
889
+ code: "COMP1008" /* PositionViolation */,
890
+ category: 0 /* Contract */,
891
+ component: context,
892
+ message: `${context}: "${ruleName}" must be ${position}, got index ${index}`
893
+ };
894
+ },
895
+ unknownVariantDim(component, label2, dim) {
896
+ return {
897
+ code: "COMP1010" /* ContractUnknownVariantDim */,
898
+ category: 0 /* Contract */,
899
+ message: `${component}: ${label2} references unknown variant "${dim}".`
900
+ };
901
+ },
902
+ unknownVariantValue(component, label2, dim, value, valid) {
903
+ return {
904
+ code: "COMP1011" /* ContractUnknownVariantValue */,
905
+ category: 0 /* Contract */,
906
+ message: `${component}: ${label2} sets "${dim}" to unknown value "${value}" (valid: ${valid.join(", ")}).`
907
+ };
908
+ },
909
+ unknownRecipeKey(component, key) {
910
+ return {
911
+ code: "COMP1012" /* ContractUnknownRecipeKey */,
912
+ category: 0 /* Contract */,
913
+ message: `${component} Unknown recipeKey "${key}" \u2014 no preset with that name exists.`
914
+ };
915
+ },
916
+ invalidVariantValue(component, key, value) {
917
+ return {
918
+ code: "COMP1013" /* ContractInvalidVariantValue */,
919
+ category: 0 /* Contract */,
920
+ message: `${component} Variant "${key}=${value}" is not a defined value for the "${key}" dimension.`
921
+ };
922
+ },
923
+ allowedAsViolation(tag, allowedAs, component) {
924
+ const allowed = allowedAs.map((t) => `"${String(t)}"`).join(", ");
925
+ return {
926
+ code: "COMP1009" /* AllowedAsViolation */,
927
+ category: 0 /* Contract */,
928
+ component,
929
+ message: `<${component}>: "as" prop received "${tag}" but only [${allowed}] are allowed.`
930
+ };
931
+ }
932
+ };
933
+
934
+ // ../../lib/contract/src/diagnostics/html.ts
935
+ var HtmlDiagnostics = {
936
+ emptyRole(tag) {
937
+ return {
938
+ code: "HTML3002" /* HtmlEmptyRole */,
939
+ category: 1 /* HTML */,
940
+ message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
941
+ };
942
+ },
943
+ implicitRoleRedundant(tag, implicitRole) {
944
+ return {
945
+ code: "HTML3003" /* HtmlImplicitRoleRedundant */,
946
+ category: 1 /* HTML */,
947
+ message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
948
+ };
949
+ },
950
+ implicitRoleOverride(tag, implicitRole, role) {
951
+ return {
952
+ code: "HTML3004" /* HtmlImplicitRoleOverride */,
953
+ category: 1 /* HTML */,
954
+ message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
955
+ };
956
+ },
957
+ standaloneRegionOverride(tag, implicitRole) {
958
+ return {
959
+ code: "HTML3005" /* HtmlStandaloneRegionOverride */,
960
+ category: 1 /* HTML */,
961
+ message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
962
+ };
963
+ },
964
+ landmarkRoleOverride(tag, implicitRole, role) {
965
+ return {
966
+ code: "HTML3006" /* HtmlLandmarkRoleOverride */,
967
+ category: 1 /* HTML */,
968
+ message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
969
+ };
970
+ },
971
+ invalidChild(child, parent, allowed) {
972
+ return {
973
+ code: "HTML3007" /* HtmlInvalidChild */,
974
+ category: 1 /* HTML */,
975
+ message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
976
+ };
977
+ }
978
+ };
979
+
980
+ // ../../lib/contract/src/aria/polymorphic-validator.ts
697
981
  var VALID = [{ valid: true }];
698
982
  function isIntrinsicTag(tag) {
699
983
  return isString(tag);
@@ -702,26 +986,28 @@ function omitProp(obj, key) {
702
986
  const { [key]: _, ...rest } = obj;
703
987
  return rest;
704
988
  }
705
- var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
989
+ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
706
990
  #extraRules;
707
991
  #planCache = /* @__PURE__ */ new Map();
708
992
  static #MAX_CACHE = 100;
709
993
  // Memoized AriaFix objects keyed by attribute name — the ARIA attribute set is
710
994
  // finite so this Map is bounded and avoids recreating closures on every cache miss.
711
995
  static #removeAttributeFixCache = /* @__PURE__ */ new Map();
712
- constructor(strict = "warn", options) {
713
- super(strict);
996
+ constructor(diagnostics, options) {
997
+ super(diagnostics);
714
998
  this.#extraRules = options?.rules ?? [];
715
999
  }
716
1000
  static #normalizeEmptyRole(tag, props) {
717
1001
  if (props.role !== "") return { normalized: false };
1002
+ const d = HtmlDiagnostics.emptyRole(tag);
718
1003
  return {
719
1004
  normalized: true,
720
1005
  result: {
721
1006
  props: omitProp(props, "role"),
722
1007
  violations: [
723
1008
  {
724
- message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`,
1009
+ message: d.message,
1010
+ diagnostic: d,
725
1011
  tag,
726
1012
  role: "",
727
1013
  attribute: void 0,
@@ -734,10 +1020,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
734
1020
  }
735
1021
  static #deriveContext(tag, props) {
736
1022
  if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
737
- const implicitRole = getImplicitRole(tag);
738
- const hasExplicitLiveRole = !implicitRole && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(props.role ?? "");
739
- if (!implicitRole && !hasExplicitLiveRole)
740
- return { proceed: false, result: { props, violations: [] } };
1023
+ const implicitRole = getImplicitRole(tag, props);
1024
+ const hasRole = implicitRole != null || isString(props.role) && props.role.length > 0;
1025
+ if (!hasRole) return { proceed: false, result: { props, violations: [] } };
741
1026
  const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
742
1027
  if (normalized.normalized) return { proceed: false, result: normalized.result };
743
1028
  const effectiveRole = props.role ?? implicitRole;
@@ -752,25 +1037,36 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
752
1037
  static #runRules(rules, context) {
753
1038
  const violations = [];
754
1039
  const fixes = [];
755
- for (const rule of rules) {
756
- for (const result of rule(context)) {
757
- if (!result.valid) {
758
- violations.push({
759
- message: result.message ?? `Invalid role "${context.props.role}" on <${context.tag}>`,
760
- tag: context.tag,
761
- role: context.props.role,
762
- attribute: result.attribute,
763
- severity: result.severity,
764
- phase: "evaluate"
765
- });
766
- if (result.fixable) fixes.push(result.fix);
767
- }
768
- }
769
- }
1040
+ iterate.forEach(rules, (rule) => {
1041
+ iterate.forEach(rule(context), (result) => {
1042
+ if (result.valid) return;
1043
+ const {
1044
+ tag,
1045
+ props: { role }
1046
+ } = context;
1047
+ const { message, attribute, severity } = result;
1048
+ const resolvedMessage = message ?? result.diagnostic?.message;
1049
+ const fallbackDiag = resolvedMessage == null ? AriaDiagnostics.invalidRole(role, tag) : void 0;
1050
+ violations.push({
1051
+ message: resolvedMessage ?? fallbackDiag.message,
1052
+ tag,
1053
+ role,
1054
+ attribute,
1055
+ severity,
1056
+ phase: "evaluate",
1057
+ ...result.diagnostic != null && { diagnostic: result.diagnostic },
1058
+ ...fallbackDiag != null && { diagnostic: fallbackDiag }
1059
+ });
1060
+ if (result.fixable) fixes.push(result.fix);
1061
+ });
1062
+ });
770
1063
  return { violations, fixes };
771
1064
  }
772
1065
  static #getRules(context) {
773
- return _AriaPolicyEngine.#hasRole(context.props) ? _AriaPolicyEngine.#pipeline : [_AriaPolicyEngine.#checkInvalidAriaAttributes];
1066
+ if (_AriaPolicyEngine.#hasRole(context.props) || context.effectiveRole != null && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1067
+ return _AriaPolicyEngine.#pipeline;
1068
+ }
1069
+ return _AriaPolicyEngine.#implicitOnlyRules;
774
1070
  }
775
1071
  static evaluate(tag, props) {
776
1072
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
@@ -795,10 +1091,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
795
1091
  return { props: next, violations };
796
1092
  }
797
1093
  report(violations) {
798
- for (const v of violations) {
799
- if (v.severity === "error") this.violate(v.message);
800
- else this.warn(v.message);
801
- }
1094
+ iterate.forEach(violations, (v) => {
1095
+ const d = v.diagnostic ?? AriaDiagnostics.fromViolation(v);
1096
+ if (v.severity === "error") this.violate(d);
1097
+ else this.warn(d);
1098
+ });
802
1099
  }
803
1100
  // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs).
804
1101
  // Non-aria props (className, onClick, etc.) do not affect ARIA decisions and are excluded
@@ -810,27 +1107,26 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
810
1107
  if (!isIntrinsicTag(tag)) return null;
811
1108
  const parts = [tag];
812
1109
  if (typeof props.role === "string") parts.push(`role:${props.role}`);
1110
+ if (tag === "input" && typeof props.type === "string") parts.push(`type:${props.type}`);
1111
+ if (tag === "img") parts.push(`alt:${props.alt === "" ? "empty" : "present"}`);
813
1112
  const ariaEntries = [];
814
- for (const k in props) {
815
- if (!Object.hasOwn(props, k) || !k.startsWith("aria-")) continue;
816
- const v = props[k];
817
- if (!isString(v) && !isNumber(v) && typeof v !== "boolean") continue;
1113
+ iterate.forEachEntry(props, (k, v) => {
1114
+ if (!k.startsWith("aria-")) return;
1115
+ if (!isString(v) && !isNumber(v) && typeof v !== "boolean") return;
818
1116
  ariaEntries.push(`${k}:${String(v)}`);
819
- }
1117
+ });
820
1118
  if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
821
1119
  return parts.join("|");
822
1120
  }
823
1121
  static #computePlan(inputProps, resultProps) {
824
1122
  const removals = /* @__PURE__ */ new Set();
825
1123
  const updates = {};
826
- for (const key in inputProps) {
827
- if (Object.hasOwn(inputProps, key) && !(key in resultProps)) removals.add(key);
828
- }
829
- for (const key in resultProps) {
830
- if (!Object.hasOwn(resultProps, key)) continue;
831
- const resultVal = resultProps[key];
1124
+ iterate.forEachKey(inputProps, (key) => {
1125
+ if (!(key in resultProps)) removals.add(key);
1126
+ });
1127
+ iterate.forEachEntry(resultProps, (key, resultVal) => {
832
1128
  if (inputProps[key] !== resultVal) updates[key] = resultVal;
833
- }
1129
+ });
834
1130
  return { removals, updates };
835
1131
  }
836
1132
  static #applyPlan(props, removals, updates) {
@@ -838,15 +1134,15 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
838
1134
  const hasUpdates = Object.keys(updates).length > 0;
839
1135
  if (!hasRemovals && !hasUpdates) return props;
840
1136
  const next = {};
841
- for (const k in props) {
842
- if (Object.hasOwn(props, k) && !removals.has(k)) next[k] = props[k];
843
- }
1137
+ iterate.forEachEntry(props, (k, v) => {
1138
+ if (!removals.has(k)) next[k] = v;
1139
+ });
844
1140
  Object.assign(next, updates);
845
1141
  return next;
846
1142
  }
847
1143
  validate(tag, props) {
848
1144
  const key = _AriaPolicyEngine.#createPlanKey(tag, props);
849
- if (!isNull2(key)) {
1145
+ if (!isNull(key)) {
850
1146
  const cached = this.#planCache.get(key);
851
1147
  if (cached !== void 0) {
852
1148
  this.#planCache.delete(key);
@@ -860,7 +1156,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
860
1156
  }
861
1157
  const result = this.#extraRules.length ? _AriaPolicyEngine.#evaluateWithRules(tag, props, this.#extraRules) : _AriaPolicyEngine.evaluate(tag, props);
862
1158
  if (result.violations.length > 0) this.report(result.violations);
863
- if (!isNull2(key)) {
1159
+ if (!isNull(key)) {
864
1160
  const { removals, updates } = _AriaPolicyEngine.#computePlan(
865
1161
  props,
866
1162
  result.props
@@ -881,12 +1177,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
881
1177
  if (fixes.length === 0) return props;
882
1178
  const sorted = [...fixes].sort((a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity));
883
1179
  let next = props;
884
- for (const { apply } of sorted) {
1180
+ iterate.forEach(sorted, ({ apply }) => {
885
1181
  const effectiveRole = next.role ?? implicitRole;
886
1182
  const fixContext = { tag, implicitRole, effectiveRole, props: next };
887
1183
  const fixResult = apply(fixContext);
888
1184
  if (fixResult.applied) next = fixResult.next;
889
- }
1185
+ });
890
1186
  return next;
891
1187
  }
892
1188
  static #removeRole = {
@@ -914,10 +1210,26 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
914
1210
  _AriaPolicyEngine.#checkInvalidRoleOverride,
915
1211
  _AriaPolicyEngine.#checkRedundantRole,
916
1212
  _AriaPolicyEngine.#checkStandaloneRegion,
1213
+ _AriaPolicyEngine.#checkAriaAttributeValues,
917
1214
  _AriaPolicyEngine.#checkInvalidAriaAttributes,
1215
+ _AriaPolicyEngine.#checkRequiredAriaProperties,
1216
+ _AriaPolicyEngine.#checkNameRequiredRoles,
1217
+ _AriaPolicyEngine.#checkRedundantAriaLevel,
918
1218
  _AriaPolicyEngine.#checkMissingLiveRegion,
919
1219
  _AriaPolicyEngine.#checkMissingAtomic,
920
- _AriaPolicyEngine.#checkInvalidAriaRelevant
1220
+ _AriaPolicyEngine.#checkInvalidAriaRelevant,
1221
+ _AriaPolicyEngine.#checkAriaHiddenOnFocusable,
1222
+ _AriaPolicyEngine.#checkPresentationalAriaAttributes
1223
+ ];
1224
+ // Rules for elements with an implicit role but no explicit role (not a live region).
1225
+ static #implicitOnlyRules = [
1226
+ _AriaPolicyEngine.#checkAriaAttributeValues,
1227
+ _AriaPolicyEngine.#checkInvalidAriaAttributes,
1228
+ _AriaPolicyEngine.#checkRequiredAriaProperties,
1229
+ _AriaPolicyEngine.#checkNameRequiredRoles,
1230
+ _AriaPolicyEngine.#checkRedundantAriaLevel,
1231
+ _AriaPolicyEngine.#checkAriaHiddenOnFocusable,
1232
+ _AriaPolicyEngine.#checkPresentationalAriaAttributes
921
1233
  ];
922
1234
  static #checkInvalidRoleOverride({
923
1235
  tag,
@@ -933,7 +1245,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
933
1245
  fixable: true,
934
1246
  severity: "error",
935
1247
  fix: _AriaPolicyEngine.#removeRole,
936
- message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
1248
+ diagnostic: HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role)
937
1249
  }
938
1250
  ];
939
1251
  }
@@ -948,44 +1260,301 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
948
1260
  fixable: true,
949
1261
  severity: "warning",
950
1262
  fix: _AriaPolicyEngine.#removeRole,
951
- message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
1263
+ diagnostic: HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole)
1264
+ }
1265
+ ];
1266
+ }
1267
+ static #checkStandaloneRegion({ tag, props, implicitRole }) {
1268
+ const role = props.role;
1269
+ if (role !== "region") return VALID;
1270
+ if (!isStandaloneTag(tag)) return VALID;
1271
+ return [
1272
+ {
1273
+ valid: false,
1274
+ fixable: true,
1275
+ severity: "error",
1276
+ fix: _AriaPolicyEngine.#removeRole,
1277
+ diagnostic: HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag)
1278
+ }
1279
+ ];
1280
+ }
1281
+ static #checkInvalidAriaAttributes({
1282
+ tag,
1283
+ props,
1284
+ effectiveRole
1285
+ }) {
1286
+ if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1287
+ const results = [];
1288
+ iterate.forEachEntry(props, (key) => {
1289
+ if (!key.startsWith("aria-")) return;
1290
+ if (isGlobalAriaAttribute(key)) return;
1291
+ if (isAriaAttributeValidForRole(key, effectiveRole)) return;
1292
+ results.push({
1293
+ valid: false,
1294
+ severity: "warning",
1295
+ fixable: true,
1296
+ attribute: key,
1297
+ diagnostic: AriaDiagnostics.attributeInvalid(key, effectiveRole ?? tag),
1298
+ fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
1299
+ });
1300
+ });
1301
+ return results;
1302
+ }
1303
+ // ─── ARIA attribute value validation ──────────────────────────────────────
1304
+ // Accepted value shapes for typed ARIA attributes.
1305
+ // Attributes not in this map are unconstrained (arbitrary string values permitted).
1306
+ static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1307
+ // Boolean (true | false)
1308
+ ["aria-atomic", { kind: "boolean" }],
1309
+ ["aria-busy", { kind: "boolean" }],
1310
+ ["aria-disabled", { kind: "boolean" }],
1311
+ ["aria-expanded", { kind: "boolean" }],
1312
+ ["aria-hidden", { kind: "boolean" }],
1313
+ ["aria-modal", { kind: "boolean" }],
1314
+ ["aria-multiline", { kind: "boolean" }],
1315
+ ["aria-multiselectable", { kind: "boolean" }],
1316
+ ["aria-readonly", { kind: "boolean" }],
1317
+ ["aria-required", { kind: "boolean" }],
1318
+ ["aria-selected", { kind: "boolean" }],
1319
+ // Tristate (true | false | mixed)
1320
+ ["aria-checked", { kind: "tristate" }],
1321
+ ["aria-pressed", { kind: "tristate" }],
1322
+ // Numeric (any finite number)
1323
+ ["aria-valuenow", { kind: "number" }],
1324
+ ["aria-valuemin", { kind: "number" }],
1325
+ ["aria-valuemax", { kind: "number" }],
1326
+ // Integer with optional range
1327
+ ["aria-level", { kind: "integer", min: 1, max: 6 }],
1328
+ ["aria-posinset", { kind: "integer", min: 1 }],
1329
+ ["aria-setsize", { kind: "integer", min: -1 }],
1330
+ ["aria-rowcount", { kind: "integer", min: -1 }],
1331
+ ["aria-colcount", { kind: "integer", min: -1 }],
1332
+ ["aria-rowindex", { kind: "integer", min: 1 }],
1333
+ ["aria-colindex", { kind: "integer", min: 1 }],
1334
+ ["aria-rowspan", { kind: "integer", min: 0 }],
1335
+ ["aria-colspan", { kind: "integer", min: 0 }],
1336
+ // Enum (specific allowed tokens)
1337
+ ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1338
+ [
1339
+ "aria-current",
1340
+ {
1341
+ kind: "enum",
1342
+ values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1343
+ }
1344
+ ],
1345
+ [
1346
+ "aria-haspopup",
1347
+ {
1348
+ kind: "enum",
1349
+ values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1350
+ }
1351
+ ],
1352
+ ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1353
+ ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1354
+ [
1355
+ "aria-orientation",
1356
+ { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
1357
+ ],
1358
+ ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1359
+ ]);
1360
+ static #isValidAriaValue(value, type) {
1361
+ switch (type.kind) {
1362
+ case "boolean":
1363
+ return value === "true" || value === "false" || value === true || value === false;
1364
+ case "tristate":
1365
+ return value === "true" || value === "false" || value === "mixed" || value === true || value === false;
1366
+ case "number": {
1367
+ if (typeof value === "number") return Number.isFinite(value);
1368
+ if (typeof value !== "string") return false;
1369
+ const n = parseFloat(value);
1370
+ return Number.isFinite(n);
1371
+ }
1372
+ case "integer": {
1373
+ const n = typeof value === "number" ? value : typeof value === "string" ? parseInt(value, 10) : NaN;
1374
+ if (!Number.isFinite(n) || !Number.isInteger(n)) return false;
1375
+ if (type.min !== void 0 && n < type.min) return false;
1376
+ if (type.max !== void 0 && n > type.max) return false;
1377
+ return true;
1378
+ }
1379
+ case "enum":
1380
+ return typeof value === "string" && type.values.has(value);
1381
+ }
1382
+ }
1383
+ static #describeExpected(type) {
1384
+ switch (type.kind) {
1385
+ case "boolean":
1386
+ return '"true" or "false"';
1387
+ case "tristate":
1388
+ return '"true", "false", or "mixed"';
1389
+ case "number":
1390
+ return "a finite number";
1391
+ case "integer": {
1392
+ const parts = ["an integer"];
1393
+ if (type.min !== void 0 && type.max !== void 0)
1394
+ parts.push(`between ${type.min} and ${type.max}`);
1395
+ else if (type.min !== void 0) parts.push(`\u2265 ${type.min}`);
1396
+ else if (type.max !== void 0) parts.push(`\u2264 ${type.max}`);
1397
+ return parts.join(" ");
1398
+ }
1399
+ case "enum":
1400
+ return [...type.values].map((v) => `"${v}"`).join(", ");
1401
+ }
1402
+ }
1403
+ static #checkAriaAttributeValues({ props, effectiveRole }) {
1404
+ if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1405
+ const results = [];
1406
+ iterate.forEachEntry(props, (key, value) => {
1407
+ if (!key.startsWith("aria-")) return;
1408
+ const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1409
+ if (type == null) return;
1410
+ if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1411
+ results.push({
1412
+ valid: false,
1413
+ fixable: true,
1414
+ severity: "warning",
1415
+ attribute: key,
1416
+ diagnostic: AriaDiagnostics.invalidAttributeValue(
1417
+ key,
1418
+ value,
1419
+ _AriaPolicyEngine.#describeExpected(type)
1420
+ ),
1421
+ fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
1422
+ });
1423
+ });
1424
+ return results;
1425
+ }
1426
+ // ─── Heading implicit level ────────────────────────────────────────────────
1427
+ static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1428
+ ["h1", 1],
1429
+ ["h2", 2],
1430
+ ["h3", 3],
1431
+ ["h4", 4],
1432
+ ["h5", 5],
1433
+ ["h6", 6]
1434
+ ]);
1435
+ static #checkRedundantAriaLevel({
1436
+ tag,
1437
+ props,
1438
+ effectiveRole
1439
+ }) {
1440
+ if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1441
+ const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1442
+ if (implicitLevel == null) return VALID;
1443
+ const raw = props["aria-level"];
1444
+ if (raw == null) return VALID;
1445
+ const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1446
+ if (!Number.isFinite(n) || n !== implicitLevel) return VALID;
1447
+ return [
1448
+ {
1449
+ valid: false,
1450
+ fixable: true,
1451
+ severity: "warning",
1452
+ attribute: "aria-level",
1453
+ diagnostic: AriaDiagnostics.redundantAriaLevel(tag, implicitLevel),
1454
+ fix: _AriaPolicyEngine.#makeRemoveAttributeFix("aria-level")
1455
+ }
1456
+ ];
1457
+ }
1458
+ // ─── Name-required roles ───────────────────────────────────────────────────
1459
+ // Roles that always require an accessible name per WAI-ARIA APG.
1460
+ // Dialog and landmark names are enforced via contracts (ariaContract) rather than
1461
+ // the built-in pipeline so consumers can opt in; img is built in because role=img
1462
+ // on any element (including bare <img>) is definitionally useless without a name.
1463
+ static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1464
+ static #checkNameRequiredRoles({
1465
+ tag,
1466
+ props,
1467
+ effectiveRole
1468
+ }) {
1469
+ if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole)) return VALID;
1470
+ if ("aria-label" in props || "aria-labelledby" in props) return VALID;
1471
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return VALID;
1472
+ return [
1473
+ {
1474
+ valid: false,
1475
+ fixable: false,
1476
+ severity: "warning",
1477
+ diagnostic: AriaDiagnostics.missingAccessibleName(tag)
952
1478
  }
953
1479
  ];
954
1480
  }
955
- static #checkStandaloneRegion({ tag, props, implicitRole }) {
956
- const role = props.role;
957
- if (role !== "region") return VALID;
958
- if (!isStandaloneTag(tag)) return VALID;
1481
+ // WAI-ARIA 1.2 required states and properties, keyed by role.
1482
+ // Source: https://www.w3.org/TR/wai-aria-1.2/#requiredState
1483
+ static #REQUIRED_PROPERTIES = /* @__PURE__ */ new Map([
1484
+ ["combobox", ["aria-expanded"]],
1485
+ ["option", ["aria-selected"]],
1486
+ ["slider", ["aria-valuenow"]],
1487
+ ["scrollbar", ["aria-controls", "aria-valuenow"]],
1488
+ ["spinbutton", ["aria-valuenow"]]
1489
+ ]);
1490
+ static #checkRequiredAriaProperties({
1491
+ props,
1492
+ effectiveRole
1493
+ }) {
1494
+ if (!effectiveRole) return VALID;
1495
+ const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1496
+ if (required == null) return VALID;
1497
+ const results = [];
1498
+ iterate.forEach(required, (attr) => {
1499
+ if (attr in props) return;
1500
+ results.push({
1501
+ valid: false,
1502
+ fixable: false,
1503
+ severity: "warning",
1504
+ attribute: attr,
1505
+ diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
1506
+ });
1507
+ });
1508
+ return results;
1509
+ }
1510
+ // Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
1511
+ static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1512
+ "a",
1513
+ "button",
1514
+ "input",
1515
+ "select",
1516
+ "textarea"
1517
+ ]);
1518
+ // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1519
+ static #checkAriaHiddenOnFocusable({ tag, props }) {
1520
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return VALID;
1521
+ const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1522
+ if (!isInteractive) {
1523
+ const tabindex = props.tabindex;
1524
+ const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1525
+ if (!Number.isFinite(n) || n < 0) return VALID;
1526
+ }
959
1527
  return [
960
1528
  {
961
1529
  valid: false,
962
- fixable: true,
1530
+ fixable: false,
963
1531
  severity: "error",
964
- fix: _AriaPolicyEngine.#removeRole,
965
- message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
1532
+ attribute: "aria-hidden",
1533
+ diagnostic: AriaDiagnostics.ariaHiddenOnFocusable(tag)
966
1534
  }
967
1535
  ];
968
1536
  }
969
- static #checkInvalidAriaAttributes({
1537
+ // Presentational elements (role=none/presentation, including <img alt="">) are removed
1538
+ // from the accessibility tree — ARIA attributes on them are meaningless and misleading.
1539
+ static #checkPresentationalAriaAttributes({
970
1540
  tag,
971
1541
  props,
972
1542
  effectiveRole
973
1543
  }) {
1544
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return VALID;
974
1545
  const results = [];
975
- for (const key in props) {
976
- if (!Object.hasOwn(props, key)) continue;
977
- if (!key.startsWith("aria-")) continue;
978
- if (isGlobalAriaAttribute(key)) continue;
979
- if (isAriaAttributeValidForRole(key, effectiveRole)) continue;
1546
+ iterate.forEachEntry(props, (key) => {
1547
+ if (!key.startsWith("aria-")) return;
1548
+ if (key === "aria-hidden") return;
980
1549
  results.push({
981
1550
  valid: false,
982
- severity: "warning",
983
1551
  fixable: true,
1552
+ severity: "warning",
984
1553
  attribute: key,
985
- message: `"${key}" is not valid on role="${effectiveRole ?? tag}". It will be removed.`,
1554
+ diagnostic: AriaDiagnostics.attributeOnPresentational(key, tag),
986
1555
  fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
987
1556
  });
988
- }
1557
+ });
989
1558
  return results;
990
1559
  }
991
1560
  // WAI-ARIA live region roles and their implied aria-live politeness values.
@@ -1014,7 +1583,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1014
1583
  fixable: true,
1015
1584
  severity: "warning",
1016
1585
  fix: injectLive,
1017
- message: `role="${effectiveRole}" implies aria-live="${impliedLive}" but it is missing. It has been injected.`
1586
+ diagnostic: AriaDiagnostics.missingLiveRegion(effectiveRole, impliedLive)
1018
1587
  }
1019
1588
  ];
1020
1589
  }
@@ -1026,7 +1595,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1026
1595
  valid: false,
1027
1596
  fixable: false,
1028
1597
  severity: "warning",
1029
- message: `role="${effectiveRole}" is a live region. Consider setting aria-atomic="true" if the full region should be announced as a unit, or aria-atomic="false" if only changed nodes should be read.`
1598
+ diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
1030
1599
  }
1031
1600
  ];
1032
1601
  }
@@ -1055,7 +1624,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1055
1624
  fixable: true,
1056
1625
  severity: "warning",
1057
1626
  attribute: "aria-relevant",
1058
- message: `aria-relevant contains invalid token(s): ${invalid.map((t) => `"${t}"`).join(", ")}. Valid tokens are: additions, removals, text, all.`,
1627
+ diagnostic: AriaDiagnostics.relevantInvalidTokens(invalid),
1059
1628
  fix: _AriaPolicyEngine.#makeRemoveAttributeFix("aria-relevant")
1060
1629
  }
1061
1630
  ];
@@ -1067,7 +1636,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1067
1636
  fixable: true,
1068
1637
  severity: "warning",
1069
1638
  attribute: "aria-relevant",
1070
- message: `aria-relevant includes "all" alongside other tokens. "all" supersedes additions, removals, and text \u2014 use aria-relevant="all" alone.`,
1639
+ diagnostic: AriaDiagnostics.relevantSuperseded(),
1071
1640
  fix: _AriaPolicyEngine.#normalizeRelevantAllFix
1072
1641
  }
1073
1642
  ];
@@ -1075,6 +1644,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1075
1644
  return VALID;
1076
1645
  }
1077
1646
  };
1647
+
1648
+ // ../../lib/contract/src/children/get-type-name.ts
1078
1649
  function getTypeName(value) {
1079
1650
  if (value === null) return "null";
1080
1651
  if (value === void 0) return "undefined";
@@ -1085,39 +1656,8 @@ function getTypeName(value) {
1085
1656
  const name = value.constructor?.name;
1086
1657
  return typeof name === "string" && name !== "Object" ? name : "object";
1087
1658
  }
1088
- var MatchValidationErrorBuilder = class {
1089
- #prefix;
1090
- constructor(ctx = "") {
1091
- this.#prefix = ctx ? `${ctx}:
1092
- ` : "";
1093
- }
1094
- #template(typeName, index, prefix = "", suffix = "") {
1095
- const leadingStr = prefix ? `${prefix} ` : "";
1096
- const followingStr = suffix ? ` ${suffix}` : "";
1097
- return `${leadingStr}child "${typeName}" at index ${index}${followingStr}.`;
1098
- }
1099
- unexpectedChild(typeName, index) {
1100
- return this.#template(typeName, index, "unexpected");
1101
- }
1102
- multipleMatches(typeName, index, ruleNames) {
1103
- const quoted = ruleNames.map((n) => `"${n}"`);
1104
- return this.#template(
1105
- typeName,
1106
- index,
1107
- "",
1108
- `matches multiple child rules: ${quoted.join(" and ")}`
1109
- );
1110
- }
1111
- #format(errors) {
1112
- return this.#prefix + errors.join("\n");
1113
- }
1114
- toError(errors) {
1115
- if (errors.length === 0) {
1116
- return new Error(this.#prefix + "Unknown validation error.");
1117
- }
1118
- return new Error(this.#format(errors));
1119
- }
1120
- };
1659
+
1660
+ // ../../lib/contract/src/children/normalize-child-rule.ts
1121
1661
  function normalizeCardinality(input, impliesSingleton) {
1122
1662
  const min = input?.min ?? 0;
1123
1663
  const max = input?.max ?? (impliesSingleton ? 1 : Infinity);
@@ -1142,6 +1682,8 @@ function normalizeChildRule(rule) {
1142
1682
  cardinality: normalizeCardinality(rule.cardinality, impliesSingleton)
1143
1683
  };
1144
1684
  }
1685
+
1686
+ // ../../lib/contract/src/children/rules-matcher.ts
1145
1687
  function getChildType(child) {
1146
1688
  if (!isObject(child) || !("type" in child)) return void 0;
1147
1689
  return child.type;
@@ -1150,8 +1692,8 @@ function buildPartialIndex(rules) {
1150
1692
  const typeIndex = /* @__PURE__ */ new Map();
1151
1693
  const duplicateTypes = /* @__PURE__ */ new Set();
1152
1694
  const untypedIndices = [];
1153
- for (let ri = 0; ri < rules.length; ri++) {
1154
- const t = rules[ri].type;
1695
+ iterate.forEach(rules, (rule, ri) => {
1696
+ const t = rule.type;
1155
1697
  if (t === void 0) {
1156
1698
  untypedIndices.push(ri);
1157
1699
  } else if (typeIndex.has(t)) {
@@ -1159,12 +1701,14 @@ function buildPartialIndex(rules) {
1159
1701
  } else {
1160
1702
  typeIndex.set(t, ri);
1161
1703
  }
1162
- }
1704
+ });
1163
1705
  if (duplicateTypes.size > 0) {
1164
- for (const t of duplicateTypes) typeIndex.delete(t);
1165
- for (let ri = 0; ri < rules.length; ri++) {
1166
- if (duplicateTypes.has(rules[ri].type)) untypedIndices.push(ri);
1167
- }
1706
+ iterate.forEachSet(duplicateTypes, (t) => {
1707
+ typeIndex.delete(t);
1708
+ });
1709
+ iterate.forEach(rules, (rule, ri) => {
1710
+ if (duplicateTypes.has(rule.type)) untypedIndices.push(ri);
1711
+ });
1168
1712
  }
1169
1713
  return { typeIndex, untypedIndices };
1170
1714
  }
@@ -1183,10 +1727,10 @@ var RuleMatcher = class {
1183
1727
  const reverse = /* @__PURE__ */ new Map();
1184
1728
  const unexpectedIndices = /* @__PURE__ */ new Set();
1185
1729
  const ambiguousIndices = /* @__PURE__ */ new Set();
1186
- for (let ri = 0; ri < this.#rules.length; ri++) {
1730
+ iterate.forEach(this.#rules, (_, ri) => {
1187
1731
  reverse.set(ri, /* @__PURE__ */ new Set());
1188
- }
1189
- for (const [ci, child] of children.entries()) {
1732
+ });
1733
+ iterate.forEach(children, (child, ci) => {
1190
1734
  const t = getChildType(child);
1191
1735
  if (t !== void 0) {
1192
1736
  const ri = this.#typeIndex.get(t);
@@ -1200,8 +1744,8 @@ var RuleMatcher = class {
1200
1744
  reverse.get(ri).add(ci);
1201
1745
  }
1202
1746
  }
1203
- for (const ri of this.#untypedIndices) {
1204
- if (!this.#rules[ri].match(child)) continue;
1747
+ iterate.forEach(this.#untypedIndices, (ri) => {
1748
+ if (!this.#rules[ri].match(child)) return;
1205
1749
  let childEntry = forward.get(ci);
1206
1750
  if (!childEntry) {
1207
1751
  childEntry = /* @__PURE__ */ new Set();
@@ -1209,33 +1753,35 @@ var RuleMatcher = class {
1209
1753
  }
1210
1754
  childEntry.add(ri);
1211
1755
  reverse.get(ri).add(ci);
1212
- }
1756
+ });
1213
1757
  const entry = forward.get(ci);
1214
1758
  if (!entry) {
1215
1759
  unexpectedIndices.add(ci);
1216
1760
  } else if (entry.size > 1) {
1217
1761
  ambiguousIndices.add(ci);
1218
1762
  }
1219
- }
1763
+ });
1220
1764
  return { matrix: { childToRules: { forward, reverse } }, unexpectedIndices, ambiguousIndices };
1221
1765
  }
1222
1766
  };
1223
- var RuleValidator = class _RuleValidator extends StrictBase {
1767
+
1768
+ // ../../lib/contract/src/children/rule-validator.ts
1769
+ var RuleValidator = class _RuleValidator extends InvariantBase {
1224
1770
  #context;
1225
- constructor(context, strict) {
1226
- super(strict);
1771
+ constructor(context, diagnostics) {
1772
+ super(diagnostics);
1227
1773
  this.#context = context;
1228
1774
  }
1229
1775
  validate(rules, matrix, childCount) {
1230
1776
  const firstIndex = 0;
1231
1777
  const lastIndex = childCount - 1;
1232
- for (const [ri, rule] of rules.entries()) {
1778
+ iterate.forEach(rules, (rule, ri) => {
1233
1779
  const matches = matrix.childToRules.reverse.get(ri);
1234
1780
  const matchCount = matches.size;
1235
1781
  this.#validateCardinality(rule, matchCount);
1236
- if (matchCount === 0) continue;
1782
+ if (matchCount === 0) return;
1237
1783
  this.#validatePositions(rule, matches, firstIndex, lastIndex);
1238
- }
1784
+ });
1239
1785
  }
1240
1786
  #validateCardinality(rule, matchCount) {
1241
1787
  const { cardinality, name } = rule;
@@ -1244,21 +1790,21 @@ var RuleValidator = class _RuleValidator extends StrictBase {
1244
1790
  }
1245
1791
  const { min, max } = cardinality;
1246
1792
  if (matchCount < min) {
1247
- this.violate(`${this.#context}: "${name}" requires at least ${min}.`);
1793
+ this.violate(ContractDiagnostics.cardinalityMin(name, min, this.#context));
1248
1794
  return;
1249
1795
  }
1250
1796
  if (matchCount > max) {
1251
- this.violate(`${this.#context}: "${name}" allows at most ${max}.`);
1797
+ this.violate(ContractDiagnostics.cardinalityMax(name, max, this.#context));
1252
1798
  }
1253
1799
  }
1254
1800
  #validatePositions(rule, matches, firstIndex, lastIndex) {
1255
1801
  const { name, position } = rule;
1256
- for (const index of matches) {
1802
+ iterate.forEachSet(matches, (index) => {
1257
1803
  if (_RuleValidator.#isValidPosition(index, position, firstIndex, lastIndex)) {
1258
- continue;
1804
+ return;
1259
1805
  }
1260
- this.violate(`${this.#context}: "${name}" must be ${position}, got index ${index}`);
1261
- }
1806
+ this.violate(ContractDiagnostics.positionViolation(name, position, index, this.#context));
1807
+ });
1262
1808
  }
1263
1809
  static #isValidPosition(matchIndex, position, firstIndex, lastIndex) {
1264
1810
  switch (position) {
@@ -1273,48 +1819,50 @@ var RuleValidator = class _RuleValidator extends StrictBase {
1273
1819
  }
1274
1820
  }
1275
1821
  };
1276
- var ChildrenEvaluator = class extends StrictBase {
1822
+
1823
+ // ../../lib/contract/src/children/children-evaluator.ts
1824
+ var ChildrenEvaluator = class extends InvariantBase {
1825
+ #context;
1277
1826
  #rules;
1278
1827
  #ruleNames;
1279
1828
  #matcher;
1280
1829
  #ruleValidator;
1281
- #matchBuilder;
1282
- constructor(rules, strict = "warn", context = "Component") {
1283
- super(strict);
1284
- this.#rules = rules.map((r2) => normalizeChildRule(r2));
1285
- this.#ruleNames = this.#rules.map((r2) => r2.name);
1286
- for (const rule of this.#rules) {
1830
+ constructor(rules, diagnostics, context = "Component") {
1831
+ super(diagnostics);
1832
+ this.#context = context;
1833
+ this.#rules = rules.map((r) => normalizeChildRule(r));
1834
+ this.#ruleNames = this.#rules.map((r) => r.name);
1835
+ iterate.forEach(this.#rules, (rule) => {
1287
1836
  const { name, position, cardinality } = rule;
1288
1837
  if ((position === "first" || position === "last") && (cardinality.kind === "unbounded" || cardinality.max > 1)) {
1289
1838
  throw new RangeError(
1290
1839
  `ChildrenEvaluator [${context}]: rule "${name}" sets position="${position}" with an unbound or >1 max. position="first|last" implies max=1.`
1291
1840
  );
1292
1841
  }
1293
- }
1842
+ });
1294
1843
  this.#matcher = new RuleMatcher(this.#rules);
1295
- this.#ruleValidator = new RuleValidator(context, strict);
1296
- this.#matchBuilder = new MatchValidationErrorBuilder(context);
1844
+ this.#ruleValidator = new RuleValidator(context, diagnostics);
1297
1845
  }
1298
1846
  evaluate(children) {
1299
- if (!this.strict) return;
1847
+ if (!this.active) return;
1300
1848
  const { matrix, unexpectedIndices, ambiguousIndices } = this.#matcher.match(children);
1301
1849
  this.#ruleValidator.validate(this.#rules, matrix, children.length);
1302
1850
  if (unexpectedIndices.size === 0 && ambiguousIndices.size === 0) return;
1303
- const errors = [];
1304
1851
  const violating = [...unexpectedIndices, ...ambiguousIndices].sort((a, b) => a - b);
1305
- for (const ci of violating) {
1852
+ iterate.forEach(violating, (ci) => {
1306
1853
  const typeName = getTypeName(children[ci]);
1307
1854
  if (unexpectedIndices.has(ci)) {
1308
- errors.push(this.#matchBuilder.unexpectedChild(typeName, ci));
1855
+ this.violate(ContractDiagnostics.unexpectedChild(typeName, ci, this.#context));
1309
1856
  } else {
1310
1857
  const matches = matrix.childToRules.forward.get(ci);
1311
1858
  const names = [...matches].map((ri) => this.#ruleNames[ri] ?? `#${ri}`);
1312
- errors.push(this.#matchBuilder.multipleMatches(typeName, ci, names));
1859
+ this.violate(ContractDiagnostics.ambiguousChild(typeName, ci, names, this.#context));
1313
1860
  }
1314
- }
1315
- this.invariant(errors.length === 0, this.#matchBuilder.toError(errors).message);
1861
+ });
1316
1862
  }
1317
1863
  };
1864
+
1865
+ // ../../lib/contract/src/props/get-disabled-props.ts
1318
1866
  var disabledProps = ({
1319
1867
  disabled,
1320
1868
  "aria-disabled": ariaDisabled,
@@ -1326,6 +1874,8 @@ var disabledProps = ({
1326
1874
  ...dataDisabled === void 0 && { "data-disabled": "" }
1327
1875
  };
1328
1876
  };
1877
+
1878
+ // ../../lib/contract/src/props/get-invalid-props.ts
1329
1879
  var invalidProps = ({
1330
1880
  invalid,
1331
1881
  "aria-invalid": ariaInvalid,
@@ -1337,6 +1887,8 @@ var invalidProps = ({
1337
1887
  ...dataInvalid === void 0 && { "data-invalid": "" }
1338
1888
  };
1339
1889
  };
1890
+
1891
+ // ../../lib/contract/src/props/get-readonly-props.ts
1340
1892
  var readonlyProps = ({
1341
1893
  readOnly,
1342
1894
  "aria-readonly": ariaReadonly,
@@ -1352,6 +1904,8 @@ var readonlyProps = ({
1352
1904
  }
1353
1905
  };
1354
1906
  };
1907
+
1908
+ // ../core/src/html/aria-rules.ts
1355
1909
  var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
1356
1910
  var removeLandmarkRoleOverride = {
1357
1911
  kind: "removeRole",
@@ -1371,154 +1925,29 @@ function landmarkRoleRule({ tag, props, implicitRole }) {
1371
1925
  fixable: true,
1372
1926
  severity: "error",
1373
1927
  fix: removeLandmarkRoleOverride,
1374
- message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
1928
+ diagnostic: HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role)
1375
1929
  }
1376
1930
  ];
1377
1931
  }
1378
- var HTML_ARIA_RULES = [landmarkRoleRule];
1379
- function isOpenContent(...blockedTags) {
1380
- const set = new Set(blockedTags);
1381
- return (child) => isObject(child) && "type" in child && (!isString(child.type) || !set.has(child.type));
1382
- }
1383
- var METADATA_TAGS = ["script", "template"];
1384
- var metadataMatch = isTag(...METADATA_TAGS);
1385
- function metadata(name = "metadata") {
1386
- return { name, match: metadataMatch };
1387
- }
1388
- function firstOptional(name, tag) {
1389
- return { name, match: isTag(tag), cardinality: { max: 1 }, position: "first" };
1390
- }
1391
- function contract(children) {
1392
- return { strict: "warn", children };
1393
- }
1394
- function ariaContract(aria) {
1395
- return { strict: "warn", aria };
1396
- }
1397
- function firstChildContract(name, tag) {
1398
- return contract([firstOptional(name, tag), { name: "content", match: isOpenContent(tag) }]);
1399
- }
1400
- var VOID_TAGS = [
1401
- "area",
1402
- "base",
1403
- "br",
1404
- "col",
1405
- "embed",
1406
- "hr",
1407
- "img",
1408
- "input",
1409
- "link",
1410
- "meta",
1411
- "param",
1412
- "source",
1413
- "track",
1414
- "wbr"
1415
- ];
1416
- var TEXT_ONLY_TAGS = ["option", "script", "style", "textarea", "title"];
1417
- var LANDMARK_TAGS = ["article", "aside", "footer", "header", "main", "nav"];
1418
- var listContract = contract([{ name: "list-item", match: isTag("li", ...METADATA_TAGS) }]);
1419
- var tableContract = contract([
1420
- firstOptional("caption", "caption"),
1421
- { name: "colgroup", match: isTag("colgroup") },
1422
- { name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
1423
- { name: "tbody", match: isTag("tbody") },
1424
- { name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
1425
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
1426
- ]);
1427
- var tableBodyContract = contract([
1428
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
1429
- ]);
1430
- var tableRowContract = contract([
1431
- { name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
1432
- ]);
1433
- var colgroupContract = contract([{ name: "column", match: isTag("col", "template") }]);
1434
- var dlContract = contract([
1435
- { name: "term", match: isTag("dt") },
1436
- { name: "description", match: isTag("dd") },
1437
- { name: "group", match: isTag("div") },
1438
- metadata()
1439
- ]);
1440
- var selectContract = contract([
1441
- { name: "option", match: isTag("option", "optgroup", "hr", ...METADATA_TAGS) }
1442
- ]);
1443
- var optgroupContract = contract([
1444
- { name: "option", match: isTag("option", ...METADATA_TAGS) }
1445
- ]);
1446
- var datalistContract = contract([
1447
- { name: "option", match: isTag("option", ...METADATA_TAGS) }
1448
- ]);
1449
- var pictureContract = contract([
1450
- { name: "source", match: isTag("source", ...METADATA_TAGS) },
1451
- { name: "image", match: isTag("img"), cardinality: { min: 1, max: 1 }, position: "last" }
1452
- ]);
1453
- var figureContract = contract([
1454
- { name: "caption", match: isTag("figcaption"), cardinality: { max: 1 } },
1455
- { name: "content", match: isOpenContent("figcaption") }
1456
- ]);
1457
- var detailsContract = firstChildContract("summary", "summary");
1458
- var fieldsetContract = firstChildContract("legend", "legend");
1459
- var mediaContract = contract([
1460
- { name: "source", match: isTag("source") },
1461
- { name: "track", match: isTag("track") },
1462
- metadata(),
1463
- { name: "content", match: isOpenContent("source", "track", ...METADATA_TAGS) }
1464
- ]);
1465
- var headContract = contract([
1466
- {
1467
- name: "metadata",
1468
- match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
1469
- }
1470
- ]);
1471
- var htmlContract = contract([
1472
- { name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
1473
- { name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
1474
- ]);
1475
- var voidContract = contract([]);
1476
- var textOnlyContract = contract([
1477
- {
1478
- name: "text",
1479
- match: (child) => isString(child) || isNumber(child)
1480
- }
1481
- ]);
1482
- var landmarkContract = ariaContract([landmarkRoleRule]);
1483
- var CONTRACT_GROUPS = [
1484
- [VOID_TAGS, voidContract],
1485
- [TEXT_ONLY_TAGS, textOnlyContract],
1486
- [LANDMARK_TAGS, landmarkContract],
1487
- [["ul", "ol", "menu"], listContract],
1488
- [["audio", "video"], mediaContract],
1489
- [["thead", "tbody", "tfoot"], tableBodyContract]
1490
- ];
1491
- function contractMap(groups) {
1492
- return Object.fromEntries(
1493
- groups.flatMap(([tags, enforcement]) => tags.map((tag) => [tag, enforcement]))
1494
- );
1495
- }
1496
- var htmlContracts = {
1497
- ...contractMap(CONTRACT_GROUPS),
1498
- table: tableContract,
1499
- tr: tableRowContract,
1500
- colgroup: colgroupContract,
1501
- dl: dlContract,
1502
- select: selectContract,
1503
- optgroup: optgroupContract,
1504
- datalist: datalistContract,
1505
- picture: pictureContract,
1506
- figure: figureContract,
1507
- details: detailsContract,
1508
- fieldset: fieldsetContract,
1509
- head: headContract,
1510
- html: htmlContract
1511
- };
1512
- function buildEvaluatorMap() {
1513
- const map = /* @__PURE__ */ new Map();
1514
- for (const [tag, { children }] of Object.entries(htmlContracts)) {
1515
- if (children?.length) {
1516
- map.set(tag, new ChildrenEvaluator(children, "warn", `<${tag}>`));
1932
+ function requireAccessibleName({ tag, props }) {
1933
+ if ("aria-label" in props || "aria-labelledby" in props) return [];
1934
+ return [
1935
+ {
1936
+ valid: false,
1937
+ fixable: false,
1938
+ severity: "warning",
1939
+ diagnostic: AriaDiagnostics.missingAccessibleName(tag)
1517
1940
  }
1518
- }
1519
- return map;
1941
+ ];
1520
1942
  }
1521
- var HTML_EVALUATORS = buildEvaluatorMap();
1943
+ var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
1944
+ function landmarkNameAdvisory(ctx) {
1945
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
1946
+ return requireAccessibleName(ctx);
1947
+ }
1948
+ var HTML_ARIA_RULES = [landmarkRoleRule, landmarkNameAdvisory];
1949
+
1950
+ // ../core/src/html/prop-normalizers.ts
1522
1951
  var HTML_FORM_NORMALIZERS = /* @__PURE__ */ new Map([
1523
1952
  ["button", [disabledProps]],
1524
1953
  ["input", [disabledProps, readonlyProps, invalidProps]],
@@ -1531,9 +1960,10 @@ function getHtmlPropNormalizers(tag) {
1531
1960
  return typeof tag === "string" ? HTML_FORM_NORMALIZERS.get(tag) : void 0;
1532
1961
  }
1533
1962
 
1534
- // ../core/dist/chunk-3T4EM5FG.js
1963
+ // ../../node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.mjs
1964
+ import { clsx as clsx2 } from "clsx";
1535
1965
  var falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
1536
- var cx = clsx;
1966
+ var cx = clsx2;
1537
1967
  var cva = (base, config) => (props) => {
1538
1968
  var _config_compoundVariants;
1539
1969
  if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
@@ -1572,10 +2002,14 @@ var cva = (base, config) => (props) => {
1572
2002
  }, []);
1573
2003
  return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
1574
2004
  };
2005
+
2006
+ // ../../lib/styling/src/cva.ts
1575
2007
  function cva2(base, config) {
1576
2008
  const fn = cva(base, config);
1577
2009
  return (props) => cn(fn(props));
1578
2010
  }
2011
+
2012
+ // ../../lib/styling/src/static-class-resolver.ts
1579
2013
  var StaticClassResolver = class {
1580
2014
  #baseClass;
1581
2015
  #cache = /* @__PURE__ */ new Map();
@@ -1606,6 +2040,8 @@ var StaticClassResolver = class {
1606
2040
  return result;
1607
2041
  }
1608
2042
  };
2043
+
2044
+ // ../../lib/styling/src/variant-class-resolver.ts
1609
2045
  var VariantClassResolver = class _VariantClassResolver {
1610
2046
  #cvaFn;
1611
2047
  #recipeMap;
@@ -1653,15 +2089,15 @@ var VariantClassResolver = class _VariantClassResolver {
1653
2089
  #createCacheKey(props, recipe) {
1654
2090
  if (this.#variantKeys !== null) {
1655
2091
  let key2 = recipe;
1656
- for (const k of this.#variantKeys) {
2092
+ iterate.forEachSet(this.#variantKeys, (k) => {
1657
2093
  if (k in props) key2 += `|${k}:${_VariantClassResolver.#serializeValue(props[k])}`;
1658
- }
2094
+ });
1659
2095
  return key2;
1660
2096
  }
1661
2097
  let key = recipe;
1662
- for (const k of Object.keys(props).sort()) {
2098
+ iterate.forEach(Object.keys(props).sort(), (k) => {
1663
2099
  key += `|${k}:${_VariantClassResolver.#serializeValue(props[k])}`;
1664
- }
2100
+ });
1665
2101
  return key;
1666
2102
  }
1667
2103
  static #serializeValue(value) {
@@ -1672,6 +2108,8 @@ var VariantClassResolver = class _VariantClassResolver {
1672
2108
  return `x:${String(value)}`;
1673
2109
  }
1674
2110
  };
2111
+
2112
+ // ../../lib/styling/src/create-class-pipeline.ts
1675
2113
  function createClassPipeline(resolved) {
1676
2114
  const baseClass = resolved.baseClassName ?? "";
1677
2115
  const cvaFn = resolved.variants ? cva2("", {
@@ -1696,7 +2134,203 @@ function createClassPipeline(resolved) {
1696
2134
  };
1697
2135
  }
1698
2136
 
1699
- // ../core/dist/index.js
2137
+ // ../core/src/options/resolve-factory-options.ts
2138
+ var EMPTY_VARIANT_KEYS = /* @__PURE__ */ new Set();
2139
+ function composeNormalizers(normalizers, fn) {
2140
+ if (!normalizers?.length) return fn;
2141
+ return ((props) => {
2142
+ const patched = normalizers.reduce(
2143
+ (acc, normalizer) => ({ ...acc, ...normalizer(acc) }),
2144
+ props
2145
+ );
2146
+ return fn ? fn(patched) : patched;
2147
+ });
2148
+ }
2149
+ function whenDefined(key, value) {
2150
+ return value === void 0 ? {} : { [key]: value };
2151
+ }
2152
+ function resolveFactoryOptions(options = {}) {
2153
+ const { styling, enforcement } = options;
2154
+ const composedNormalizeFn = composeNormalizers(enforcement?.props, options.normalize);
2155
+ const variantKeys = styling?.variants === void 0 ? EMPTY_VARIANT_KEYS : new Set(Object.keys(styling.variants));
2156
+ return Object.freeze({
2157
+ defaultTag: options.tag ?? "div",
2158
+ diagnostics: enforcement?.diagnostics ?? silentDiagnostics,
2159
+ variantKeys,
2160
+ ...whenDefined("displayName", options.name),
2161
+ ...whenDefined("defaultProps", options.defaults),
2162
+ ...whenDefined("baseClassName", styling?.base),
2163
+ ...whenDefined("tagMap", styling?.tags),
2164
+ ...whenDefined("recipeMap", styling?.presets),
2165
+ ...whenDefined("variants", styling?.variants),
2166
+ ...whenDefined("defaultVariants", styling?.defaults),
2167
+ ...whenDefined("compoundVariants", styling?.compounds),
2168
+ ...whenDefined("normalizeFn", composedNormalizeFn),
2169
+ ...whenDefined("ariaRules", enforcement?.aria),
2170
+ ...whenDefined("childRules", enforcement?.children),
2171
+ ...whenDefined("allowedAs", enforcement?.allowedAs),
2172
+ ...whenDefined("precomputedClasses", styling?.precomputedClasses)
2173
+ });
2174
+ }
2175
+
2176
+ // ../core/src/options/validate-factory-options.ts
2177
+ function validateFactoryOptions(resolved, diagnostics) {
2178
+ if (!diagnostics.active) return;
2179
+ const name = resolved.displayName ?? "Component";
2180
+ const { variants } = resolved;
2181
+ const checkSelection = (label2, selection) => {
2182
+ iterate.forEachEntry(selection, (dim, value) => {
2183
+ if (value === void 0 || value === null) return;
2184
+ if (!variants || !Object.hasOwn(variants, dim)) {
2185
+ diagnostics.error(ContractDiagnostics.unknownVariantDim(name, label2, dim));
2186
+ return;
2187
+ }
2188
+ const states = variants[dim];
2189
+ const stateKey = String(value);
2190
+ if (!Object.hasOwn(states, stateKey)) {
2191
+ diagnostics.error(
2192
+ ContractDiagnostics.unknownVariantValue(name, label2, dim, stateKey, Object.keys(states))
2193
+ );
2194
+ }
2195
+ });
2196
+ };
2197
+ const { recipeMap } = resolved;
2198
+ if (recipeMap) {
2199
+ iterate.forEachEntry(recipeMap, (recipeKey, selection) => {
2200
+ checkSelection(`preset "${recipeKey}"`, selection);
2201
+ });
2202
+ }
2203
+ if (resolved.defaultVariants) checkSelection("defaults", resolved.defaultVariants);
2204
+ }
2205
+
2206
+ // ../core/src/options/validate-render-props.ts
2207
+ function label(name) {
2208
+ return name ? `[${name}]` : "[createContractComponent]";
2209
+ }
2210
+ function validateRenderProps(diagnostics, options, props, recipeKey) {
2211
+ const { recipeMap, variants, displayName } = options;
2212
+ const tag = label(displayName);
2213
+ if (recipeKey !== void 0 && (!recipeMap || !Object.hasOwn(recipeMap, recipeKey))) {
2214
+ diagnostics.error(ContractDiagnostics.unknownRecipeKey(tag, recipeKey));
2215
+ }
2216
+ if (variants) {
2217
+ iterate.forEachKey(variants, (key) => {
2218
+ if (!Object.hasOwn(props, key)) return;
2219
+ const value = props[key];
2220
+ if (value === void 0 || value === null) return;
2221
+ const dim = variants[key];
2222
+ if (dim && !Object.hasOwn(dim, String(value))) {
2223
+ diagnostics.error(ContractDiagnostics.invalidVariantValue(tag, key, String(value)));
2224
+ }
2225
+ });
2226
+ }
2227
+ }
2228
+
2229
+ // ../core/src/factory/plugin-diagnostics.ts
2230
+ var PluginDiagnostics = {
2231
+ invalidShape(received) {
2232
+ const got = received === null ? "null" : typeof received;
2233
+ return {
2234
+ code: "PLUGIN7001" /* PluginInvalidShape */,
2235
+ category: 7 /* Internal */,
2236
+ message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
2237
+ };
2238
+ },
2239
+ pipelineReturnType(received) {
2240
+ const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
2241
+ return {
2242
+ code: "PLUGIN7002" /* PluginPipelineReturnType */,
2243
+ category: 7 /* Internal */,
2244
+ message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
2245
+ };
2246
+ }
2247
+ };
2248
+
2249
+ // ../core/src/factory/plugin-invariants.ts
2250
+ function assertPluginShape(result) {
2251
+ if (result === null || typeof result !== "object")
2252
+ throwDiagnostics.error(PluginDiagnostics.invalidShape(result));
2253
+ const plugin = result;
2254
+ if (typeof plugin.pipeline !== "function")
2255
+ throwDiagnostics.error(PluginDiagnostics.invalidShape(result));
2256
+ }
2257
+ function guardPipeline(pipeline) {
2258
+ if (process.env.NODE_ENV === "production") return pipeline;
2259
+ return function guardedPipeline(tag, props, className, recipe) {
2260
+ const result = pipeline(tag, props, className, recipe);
2261
+ if (typeof result !== "string")
2262
+ throwDiagnostics.error(PluginDiagnostics.pipelineReturnType(result));
2263
+ return result;
2264
+ };
2265
+ }
2266
+
2267
+ // ../core/src/factory/create-polymorphic.ts
2268
+ var NOOP_CLASS_PIPELINE = (_tag, _props, className) => Array.isArray(className) ? className.join(" ") : className ?? "";
2269
+ function resolveClassPipeline(options, resolved, diagnostics, capabilities) {
2270
+ const factory = options.styling?.plugin;
2271
+ if (!factory) {
2272
+ const createClassPipeline2 = capabilities?.createClassPipeline;
2273
+ const classPipeline2 = createClassPipeline2 ? createClassPipeline2(resolved) : NOOP_CLASS_PIPELINE;
2274
+ return { pluginResult: void 0, classPipeline: classPipeline2 };
2275
+ }
2276
+ const pluginResult = factory(resolved, diagnostics);
2277
+ assertPluginShape(pluginResult);
2278
+ const classPipeline = guardPipeline(pluginResult.pipeline);
2279
+ return { pluginResult, classPipeline };
2280
+ }
2281
+ function createRuntimeMethods(resolved, classPipeline, engine, renderDiagnostics) {
2282
+ return {
2283
+ resolveTag: makeResolveTag(resolved.defaultTag),
2284
+ resolveProps(props) {
2285
+ return mergeProps(resolved.defaultProps, props);
2286
+ },
2287
+ resolveClasses(tag, props, className, recipe) {
2288
+ if (process.env.NODE_ENV !== "production") {
2289
+ validateRenderProps(renderDiagnostics, resolved, props, recipe);
2290
+ }
2291
+ return classPipeline(tag, props, className, recipe);
2292
+ },
2293
+ resolveAria(tag, props) {
2294
+ if (!engine) return { props };
2295
+ const result = engine.validate(tag, props);
2296
+ return { props: result.props };
2297
+ }
2298
+ };
2299
+ }
2300
+ function createRuntimeObject(methods, resolved, pluginResult) {
2301
+ return pluginResult ? { ...methods, options: resolved, hasStyling: true, classPlugin: pluginResult } : { ...methods, options: resolved };
2302
+ }
2303
+ function createPolymorphic(options = {}, capabilities) {
2304
+ const baseResolved = resolveFactoryOptions(options);
2305
+ const resolved = capabilities?.htmlPropNormalizersFn !== void 0 ? Object.freeze({
2306
+ ...baseResolved,
2307
+ htmlPropNormalizersFn: capabilities.htmlPropNormalizersFn
2308
+ }) : baseResolved;
2309
+ if (process.env.NODE_ENV !== "production") {
2310
+ validateFactoryOptions(resolved, resolved.diagnostics);
2311
+ }
2312
+ const { pluginResult, classPipeline } = resolveClassPipeline(
2313
+ options,
2314
+ resolved,
2315
+ resolved.diagnostics,
2316
+ capabilities
2317
+ );
2318
+ const allAriaRules = [
2319
+ .../* @__PURE__ */ new Set([...capabilities?.htmlAriaRules ?? [], ...resolved.ariaRules ?? []])
2320
+ ];
2321
+ const engine = options.enforcement !== void 0 && capabilities?.AriaEngine ? new capabilities.AriaEngine(
2322
+ resolved.diagnostics,
2323
+ allAriaRules.length ? { rules: allAriaRules } : void 0
2324
+ ) : null;
2325
+ const methods = createRuntimeMethods(resolved, classPipeline, engine, resolved.diagnostics);
2326
+ return createRuntimeObject(
2327
+ methods,
2328
+ resolved,
2329
+ pluginResult
2330
+ );
2331
+ }
2332
+
2333
+ // ../core/src/factory/create-polymorphic-full.ts
1700
2334
  var FULL_CAPABILITIES = {
1701
2335
  createClassPipeline,
1702
2336
  AriaEngine: AriaPolicyEngine,
@@ -1707,6 +2341,11 @@ function createPolymorphic2(options = {}) {
1707
2341
  return createPolymorphic(options, FULL_CAPABILITIES);
1708
2342
  }
1709
2343
 
2344
+ // ../../lib/adapter-utils/src/define-component.ts
2345
+ function defineContractComponent(options) {
2346
+ return (factory) => factory(options);
2347
+ }
2348
+
1710
2349
  // ../../lib/adapter-utils/src/build-core-runtime.ts
1711
2350
  var EMPTY_SET = /* @__PURE__ */ new Set();
1712
2351
  function buildCoreRuntime(normalized) {
@@ -1716,8 +2355,8 @@ function buildCoreRuntime(normalized) {
1716
2355
  }
1717
2356
 
1718
2357
  // ../../lib/adapter-utils/src/build-engines.ts
1719
- function buildEngines(strict, childRules, context) {
1720
- return childRules?.length ? { childrenEvaluator: new ChildrenEvaluator(childRules, strict, context) } : {};
2358
+ function buildEngines(diagnostics, childRules, context) {
2359
+ return childRules?.length ? { childrenEvaluator: new ChildrenEvaluator(childRules, diagnostics, context) } : {};
1721
2360
  }
1722
2361
 
1723
2362
  // ../../lib/adapter-utils/src/compose-filter.ts
@@ -1730,10 +2369,10 @@ function composeFilter(ownedKeys, filterProps) {
1730
2369
  }
1731
2370
 
1732
2371
  // ../../lib/adapter-utils/src/resolve-adapter-common-options.ts
1733
- function resolveAdapterCommonOptions(options, defaultName = "PolymorphicComponent", defaultStrict = "throw") {
2372
+ function resolveAdapterCommonOptions(options, defaultName = "PolymorphicComponent", defaultDiagnostics = throwDiagnostics) {
1734
2373
  return {
1735
2374
  name: options.name ?? defaultName,
1736
- strict: options.enforcement?.strict ?? defaultStrict
2375
+ diagnostics: options.enforcement?.diagnostics ?? defaultDiagnostics
1737
2376
  };
1738
2377
  }
1739
2378
 
@@ -1741,7 +2380,7 @@ function resolveAdapterCommonOptions(options, defaultName = "PolymorphicComponen
1741
2380
  function normalizeOptions(options) {
1742
2381
  return {
1743
2382
  ...options,
1744
- ...resolveAdapterCommonOptions(options, "PolymorphicElement", false)
2383
+ ...resolveAdapterCommonOptions(options, "PolymorphicElement", silentDiagnostics)
1745
2384
  };
1746
2385
  }
1747
2386
  function buildRuntime(options) {
@@ -1749,7 +2388,7 @@ function buildRuntime(options) {
1749
2388
  const normalized = normalizeOptions(options);
1750
2389
  const { runtime, ownedKeys } = buildCoreRuntime(normalized);
1751
2390
  const { childrenEvaluator } = buildEngines(
1752
- normalized.strict,
2391
+ normalized.diagnostics,
1753
2392
  enforcement?.children,
1754
2393
  normalized.name
1755
2394
  );
@@ -1771,16 +2410,14 @@ function escapeAttr(value) {
1771
2410
  }
1772
2411
  function buildAttrString(attributes) {
1773
2412
  const parts = [];
1774
- for (const key in attributes) {
1775
- if (!Object.hasOwn(attributes, key)) continue;
1776
- const value = attributes[key];
1777
- if (value === false || value === null || value === void 0) continue;
2413
+ iterate.forEachEntry(attributes, (key, value) => {
2414
+ if (value === false || value === null || value === void 0) return;
1778
2415
  if (value === true) {
1779
2416
  parts.push(key);
1780
2417
  } else {
1781
2418
  parts.push(`${key}="${escapeAttr(String(value))}"`);
1782
2419
  }
1783
- }
2420
+ });
1784
2421
  return parts.length > 0 ? " " + parts.join(" ") : "";
1785
2422
  }
1786
2423
  function renderToString(component, props = {}, innerHTML = "") {
@@ -1858,19 +2495,19 @@ function resolveHostState(bundle, props) {
1858
2495
  }
1859
2496
  function applyHostState(host, state, prevPipelineAttrs, incomingProps) {
1860
2497
  host.className = state.className;
1861
- for (const key of prevPipelineAttrs) {
2498
+ iterate.forEachSet(prevPipelineAttrs, (key) => {
1862
2499
  if (!Object.hasOwn(state.attributes, key)) {
1863
2500
  host.removeAttribute(key);
1864
2501
  }
1865
- }
2502
+ });
1866
2503
  prevPipelineAttrs.clear();
1867
- for (const key in incomingProps) {
1868
- if (!Object.hasOwn(incomingProps, key)) continue;
1869
- if (!key.startsWith("aria-") && key !== "role") continue;
2504
+ iterate.forEachKey(incomingProps, (key) => {
2505
+ if (!Object.hasOwn(incomingProps, key)) return;
2506
+ if (!key.startsWith("aria-") && key !== "role") return;
1870
2507
  if (!Object.hasOwn(state.attributes, key)) host.removeAttribute(key);
1871
- }
1872
- for (const key in state.attributes) {
1873
- if (!Object.hasOwn(state.attributes, key)) continue;
2508
+ });
2509
+ iterate.forEachKey(state.attributes, (key) => {
2510
+ if (!Object.hasOwn(state.attributes, key)) return;
1874
2511
  const value = state.attributes[key];
1875
2512
  if (value === void 0 || value === null || value === false) {
1876
2513
  host.removeAttribute(key);
@@ -1881,13 +2518,20 @@ function applyHostState(host, state, prevPipelineAttrs, incomingProps) {
1881
2518
  host.setAttribute(key, String(value));
1882
2519
  prevPipelineAttrs.add(key);
1883
2520
  }
1884
- }
2521
+ });
1885
2522
  }
1886
2523
  function createContractComponent(options) {
1887
2524
  const bundle = buildRuntime(options);
1888
2525
  const looseBundle = toLooseBundle(bundle);
1889
2526
  const variantKeys = options.styling?.variants ? Object.keys(options.styling.variants) : [];
1890
- const praxisProps = /* @__PURE__ */ new Set(["as", "recipe", "praxisClass", ...variantKeys]);
2527
+ const pluginKeys = "classPlugin" in bundle.runtime ? [...bundle.runtime.classPlugin.ownedKeys ?? []] : [];
2528
+ const praxisProps = /* @__PURE__ */ new Set([
2529
+ "as",
2530
+ "recipe",
2531
+ "praxisClass",
2532
+ ...variantKeys,
2533
+ ...pluginKeys
2534
+ ]);
1891
2535
  const staticProps = {
1892
2536
  as: { type: String, attribute: "as" },
1893
2537
  recipe: { type: String, attribute: "variant-key" },
@@ -1895,9 +2539,12 @@ function createContractComponent(options) {
1895
2539
  // attribute so _applyPraxis can read it without a circular class→pipeline→class loop.
1896
2540
  praxisClass: { type: String, attribute: "praxis-class" }
1897
2541
  };
1898
- for (const key of variantKeys) {
2542
+ iterate.forEach(variantKeys, (key) => {
1899
2543
  staticProps[key] = { type: String, attribute: key };
1900
- }
2544
+ });
2545
+ iterate.forEach(pluginKeys, (key) => {
2546
+ staticProps[key] = { type: String, attribute: key };
2547
+ });
1901
2548
  class PolymorphicLitElement extends LitElement {
1902
2549
  // Tracks keys set by the pipeline last render so stale attrs are removed.
1903
2550
  _pipelineAttrs = /* @__PURE__ */ new Set();
@@ -1939,16 +2586,16 @@ function createContractComponent(options) {
1939
2586
  _applyPraxis() {
1940
2587
  const self = this._self;
1941
2588
  const props = {};
1942
- for (const attr of Array.from(this.attributes)) {
2589
+ iterate.forEach(iterate.items(this.attributes), (attr) => {
1943
2590
  if (attr.name !== "class") props[attr.name] = attr.value;
1944
- }
2591
+ });
1945
2592
  props["as"] = self.as;
1946
2593
  props["recipe"] = self.recipe;
1947
2594
  props["className"] = self.praxisClass;
1948
- for (const key of variantKeys) {
2595
+ iterate.forEach(variantKeys, (key) => {
1949
2596
  const val = self[key];
1950
2597
  if (val != null) props[key] = val;
1951
- }
2598
+ });
1952
2599
  applyHostState(this, resolveHostState(looseBundle, props), this._pipelineAttrs, props);
1953
2600
  }
1954
2601
  render() {