praxis-kit 3.1.1 → 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/vue/index.js CHANGED
@@ -1,375 +1,212 @@
1
1
  // ../../adapters/vue/src/create-contract-component.ts
2
- import { computed, defineComponent as defineComponent2 } from "vue";
2
+ import { cloneVNode, computed, defineComponent as defineComponent2 } from "vue";
3
3
 
4
- // ../shared/src/guards/children/component-id.ts
5
- var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default-tag");
4
+ // ../../lib/adapter-utils/src/invariant.ts
5
+ function panic(message) {
6
+ throw new Error(message);
7
+ }
8
+ function invariant(condition, message) {
9
+ if (!condition) panic(message);
10
+ }
6
11
 
7
- // ../../adapters/vue/src/apply-display-name.ts
12
+ // ../../lib/adapter-utils/src/apply-display-name.ts
8
13
  function applyDisplayName(component, name) {
9
14
  Object.assign(component, { displayName: name ?? "PolymorphicComponent" });
10
15
  }
11
16
 
12
- // ../../lib/adapter-utils/src/apply-filter.ts
13
- function applyFilter(props, filterProps, variantKeys) {
14
- const out = {};
15
- for (const k in props) {
16
- if (!Object.hasOwn(props, k)) continue;
17
- if (filterProps(k, variantKeys)) continue;
18
- out[k] = props[k];
19
- }
20
- return out;
17
+ // ../../lib/primitive/src/utils/is-object.ts
18
+ function isObject(value, excludeArrays = false) {
19
+ if (value === null || typeof value !== "object") return false;
20
+ return excludeArrays ? !Array.isArray(value) : true;
21
21
  }
22
-
23
- // ../../lib/adapter-utils/src/define-component.ts
24
- function defineContractComponent(options) {
25
- return (factory) => factory(options);
22
+ function isString(value) {
23
+ return typeof value === "string";
24
+ }
25
+ function isNumber(value) {
26
+ return typeof value === "number";
26
27
  }
27
28
 
28
- // ../core/dist/chunk-KKSHJDE7.js
29
- function makeResolveTag(defaultTag) {
30
- return function tag(as) {
31
- return as ?? defaultTag;
32
- };
29
+ // ../../lib/primitive/src/merge/predicates.ts
30
+ function isFunction(value) {
31
+ return typeof value === "function";
33
32
  }
33
+
34
+ // ../../lib/primitive/src/utils/assert-never.ts
34
35
  function assertNever(value) {
35
36
  throw new Error(`Unexpected value: ${String(value)}`);
36
37
  }
37
- function r(e) {
38
- var t, f, n = "";
39
- if ("string" == typeof e || "number" == typeof e) n += e;
40
- else if ("object" == typeof e) if (Array.isArray(e)) {
41
- var o = e.length;
42
- for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
43
- } else for (f in e) e[f] && (n && (n += " "), n += f);
44
- return n;
45
- }
46
- function clsx() {
47
- for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
48
- return n;
49
- }
50
- function cn(...inputs) {
51
- return clsx(...inputs);
52
- }
53
- function mergeProps(defaultProps, props) {
54
- return {
55
- ...defaultProps ?? {},
56
- ...props
57
- };
38
+
39
+ // ../../lib/primitive/src/utils/iterate.ts
40
+ function find(iterable, callback) {
41
+ for (const value of iterable) {
42
+ const result = callback(value);
43
+ if (result != null) {
44
+ return result;
45
+ }
46
+ }
47
+ return null;
58
48
  }
59
- function isNull(value) {
60
- return value === null;
49
+ function some(iterable, predicate) {
50
+ for (const value of iterable) {
51
+ if (predicate(value)) return true;
52
+ }
53
+ return false;
61
54
  }
62
- function isObject(value) {
63
- return !isNull(value) && typeof value === "object";
55
+ function every(iterable, predicate) {
56
+ let index = 0;
57
+ for (const value of iterable) {
58
+ if (!predicate(value, index++)) {
59
+ return false;
60
+ }
61
+ }
62
+ return true;
64
63
  }
65
- function isString(value) {
66
- return typeof value === "string";
64
+ function* filter(iterable, predicate) {
65
+ let index = 0;
66
+ for (const value of iterable) {
67
+ if (predicate(value, index++)) {
68
+ yield value;
69
+ }
70
+ }
67
71
  }
68
- function isNumber(value) {
69
- return typeof value === "number";
72
+ function* map(iterable, callback) {
73
+ let index = 0;
74
+ for (const value of iterable) {
75
+ yield callback(value, index++);
76
+ }
70
77
  }
71
- var EMPTY_VARIANT_KEYS = /* @__PURE__ */ new Set();
72
- function composeNormalizers(normalizers, fn) {
73
- if (!normalizers?.length) return fn;
74
- return ((props) => {
75
- const patched = normalizers.reduce(
76
- (acc, normalizer) => ({ ...acc, ...normalizer(acc) }),
77
- props
78
- );
79
- return fn ? fn(patched) : patched;
80
- });
78
+ function forEach(iterable, callback) {
79
+ let index = 0;
80
+ for (const value of iterable) {
81
+ callback(value, index++);
82
+ }
81
83
  }
82
- function whenDefined(key, value) {
83
- return value === void 0 ? {} : { [key]: value };
84
- }
85
- function resolveFactoryOptions(options = {}) {
86
- const { styling, enforcement } = options;
87
- const composedNormalizeFn = composeNormalizers(enforcement?.props, options.normalize);
88
- const variantKeys = styling?.variants === void 0 ? EMPTY_VARIANT_KEYS : new Set(Object.keys(styling.variants));
89
- return Object.freeze({
90
- defaultTag: options.tag ?? "div",
91
- strict: enforcement?.strict ?? false,
92
- variantKeys,
93
- ...whenDefined("displayName", options.name),
94
- ...whenDefined("defaultProps", options.defaults),
95
- ...whenDefined("baseClassName", styling?.base),
96
- ...whenDefined("tagMap", styling?.tags),
97
- ...whenDefined("recipeMap", styling?.presets),
98
- ...whenDefined("variants", styling?.variants),
99
- ...whenDefined("defaultVariants", styling?.defaults),
100
- ...whenDefined("compoundVariants", styling?.compounds),
101
- ...whenDefined("normalizeFn", composedNormalizeFn),
102
- ...whenDefined("ariaRules", enforcement?.aria),
103
- ...whenDefined("childRules", enforcement?.children),
104
- ...whenDefined("allowedAs", enforcement?.allowedAs),
105
- ...whenDefined("precomputedClasses", styling?.precomputedClasses)
106
- });
84
+ function reduce(iterable, initial, callback) {
85
+ let accumulator = initial;
86
+ let index = 0;
87
+ for (const value of iterable) {
88
+ accumulator = callback(accumulator, value, index++);
89
+ }
90
+ return accumulator;
107
91
  }
108
- function report(strict, message) {
109
- if (strict === false) return;
110
- const mode = strict === true ? "throw" : strict;
111
- if (mode === "throw") throw new Error(message);
112
- console.warn(message);
113
- }
114
- function validateFactoryOptions(resolved) {
115
- const { strict } = resolved;
116
- if (strict === false) return;
117
- const name = resolved.displayName ?? "Component";
118
- const { variants } = resolved;
119
- const checkSelection = (label2, selection) => {
120
- const record = selection;
121
- for (const dim in record) {
122
- const value = record[dim];
123
- if (value === void 0 || value === null) continue;
124
- if (!variants || !Object.hasOwn(variants, dim)) {
125
- report(strict, `${name}: ${label2} references unknown variant "${dim}".`);
126
- continue;
127
- }
128
- const states = variants[dim];
129
- const stateKey = String(value);
130
- if (!Object.hasOwn(states, stateKey)) {
131
- report(
132
- strict,
133
- `${name}: ${label2} sets "${dim}" to unknown value "${stateKey}" (valid: ${Object.keys(states).join(", ")}).`
134
- );
135
- }
92
+ function collect(iterable, callback) {
93
+ const result = {};
94
+ let index = 0;
95
+ for (const value of iterable) {
96
+ const entry = callback(value, index++);
97
+ if (entry === null) {
98
+ return null;
136
99
  }
137
- };
138
- const { recipeMap } = resolved;
139
- if (recipeMap) {
140
- for (const recipeKey in recipeMap) {
141
- checkSelection(`preset "${recipeKey}"`, recipeMap[recipeKey]);
100
+ result[entry[0]] = entry[1];
101
+ }
102
+ return result;
103
+ }
104
+ function findLast(value, callback) {
105
+ for (let index = value.length - 1; index >= 0; index--) {
106
+ const result = callback(value[index], index);
107
+ if (result != null) {
108
+ return result;
142
109
  }
143
110
  }
144
- if (resolved.defaultVariants) checkSelection("defaults", resolved.defaultVariants);
145
- }
146
- var warned = /* @__PURE__ */ new Set();
147
- var pendingAsyncWarns = /* @__PURE__ */ new Set();
148
- var asyncWarnScheduled = false;
149
- function flushAsyncWarns() {
150
- asyncWarnScheduled = false;
151
- const messages = [...pendingAsyncWarns];
152
- pendingAsyncWarns.clear();
153
- for (const msg of messages) {
154
- console.warn(msg);
155
- }
156
- }
157
- function report2(strict, message) {
158
- if (!strict) return;
159
- const mode = strict === true ? "throw" : strict;
160
- if (mode === "throw") throw new Error(message);
161
- if (mode === "async-warn") {
162
- if (pendingAsyncWarns.has(message)) return;
163
- pendingAsyncWarns.add(message);
164
- if (!asyncWarnScheduled) {
165
- asyncWarnScheduled = true;
166
- queueMicrotask(flushAsyncWarns);
111
+ return null;
112
+ }
113
+ function* items(collection) {
114
+ for (let i = 0; i < collection.length; i++) {
115
+ const item = collection.item(i);
116
+ if (item !== null) {
117
+ yield item;
167
118
  }
168
- return;
169
- }
170
- if (warned.has(message)) return;
171
- warned.add(message);
172
- console.warn(message);
173
- }
174
- function label(name) {
175
- return name ? `[${name}]` : "[createContractComponent]";
176
- }
177
- function validateRenderProps(options, props, recipeKey) {
178
- const { strict, recipeMap, variants, displayName } = options;
179
- if (!strict) return;
180
- const tag = label(displayName);
181
- if (recipeKey !== void 0 && (!recipeMap || !Object.hasOwn(recipeMap, recipeKey))) {
182
- report2(strict, `${tag} Unknown recipeKey "${recipeKey}" \u2014 no preset with that name exists.`);
183
- }
184
- if (variants) {
185
- for (const key in variants) {
186
- if (!Object.hasOwn(props, key)) continue;
187
- const value = props[key];
188
- if (value === void 0 || value === null) continue;
189
- const dim = variants[key];
190
- if (dim && !Object.hasOwn(dim, String(value))) {
191
- report2(
192
- strict,
193
- `${tag} Variant "${key}=${String(value)}" is not a defined value for the "${key}" dimension.`
194
- );
119
+ }
120
+ }
121
+ function nodeList(list) {
122
+ return {
123
+ *[Symbol.iterator]() {
124
+ for (let i = 0; i < list.length; i++) {
125
+ const node = list.item(i);
126
+ if (node !== null) {
127
+ yield node;
128
+ }
195
129
  }
196
130
  }
131
+ };
132
+ }
133
+ function mapEntries(m) {
134
+ return m.entries();
135
+ }
136
+ function set(s) {
137
+ return s.values();
138
+ }
139
+ function hasOwn(object, key) {
140
+ return Object.hasOwn(object, key);
141
+ }
142
+ function* entries(object) {
143
+ for (const key in object) {
144
+ if (!hasOwn(object, key)) continue;
145
+ yield [key, object[key]];
197
146
  }
198
147
  }
199
- function panic(message) {
200
- throw new Error(message);
148
+ function* keys(object) {
149
+ for (const [key] of entries(object)) {
150
+ yield key;
151
+ }
201
152
  }
202
- function describe(value) {
203
- if (value === null) return "null";
204
- if (Array.isArray(value)) return "array";
205
- return typeof value;
153
+ function* values(object) {
154
+ for (const [, value] of entries(object)) {
155
+ yield value;
156
+ }
206
157
  }
207
- function assertPluginShape(result) {
208
- if (result === null || typeof result !== "object")
209
- panic(
210
- `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${result === null ? "null" : typeof result}.`
211
- );
212
- const plugin = result;
213
- if (typeof plugin.pipeline !== "function")
214
- panic(
215
- `[praxis-kit] Plugin factory return value is missing a 'pipeline' function. Got pipeline: ${typeof plugin.pipeline}.`
216
- );
158
+ function mapValues(object, callback) {
159
+ const result = {};
160
+ for (const [key, value] of entries(object)) {
161
+ result[key] = callback(value, key);
162
+ }
163
+ return result;
217
164
  }
218
- function guardPipeline(pipeline) {
219
- if (process.env.NODE_ENV === "production") return pipeline;
220
- return function guardedPipeline(tag, props, className, recipe) {
221
- const result = pipeline(tag, props, className, recipe);
222
- if (typeof result !== "string")
223
- panic(`[praxis-kit] Plugin pipeline must return a string. Got: ${describe(result)}.`);
224
- return result;
225
- };
165
+ function forEachEntry(object, callback) {
166
+ for (const [key, value] of entries(object)) {
167
+ callback(key, value);
168
+ }
226
169
  }
227
- var NOOP_CLASS_PIPELINE = (_tag, _props, className) => Array.isArray(className) ? className.join(" ") : className ?? "";
228
- function resolveClassPipeline(options, resolved, strict, capabilities) {
229
- const factory = options.styling?.plugin;
230
- if (!factory) {
231
- const createClassPipeline2 = capabilities?.createClassPipeline;
232
- const classPipeline2 = createClassPipeline2 ? createClassPipeline2(resolved) : NOOP_CLASS_PIPELINE;
233
- return { pluginResult: void 0, classPipeline: classPipeline2 };
170
+ function forEachKey(object, callback) {
171
+ for (const key of keys(object)) {
172
+ callback(key);
234
173
  }
235
- const pluginResult = factory(resolved, strict);
236
- assertPluginShape(pluginResult);
237
- const classPipeline = guardPipeline(pluginResult.pipeline);
238
- return { pluginResult, classPipeline };
239
174
  }
240
- function createRuntimeMethods(resolved, classPipeline, engine) {
241
- return {
242
- resolveTag: makeResolveTag(resolved.defaultTag),
243
- resolveProps(props) {
244
- return mergeProps(resolved.defaultProps, props);
245
- },
246
- resolveClasses(tag, props, className, recipe) {
247
- if (process.env.NODE_ENV !== "production") {
248
- validateRenderProps(resolved, props, recipe);
249
- }
250
- return classPipeline(tag, props, className, recipe);
251
- },
252
- resolveAria(tag, props) {
253
- if (!engine) return { props };
254
- const result = engine.validate(tag, props);
255
- return { props: result.props };
256
- }
257
- };
175
+ function forEachValue(object, callback) {
176
+ for (const value of values(object)) {
177
+ callback(value);
178
+ }
258
179
  }
259
- function createRuntimeObject(methods, resolved, pluginResult) {
260
- return pluginResult ? { ...methods, options: resolved, hasStyling: true, classPlugin: pluginResult } : { ...methods, options: resolved };
261
- }
262
- function createPolymorphic(options = {}, capabilities) {
263
- const baseResolved = resolveFactoryOptions(options);
264
- const resolved = capabilities?.htmlPropNormalizersFn !== void 0 ? Object.freeze({
265
- ...baseResolved,
266
- htmlPropNormalizersFn: capabilities.htmlPropNormalizersFn
267
- }) : baseResolved;
268
- if (process.env.NODE_ENV !== "production") validateFactoryOptions(resolved);
269
- const { pluginResult, classPipeline } = resolveClassPipeline(
270
- options,
271
- resolved,
272
- resolved.strict,
273
- capabilities
274
- );
275
- const allAriaRules = [
276
- .../* @__PURE__ */ new Set([...capabilities?.htmlAriaRules ?? [], ...resolved.ariaRules ?? []])
277
- ];
278
- const engine = options.enforcement !== void 0 && capabilities?.AriaEngine ? new capabilities.AriaEngine(
279
- resolved.strict,
280
- allAriaRules.length ? { rules: allAriaRules } : void 0
281
- ) : null;
282
- const methods = createRuntimeMethods(resolved, classPipeline, engine);
283
- return createRuntimeObject(
284
- methods,
285
- resolved,
286
- pluginResult
287
- );
180
+ function forEachSet(s, callback) {
181
+ for (const value of s) {
182
+ callback(value);
183
+ }
288
184
  }
289
- var KNOWN_ARIA_ROLES = [
290
- "alert",
291
- "alertdialog",
292
- "application",
293
- "article",
294
- "banner",
295
- "blockquote",
296
- "button",
297
- "caption",
298
- "cell",
299
- "checkbox",
300
- "code",
301
- "columnheader",
302
- "combobox",
303
- "complementary",
304
- "contentinfo",
305
- "definition",
306
- "deletion",
307
- "dialog",
308
- "document",
309
- "emphasis",
310
- "feed",
311
- "figure",
312
- "form",
313
- "generic",
314
- "grid",
315
- "gridcell",
316
- "group",
317
- "heading",
318
- "img",
319
- "insertion",
320
- "link",
321
- "list",
322
- "listbox",
323
- "listitem",
324
- "log",
325
- "main",
326
- "marquee",
327
- "math",
328
- "menu",
329
- "menubar",
330
- "menuitem",
331
- "menuitemcheckbox",
332
- "menuitemradio",
333
- "meter",
334
- "navigation",
335
- "none",
336
- "note",
337
- "option",
338
- "paragraph",
339
- "presentation",
340
- "progressbar",
341
- "radio",
342
- "radiogroup",
343
- "region",
344
- "row",
345
- "rowgroup",
346
- "rowheader",
347
- "scrollbar",
348
- "search",
349
- "searchbox",
350
- "separator",
351
- "slider",
352
- "spinbutton",
353
- "status",
354
- "strong",
355
- "subscript",
356
- "superscript",
357
- "switch",
358
- "tab",
359
- "table",
360
- "tablist",
361
- "tabpanel",
362
- "term",
363
- "textbox",
364
- "time",
365
- "timer",
366
- "toolbar",
367
- "tooltip",
368
- "tree",
369
- "treegrid",
370
- "treeitem"
371
- ];
372
- var KNOWN_ARIA_ROLES_SET = new Set(KNOWN_ARIA_ROLES);
185
+ var iterate = Object.freeze({
186
+ entries,
187
+ filter,
188
+ find,
189
+ findLast,
190
+ forEach,
191
+ forEachEntry,
192
+ forEachKey,
193
+ forEachSet,
194
+ forEachValue,
195
+ items,
196
+ keys,
197
+ map,
198
+ mapEntries,
199
+ mapValues,
200
+ nodeList,
201
+ reduce,
202
+ collect,
203
+ set,
204
+ some,
205
+ every,
206
+ values
207
+ });
208
+
209
+ // ../../lib/primitive/src/constants/aria/global-aria-attributes.ts
373
210
  var GLOBAL_ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
374
211
  "aria-atomic",
375
212
  "aria-busy",
@@ -389,29 +226,59 @@ var GLOBAL_ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
389
226
  "aria-relevant",
390
227
  "aria-roledescription"
391
228
  ]);
229
+
230
+ // ../../lib/primitive/src/constants/aria/implicit-role-record.ts
392
231
  var IMPLICIT_ROLE_RECORD = {
232
+ // Landmarks
393
233
  article: "article",
394
234
  aside: "complementary",
395
235
  footer: "contentinfo",
396
236
  header: "banner",
397
237
  main: "main",
398
238
  nav: "navigation",
399
- button: "button",
239
+ // Interactive
400
240
  a: "link",
241
+ button: "button",
401
242
  select: "listbox",
243
+ textarea: "textbox",
244
+ // Headings
402
245
  h1: "heading",
403
246
  h2: "heading",
404
247
  h3: "heading",
405
248
  h4: "heading",
406
249
  h5: "heading",
407
250
  h6: "heading",
251
+ // Lists
408
252
  ul: "list",
409
253
  ol: "list",
410
254
  li: "listitem",
255
+ // Tables
411
256
  table: "table",
412
257
  tr: "row",
413
258
  td: "cell",
414
- th: "columnheader"
259
+ th: "columnheader",
260
+ // Structural / semantic
261
+ dialog: "dialog",
262
+ fieldset: "group",
263
+ figure: "figure",
264
+ meter: "meter",
265
+ output: "status",
266
+ progress: "progressbar"
267
+ };
268
+ var INPUT_TYPE_ROLE_MAP = {
269
+ checkbox: "checkbox",
270
+ radio: "radio",
271
+ range: "slider",
272
+ number: "spinbutton",
273
+ search: "searchbox",
274
+ text: "textbox",
275
+ email: "textbox",
276
+ tel: "textbox",
277
+ url: "textbox",
278
+ button: "button",
279
+ submit: "button",
280
+ reset: "button",
281
+ image: "button"
415
282
  };
416
283
  var STRONG_ROLES = [
417
284
  "main",
@@ -423,6 +290,8 @@ var STRONG_ROLES = [
423
290
  var STANDALONE_ROLES = ["article"];
424
291
  var STRONG_ROLES_SET = new Set(STRONG_ROLES);
425
292
  var STANDALONE_ROLES_SET = new Set(STANDALONE_ROLES);
293
+
294
+ // ../../lib/primitive/src/constants/aria/role-restricted-attributes.ts
426
295
  var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
427
296
  [
428
297
  "aria-activedescendant",
@@ -572,9 +441,18 @@ var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
572
441
  /* @__PURE__ */ new Set(["meter", "progressbar", "scrollbar", "separator", "slider", "spinbutton"])
573
442
  ]
574
443
  ]);
444
+
445
+ // ../../lib/primitive/src/guards/foundational/is-defined.ts
575
446
  function isUndefined(value) {
576
447
  return value === void 0;
577
448
  }
449
+
450
+ // ../../lib/primitive/src/guards/foundational/is-null.ts
451
+ function isNull(value) {
452
+ return value === null;
453
+ }
454
+
455
+ // ../../lib/primitive/src/guards/aria/is-aria-attribute.ts
578
456
  function isGlobalAriaAttribute(attr) {
579
457
  return GLOBAL_ARIA_ATTRIBUTES.has(attr);
580
458
  }
@@ -584,6 +462,8 @@ function isAriaAttributeValidForRole(attr, role) {
584
462
  if (isUndefined(role)) return false;
585
463
  return allowedRoles.has(role);
586
464
  }
465
+
466
+ // ../../lib/primitive/src/guards/aria/is-aria-role.ts
587
467
  function isStrongImplicitRole(tag) {
588
468
  if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
589
469
  return STRONG_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
@@ -592,40 +472,22 @@ function isStandaloneTag(tag) {
592
472
  if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
593
473
  return STANDALONE_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
594
474
  }
595
- function isKnownAriaRole(value) {
596
- return isString(value) && KNOWN_ARIA_ROLES_SET.has(value);
475
+ function getInputImplicitRole(type) {
476
+ if (type == null || !(type in INPUT_TYPE_ROLE_MAP)) return void 0;
477
+ return INPUT_TYPE_ROLE_MAP[type];
597
478
  }
598
-
599
- // ../core/dist/chunk-2NJ5XLOA.js
600
- var IMPLICIT_ROLE_RECORD2 = {
601
- article: "article",
602
- aside: "complementary",
603
- footer: "contentinfo",
604
- header: "banner",
605
- main: "main",
606
- nav: "navigation",
607
- button: "button",
608
- a: "link",
609
- select: "listbox",
610
- h1: "heading",
611
- h2: "heading",
612
- h3: "heading",
613
- h4: "heading",
614
- h5: "heading",
615
- h6: "heading",
616
- ul: "list",
617
- ol: "list",
618
- li: "listitem",
619
- table: "table",
620
- tr: "row",
621
- td: "cell",
622
- th: "columnheader"
623
- };
624
- function getImplicitRole(tag) {
625
- if (tag in IMPLICIT_ROLE_RECORD2) return IMPLICIT_ROLE_RECORD2[tag];
479
+ function getConditionalImplicitRole(tag, ariaLabel, ariaLabelledBy) {
480
+ const isNamed = typeof ariaLabel === "string" || typeof ariaLabelledBy === "string";
481
+ if (!isNamed) return void 0;
482
+ if (tag === "section") return "region";
483
+ if (tag === "form") return "form";
626
484
  return void 0;
627
485
  }
628
- var COMPONENT_DEFAULT_TAG2 = /* @__PURE__ */ Symbol.for("praxis.component-default-tag");
486
+
487
+ // ../../lib/primitive/src/guards/children/component-id.ts
488
+ var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default-tag");
489
+
490
+ // ../../lib/primitive/src/guards/children/is-tag.ts
629
491
  function getAsProp(child) {
630
492
  if (!isObject(child) || !("props" in child)) return void 0;
631
493
  const props = child.props;
@@ -638,7 +500,7 @@ function getTag(child) {
638
500
  const t = child.type;
639
501
  if (isString(t)) return t;
640
502
  if (typeof t === "function" || isObject(t)) {
641
- const defaultTag = t[COMPONENT_DEFAULT_TAG2];
503
+ const defaultTag = t[COMPONENT_DEFAULT_TAG];
642
504
  if (!isString(defaultTag)) return void 0;
643
505
  return getAsProp(child) ?? defaultTag;
644
506
  }
@@ -646,65 +508,543 @@ function getTag(child) {
646
508
  }
647
509
  function isTag(...args) {
648
510
  if (isString(args[0])) {
649
- const set2 = new Set(args);
511
+ const set3 = new Set(args);
650
512
  return (child2) => {
651
513
  const tag2 = getTag(child2);
652
- return tag2 !== void 0 && set2.has(tag2);
514
+ return tag2 !== void 0 && set3.has(tag2);
653
515
  };
654
516
  }
655
517
  const [child, ...tags] = args;
656
- const set = new Set(tags);
518
+ const set2 = new Set(tags);
657
519
  const tag = getTag(child);
658
- return tag !== void 0 && set.has(tag);
659
- }
660
- var pendingAsyncWarns2 = /* @__PURE__ */ new Set();
661
- var asyncWarnScheduled2 = false;
662
- function flushAsyncWarns2() {
663
- asyncWarnScheduled2 = false;
664
- const messages = [...pendingAsyncWarns2];
665
- pendingAsyncWarns2.clear();
666
- for (const msg of messages) {
667
- console.warn(msg);
668
- }
669
- }
670
- function scheduleAsyncWarn(message) {
671
- if (pendingAsyncWarns2.has(message)) return;
672
- pendingAsyncWarns2.add(message);
673
- if (!asyncWarnScheduled2) {
674
- asyncWarnScheduled2 = true;
675
- queueMicrotask(flushAsyncWarns2);
676
- }
677
- }
678
- var StrictBase = class {
679
- strict;
680
- constructor(strict) {
681
- this.strict = strict;
682
- }
683
- violate(message) {
684
- if (this.strict === true || this.strict === "throw") {
685
- throw new Error(message);
686
- }
687
- this.warn(message);
520
+ return tag !== void 0 && set2.has(tag);
521
+ }
522
+
523
+ // ../../lib/contract/src/aria/aria-role-policy.ts
524
+ function getImplicitRole(tag, props) {
525
+ if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
526
+ if (tag === "input") return getInputImplicitRole(props?.type);
527
+ if (tag === "img") return props?.alt === "" ? "none" : "img";
528
+ if (tag === "section" || tag === "form") {
529
+ return getConditionalImplicitRole(tag, props?.["aria-label"], props?.["aria-labelledby"]);
688
530
  }
689
- // Always caps at console.warn — never throws. ARIA 'warning' violations route here
531
+ return void 0;
532
+ }
533
+
534
+ // ../../lib/contract/src/strict/invariant-base.ts
535
+ var InvariantBase = class {
536
+ diagnostics;
537
+ constructor(diagnostics) {
538
+ this.diagnostics = diagnostics;
539
+ }
540
+ get active() {
541
+ return this.diagnostics.active;
542
+ }
543
+ violate(input) {
544
+ this.diagnostics.error(input);
545
+ }
546
+ // Always caps at warn — never throws. ARIA 'warning' violations route here
690
547
  // so they surface even in strict='throw' mode without aborting a render.
691
- warn(message) {
692
- if (!this.strict) return;
693
- if (this.strict === "async-warn") {
694
- scheduleAsyncWarn(message);
695
- return;
696
- }
697
- console.warn(message);
548
+ warn(input) {
549
+ this.diagnostics.warn(input);
698
550
  }
699
- invariant(condition, message) {
700
- if (!condition) {
701
- this.violate(message);
702
- }
551
+ invariant(condition, input) {
552
+ if (!condition) this.violate(input);
703
553
  }
704
554
  };
705
- function isNull2(value) {
706
- return value === null;
555
+
556
+ // ../../lib/diagnostics/src/category.ts
557
+ var DiagnosticCategory = /* @__PURE__ */ ((DiagnosticCategory2) => {
558
+ DiagnosticCategory2[DiagnosticCategory2["Contract"] = 0] = "Contract";
559
+ DiagnosticCategory2[DiagnosticCategory2["HTML"] = 1] = "HTML";
560
+ DiagnosticCategory2[DiagnosticCategory2["ARIA"] = 2] = "ARIA";
561
+ DiagnosticCategory2[DiagnosticCategory2["Composition"] = 3] = "Composition";
562
+ DiagnosticCategory2[DiagnosticCategory2["Rendering"] = 4] = "Rendering";
563
+ DiagnosticCategory2[DiagnosticCategory2["Accessibility"] = 5] = "Accessibility";
564
+ DiagnosticCategory2[DiagnosticCategory2["Performance"] = 6] = "Performance";
565
+ DiagnosticCategory2[DiagnosticCategory2["Internal"] = 7] = "Internal";
566
+ DiagnosticCategory2[DiagnosticCategory2["Deprecation"] = 8] = "Deprecation";
567
+ DiagnosticCategory2[DiagnosticCategory2["Lint"] = 9] = "Lint";
568
+ return DiagnosticCategory2;
569
+ })(DiagnosticCategory || {});
570
+
571
+ // ../../lib/diagnostics/src/error.ts
572
+ var PraxisError = class extends Error {
573
+ diagnostic;
574
+ constructor(diagnostic) {
575
+ super(diagnostic.message);
576
+ this.name = "PraxisError";
577
+ this.diagnostic = diagnostic;
578
+ }
579
+ };
580
+
581
+ // ../../lib/diagnostics/src/severity.ts
582
+ var Severity = /* @__PURE__ */ ((Severity2) => {
583
+ Severity2[Severity2["Debug"] = 0] = "Debug";
584
+ Severity2[Severity2["Info"] = 1] = "Info";
585
+ Severity2[Severity2["Warning"] = 2] = "Warning";
586
+ Severity2[Severity2["Error"] = 3] = "Error";
587
+ Severity2[Severity2["Fatal"] = 4] = "Fatal";
588
+ return Severity2;
589
+ })(Severity || {});
590
+
591
+ // ../../lib/diagnostics/src/policy.ts
592
+ var DefaultPolicy = class {
593
+ reportThreshold;
594
+ throwThreshold;
595
+ constructor({
596
+ reportThreshold = 1 /* Info */,
597
+ throwThreshold = 4 /* Fatal */
598
+ } = {}) {
599
+ this.reportThreshold = reportThreshold;
600
+ this.throwThreshold = throwThreshold;
601
+ }
602
+ resolve(diagnostic) {
603
+ if (diagnostic.severity >= this.throwThreshold) return 2 /* Throw */;
604
+ if (diagnostic.severity >= this.reportThreshold) return 1 /* Report */;
605
+ return 0 /* Ignore */;
606
+ }
607
+ };
608
+
609
+ // ../../lib/diagnostics/src/diagnostics.ts
610
+ var Diagnostics = class {
611
+ reporter;
612
+ policy;
613
+ // Pre-computed at construction time: true if Warning-level diagnostics are not ignored.
614
+ active;
615
+ constructor(reporter, policy = new DefaultPolicy()) {
616
+ this.reporter = reporter;
617
+ this.policy = policy;
618
+ this.active = policy.resolve({ severity: 2 /* Warning */ }) !== 0 /* Ignore */;
619
+ }
620
+ report(diagnostic) {
621
+ const enforcement = this.policy.resolve(diagnostic);
622
+ if (enforcement === 0 /* Ignore */) return diagnostic;
623
+ if (enforcement === 2 /* Throw */) throw new PraxisError(diagnostic);
624
+ this.reporter.report(diagnostic);
625
+ return diagnostic;
626
+ }
627
+ warn(input) {
628
+ return this.report({ ...input, severity: 2 /* Warning */ });
629
+ }
630
+ error(input) {
631
+ return this.report({ ...input, severity: 3 /* Error */ });
632
+ }
633
+ info(input) {
634
+ return this.report({ ...input, severity: 1 /* Info */ });
635
+ }
636
+ };
637
+
638
+ // ../../lib/diagnostics/src/formatter.ts
639
+ function formatDiagnostic(diagnostic) {
640
+ const level = Severity[diagnostic.severity];
641
+ const category = DiagnosticCategory[diagnostic.category];
642
+ const prefix = category !== void 0 ? `[${category}] ` : "";
643
+ return `${level} ${diagnostic.code}: ${prefix}${diagnostic.message}`;
707
644
  }
645
+
646
+ // ../../lib/diagnostics/src/console-reporter.ts
647
+ var ConsoleReporter = class {
648
+ report(diagnostic) {
649
+ const message = formatDiagnostic(diagnostic);
650
+ switch (diagnostic.severity) {
651
+ case 0 /* Debug */:
652
+ console.debug(message);
653
+ break;
654
+ case 1 /* Info */:
655
+ console.info(message);
656
+ break;
657
+ case 2 /* Warning */:
658
+ console.warn(message);
659
+ break;
660
+ case 3 /* Error */:
661
+ case 4 /* Fatal */:
662
+ console.error(message);
663
+ break;
664
+ }
665
+ }
666
+ };
667
+
668
+ // ../../lib/diagnostics/src/null-reporter.ts
669
+ var nullReporter = {
670
+ report() {
671
+ }
672
+ };
673
+
674
+ // ../../lib/diagnostics/src/presets.ts
675
+ var ignoreAllPolicy = {
676
+ resolve(_) {
677
+ return 0 /* Ignore */;
678
+ }
679
+ };
680
+ var warnOnlyReporter = {
681
+ report(diagnostic) {
682
+ console.warn(formatDiagnostic(diagnostic));
683
+ }
684
+ };
685
+ var silentDiagnostics = new Diagnostics(nullReporter, ignoreAllPolicy);
686
+ var warnDiagnostics = new Diagnostics(
687
+ warnOnlyReporter,
688
+ new DefaultPolicy({ reportThreshold: 2 /* Warning */, throwThreshold: 4 /* Fatal */ })
689
+ );
690
+ var throwDiagnostics = new Diagnostics(
691
+ new ConsoleReporter(),
692
+ new DefaultPolicy({ reportThreshold: 2 /* Warning */, throwThreshold: 3 /* Error */ })
693
+ );
694
+
695
+ // ../../lib/contract/src/diagnostics/aria.ts
696
+ var AriaDiagnostics = {
697
+ /** Generic bridge for violations produced by external AriaRule functions. */
698
+ fromViolation(v) {
699
+ return {
700
+ code: "ARIA2002" /* AriaViolation */,
701
+ category: 2 /* ARIA */,
702
+ message: v.message
703
+ };
704
+ },
705
+ attributeInvalid(key, role) {
706
+ return {
707
+ code: "ARIA2003" /* AriaAttributeInvalid */,
708
+ category: 2 /* ARIA */,
709
+ message: `"${key}" is not valid on role="${role}". It will be removed.`,
710
+ rationale: "Invalid ARIA attributes are ignored by assistive technology and may trigger accessibility-tree warnings in browser devtools.",
711
+ suggestions: [
712
+ {
713
+ title: "Remove the attribute",
714
+ description: `"${key}" is not in the allowed attribute set for role="${role}".`
715
+ }
716
+ ]
717
+ };
718
+ },
719
+ missingLiveRegion(role, impliedLive) {
720
+ return {
721
+ code: "ARIA2004" /* AriaMissingLiveRegion */,
722
+ category: 2 /* ARIA */,
723
+ message: `role="${role}" implies aria-live="${impliedLive}" but it is missing. It has been injected.`,
724
+ rationale: "Live-region roles announce dynamic content changes to screen readers. Without aria-live the politeness level is unspecified and announcements may be silent.",
725
+ suggestions: [
726
+ {
727
+ title: `Add aria-live="${impliedLive}"`,
728
+ description: `role="${role}" conventionally implies aria-live="${impliedLive}".`,
729
+ fix: `aria-live="${impliedLive}"`
730
+ }
731
+ ]
732
+ };
733
+ },
734
+ missingAtomic(role) {
735
+ return {
736
+ code: "ARIA2005" /* AriaMissingAtomic */,
737
+ category: 2 /* ARIA */,
738
+ 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.`,
739
+ rationale: "aria-atomic controls whether assistive technology announces the entire live region or only the changed nodes. Omitting it leaves the behaviour browser-defined."
740
+ };
741
+ },
742
+ relevantInvalidTokens(invalid) {
743
+ const quoted = invalid.map((t) => `"${t}"`).join(", ");
744
+ return {
745
+ code: "ARIA2006" /* AriaRelevantInvalidToken */,
746
+ category: 2 /* ARIA */,
747
+ message: `aria-relevant contains invalid token(s): ${quoted}. Valid tokens are: additions, removals, text, all.`,
748
+ rationale: "aria-relevant accepts a space-separated list of change types. Unrecognised tokens are silently ignored by assistive technology, making the attribute ineffective.",
749
+ suggestions: [
750
+ {
751
+ title: "Use only valid tokens",
752
+ description: "Valid values are: additions, removals, text, all (or a space-separated combination)."
753
+ }
754
+ ]
755
+ };
756
+ },
757
+ relevantSuperseded() {
758
+ return {
759
+ code: "ARIA2007" /* AriaRelevantSuperseded */,
760
+ category: 2 /* ARIA */,
761
+ message: 'aria-relevant includes "all" alongside other tokens. "all" supersedes additions, removals, and text \u2014 use aria-relevant="all" alone.',
762
+ rationale: '"all" is equivalent to "additions removals text". Combining it with other tokens is redundant and may confuse readers of the markup.',
763
+ suggestions: [
764
+ {
765
+ title: 'Use aria-relevant="all"',
766
+ fix: 'aria-relevant="all"'
767
+ }
768
+ ]
769
+ };
770
+ },
771
+ missingAccessibleName(tag) {
772
+ return {
773
+ code: "ARIA2009" /* AriaMissingAccessibleName */,
774
+ category: 2 /* ARIA */,
775
+ message: `<${tag}> has no accessible name. Add aria-label or aria-labelledby.`,
776
+ 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.",
777
+ suggestions: [
778
+ {
779
+ title: "Add aria-label",
780
+ description: `Add aria-label="\u2026" directly to the <${tag}> element.`
781
+ },
782
+ {
783
+ title: "Add aria-labelledby",
784
+ description: "Point aria-labelledby at the id of an existing heading or label element."
785
+ }
786
+ ]
787
+ };
788
+ },
789
+ attributeOnPresentational(attr, tag) {
790
+ return {
791
+ code: "ARIA2010" /* AriaAttributeOnPresentational */,
792
+ category: 2 /* ARIA */,
793
+ message: `"${attr}" is not allowed on a presentational <${tag}>. Presentational elements are invisible to assistive technology.`,
794
+ 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.',
795
+ suggestions: [
796
+ {
797
+ title: "Remove the attribute",
798
+ description: `"${attr}" has no effect when the element has role="none" or role="presentation".`
799
+ }
800
+ ]
801
+ };
802
+ },
803
+ ariaHiddenOnFocusable(tag) {
804
+ return {
805
+ code: "ARIA2011" /* AriaHiddenOnFocusable */,
806
+ category: 2 /* ARIA */,
807
+ 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.`,
808
+ 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.',
809
+ suggestions: [
810
+ {
811
+ title: "Remove aria-hidden",
812
+ description: "If the element should be hidden from all users, use the HTML hidden attribute or CSS display:none instead."
813
+ },
814
+ {
815
+ title: "Make the element non-focusable",
816
+ description: 'If the element is intentionally decorative, add tabindex="-1" and disable it so it is not reachable by keyboard.'
817
+ }
818
+ ]
819
+ };
820
+ },
821
+ invalidAttributeValue(attr, value, expected) {
822
+ const got = value === null ? "null" : value === void 0 ? "undefined" : typeof value === "string" ? `"${value}"` : String(value);
823
+ return {
824
+ code: "ARIA2013" /* AriaInvalidAttributeValue */,
825
+ category: 2 /* ARIA */,
826
+ message: `"${attr}" has an invalid value (${got}). Expected: ${expected}.`,
827
+ rationale: "ARIA attributes with invalid values are silently ignored by assistive technology, making the markup semantically inert.",
828
+ suggestions: [
829
+ {
830
+ title: `Use a valid value for ${attr}`,
831
+ description: `Valid values are: ${expected}.`
832
+ }
833
+ ]
834
+ };
835
+ },
836
+ redundantAriaLevel(tag, level) {
837
+ return {
838
+ code: "ARIA2014" /* AriaRedundantLevelAttribute */,
839
+ category: 2 /* ARIA */,
840
+ message: `aria-level="${level}" is redundant on <${tag}>: the element already has an implicit heading level of ${level}. Remove the attribute.`,
841
+ 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>).',
842
+ suggestions: [
843
+ {
844
+ title: "Remove aria-level",
845
+ description: `<${tag}> already implies aria-level="${level}".`
846
+ }
847
+ ]
848
+ };
849
+ },
850
+ requiredProperty(attr, role) {
851
+ return {
852
+ code: "ARIA2012" /* AriaRequiredProperty */,
853
+ category: 2 /* ARIA */,
854
+ message: `"${attr}" is required for role="${role}" but is missing.`,
855
+ 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.`,
856
+ suggestions: [
857
+ {
858
+ title: `Add ${attr}`,
859
+ description: `role="${role}" requires "${attr}" to be present.`
860
+ }
861
+ ]
862
+ };
863
+ },
864
+ invalidRole(role, tag) {
865
+ return {
866
+ code: "ARIA2008" /* AriaInvalidRole */,
867
+ category: 2 /* ARIA */,
868
+ message: `Invalid role "${role ?? ""}" on <${tag}>.`,
869
+ rationale: "An unrecognised or misapplied ARIA role is ignored by assistive technology and may degrade the accessibility of the element."
870
+ };
871
+ }
872
+ };
873
+
874
+ // ../../lib/contract/src/diagnostics/contract.ts
875
+ var ContractDiagnostics = {
876
+ unexpectedChild(typeName, index, context) {
877
+ return {
878
+ code: "COMP1004" /* UnexpectedChild */,
879
+ category: 0 /* Contract */,
880
+ component: context,
881
+ message: `${context}: unexpected child "${typeName}" at index ${index}.`
882
+ };
883
+ },
884
+ ambiguousChild(typeName, index, ruleNames, context) {
885
+ const quoted = ruleNames.map((n) => `"${n}"`).join(" and ");
886
+ return {
887
+ code: "COMP1005" /* AmbiguousChild */,
888
+ category: 0 /* Contract */,
889
+ component: context,
890
+ message: `${context}: child "${typeName}" at index ${index} matches multiple child rules: ${quoted}.`
891
+ };
892
+ },
893
+ cardinalityMin(ruleName, min, context) {
894
+ return {
895
+ code: "COMP1006" /* CardinalityMin */,
896
+ category: 0 /* Contract */,
897
+ component: context,
898
+ message: `${context}: "${ruleName}" requires at least ${min}.`
899
+ };
900
+ },
901
+ cardinalityMax(ruleName, max, context) {
902
+ return {
903
+ code: "COMP1007" /* CardinalityMax */,
904
+ category: 0 /* Contract */,
905
+ component: context,
906
+ message: `${context}: "${ruleName}" allows at most ${max}.`
907
+ };
908
+ },
909
+ positionViolation(ruleName, position, index, context) {
910
+ return {
911
+ code: "COMP1008" /* PositionViolation */,
912
+ category: 0 /* Contract */,
913
+ component: context,
914
+ message: `${context}: "${ruleName}" must be ${position}, got index ${index}`
915
+ };
916
+ },
917
+ unknownVariantDim(component, label, dim) {
918
+ return {
919
+ code: "COMP1010" /* ContractUnknownVariantDim */,
920
+ category: 0 /* Contract */,
921
+ message: `${component}: ${label} references unknown variant "${dim}".`
922
+ };
923
+ },
924
+ unknownVariantValue(component, label, dim, value, valid) {
925
+ return {
926
+ code: "COMP1011" /* ContractUnknownVariantValue */,
927
+ category: 0 /* Contract */,
928
+ message: `${component}: ${label} sets "${dim}" to unknown value "${value}" (valid: ${valid.join(", ")}).`
929
+ };
930
+ },
931
+ unknownRecipeKey(component, key) {
932
+ return {
933
+ code: "COMP1012" /* ContractUnknownRecipeKey */,
934
+ category: 0 /* Contract */,
935
+ message: `${component} Unknown recipeKey "${key}" \u2014 no preset with that name exists.`
936
+ };
937
+ },
938
+ invalidVariantValue(component, key, value) {
939
+ return {
940
+ code: "COMP1013" /* ContractInvalidVariantValue */,
941
+ category: 0 /* Contract */,
942
+ message: `${component} Variant "${key}=${value}" is not a defined value for the "${key}" dimension.`
943
+ };
944
+ },
945
+ allowedAsViolation(tag, allowedAs, component) {
946
+ const allowed = allowedAs.map((t) => `"${String(t)}"`).join(", ");
947
+ return {
948
+ code: "COMP1009" /* AllowedAsViolation */,
949
+ category: 0 /* Contract */,
950
+ component,
951
+ message: `<${component}>: "as" prop received "${tag}" but only [${allowed}] are allowed.`
952
+ };
953
+ }
954
+ };
955
+
956
+ // ../../lib/contract/src/diagnostics/html.ts
957
+ var HtmlDiagnostics = {
958
+ emptyRole(tag) {
959
+ return {
960
+ code: "HTML3002" /* HtmlEmptyRole */,
961
+ category: 1 /* HTML */,
962
+ message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
963
+ };
964
+ },
965
+ implicitRoleRedundant(tag, implicitRole) {
966
+ return {
967
+ code: "HTML3003" /* HtmlImplicitRoleRedundant */,
968
+ category: 1 /* HTML */,
969
+ message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
970
+ };
971
+ },
972
+ implicitRoleOverride(tag, implicitRole, role) {
973
+ return {
974
+ code: "HTML3004" /* HtmlImplicitRoleOverride */,
975
+ category: 1 /* HTML */,
976
+ message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
977
+ };
978
+ },
979
+ standaloneRegionOverride(tag, implicitRole) {
980
+ return {
981
+ code: "HTML3005" /* HtmlStandaloneRegionOverride */,
982
+ category: 1 /* HTML */,
983
+ message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
984
+ };
985
+ },
986
+ landmarkRoleOverride(tag, implicitRole, role) {
987
+ return {
988
+ code: "HTML3006" /* HtmlLandmarkRoleOverride */,
989
+ category: 1 /* HTML */,
990
+ message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
991
+ };
992
+ },
993
+ invalidChild(child, parent, allowed) {
994
+ return {
995
+ code: "HTML3007" /* HtmlInvalidChild */,
996
+ category: 1 /* HTML */,
997
+ message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
998
+ };
999
+ }
1000
+ };
1001
+
1002
+ // ../../lib/contract/src/diagnostics/slot.ts
1003
+ var SlotDiagnostics = {
1004
+ exclusive(name) {
1005
+ return {
1006
+ code: "SLOT1001" /* SlotExclusive */,
1007
+ category: 0 /* Contract */,
1008
+ component: name,
1009
+ message: `${name}: "as" and "asChild" are mutually exclusive`
1010
+ };
1011
+ },
1012
+ singleChildRequired(name, elementTerm) {
1013
+ return {
1014
+ code: "SLOT1002" /* SlotSingleChild */,
1015
+ category: 0 /* Contract */,
1016
+ component: name,
1017
+ message: `${name}: asChild requires a ${elementTerm} child`
1018
+ };
1019
+ },
1020
+ singleChildExceeded(name, elementTerm, count) {
1021
+ return {
1022
+ code: "SLOT1002" /* SlotSingleChild */,
1023
+ category: 0 /* Contract */,
1024
+ component: name,
1025
+ message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
1026
+ };
1027
+ },
1028
+ discardedChildren(name, elementTerm, count) {
1029
+ const suffix = count === 1 ? "" : "ren";
1030
+ return {
1031
+ code: "SLOT1003" /* SlotDiscardedChildren */,
1032
+ category: 0 /* Contract */,
1033
+ component: name,
1034
+ message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
1035
+ };
1036
+ },
1037
+ renderFnRequired(name, received) {
1038
+ return {
1039
+ code: "SLOT1004" /* SlotRenderFn */,
1040
+ category: 0 /* Contract */,
1041
+ component: name,
1042
+ message: `${name}: asChild requires a render function as children, got ${received}`
1043
+ };
1044
+ }
1045
+ };
1046
+
1047
+ // ../../lib/contract/src/aria/polymorphic-validator.ts
708
1048
  var VALID = [{ valid: true }];
709
1049
  function isIntrinsicTag(tag) {
710
1050
  return isString(tag);
@@ -713,26 +1053,28 @@ function omitProp(obj, key) {
713
1053
  const { [key]: _, ...rest } = obj;
714
1054
  return rest;
715
1055
  }
716
- var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1056
+ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
717
1057
  #extraRules;
718
1058
  #planCache = /* @__PURE__ */ new Map();
719
1059
  static #MAX_CACHE = 100;
720
1060
  // Memoized AriaFix objects keyed by attribute name — the ARIA attribute set is
721
1061
  // finite so this Map is bounded and avoids recreating closures on every cache miss.
722
1062
  static #removeAttributeFixCache = /* @__PURE__ */ new Map();
723
- constructor(strict = "warn", options) {
724
- super(strict);
1063
+ constructor(diagnostics, options) {
1064
+ super(diagnostics);
725
1065
  this.#extraRules = options?.rules ?? [];
726
1066
  }
727
1067
  static #normalizeEmptyRole(tag, props) {
728
1068
  if (props.role !== "") return { normalized: false };
1069
+ const d = HtmlDiagnostics.emptyRole(tag);
729
1070
  return {
730
1071
  normalized: true,
731
1072
  result: {
732
1073
  props: omitProp(props, "role"),
733
1074
  violations: [
734
1075
  {
735
- message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`,
1076
+ message: d.message,
1077
+ diagnostic: d,
736
1078
  tag,
737
1079
  role: "",
738
1080
  attribute: void 0,
@@ -745,10 +1087,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
745
1087
  }
746
1088
  static #deriveContext(tag, props) {
747
1089
  if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
748
- const implicitRole = getImplicitRole(tag);
749
- const hasExplicitLiveRole = !implicitRole && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(props.role ?? "");
750
- if (!implicitRole && !hasExplicitLiveRole)
751
- return { proceed: false, result: { props, violations: [] } };
1090
+ const implicitRole = getImplicitRole(tag, props);
1091
+ const hasRole2 = implicitRole != null || isString(props.role) && props.role.length > 0;
1092
+ if (!hasRole2) return { proceed: false, result: { props, violations: [] } };
752
1093
  const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
753
1094
  if (normalized.normalized) return { proceed: false, result: normalized.result };
754
1095
  const effectiveRole = props.role ?? implicitRole;
@@ -763,25 +1104,36 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
763
1104
  static #runRules(rules, context) {
764
1105
  const violations = [];
765
1106
  const fixes = [];
766
- for (const rule of rules) {
767
- for (const result of rule(context)) {
768
- if (!result.valid) {
769
- violations.push({
770
- message: result.message ?? `Invalid role "${context.props.role}" on <${context.tag}>`,
771
- tag: context.tag,
772
- role: context.props.role,
773
- attribute: result.attribute,
774
- severity: result.severity,
775
- phase: "evaluate"
776
- });
777
- if (result.fixable) fixes.push(result.fix);
778
- }
779
- }
780
- }
1107
+ iterate.forEach(rules, (rule) => {
1108
+ iterate.forEach(rule(context), (result) => {
1109
+ if (result.valid) return;
1110
+ const {
1111
+ tag,
1112
+ props: { role }
1113
+ } = context;
1114
+ const { message, attribute, severity } = result;
1115
+ const resolvedMessage = message ?? result.diagnostic?.message;
1116
+ const fallbackDiag = resolvedMessage == null ? AriaDiagnostics.invalidRole(role, tag) : void 0;
1117
+ violations.push({
1118
+ message: resolvedMessage ?? fallbackDiag.message,
1119
+ tag,
1120
+ role,
1121
+ attribute,
1122
+ severity,
1123
+ phase: "evaluate",
1124
+ ...result.diagnostic != null && { diagnostic: result.diagnostic },
1125
+ ...fallbackDiag != null && { diagnostic: fallbackDiag }
1126
+ });
1127
+ if (result.fixable) fixes.push(result.fix);
1128
+ });
1129
+ });
781
1130
  return { violations, fixes };
782
1131
  }
783
1132
  static #getRules(context) {
784
- return _AriaPolicyEngine.#hasRole(context.props) ? _AriaPolicyEngine.#pipeline : [_AriaPolicyEngine.#checkInvalidAriaAttributes];
1133
+ if (_AriaPolicyEngine.#hasRole(context.props) || context.effectiveRole != null && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1134
+ return _AriaPolicyEngine.#pipeline;
1135
+ }
1136
+ return _AriaPolicyEngine.#implicitOnlyRules;
785
1137
  }
786
1138
  static evaluate(tag, props) {
787
1139
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
@@ -806,10 +1158,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
806
1158
  return { props: next, violations };
807
1159
  }
808
1160
  report(violations) {
809
- for (const v of violations) {
810
- if (v.severity === "error") this.violate(v.message);
811
- else this.warn(v.message);
812
- }
1161
+ iterate.forEach(violations, (v) => {
1162
+ const d = v.diagnostic ?? AriaDiagnostics.fromViolation(v);
1163
+ if (v.severity === "error") this.violate(d);
1164
+ else this.warn(d);
1165
+ });
813
1166
  }
814
1167
  // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs).
815
1168
  // Non-aria props (className, onClick, etc.) do not affect ARIA decisions and are excluded
@@ -821,27 +1174,26 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
821
1174
  if (!isIntrinsicTag(tag)) return null;
822
1175
  const parts = [tag];
823
1176
  if (typeof props.role === "string") parts.push(`role:${props.role}`);
1177
+ if (tag === "input" && typeof props.type === "string") parts.push(`type:${props.type}`);
1178
+ if (tag === "img") parts.push(`alt:${props.alt === "" ? "empty" : "present"}`);
824
1179
  const ariaEntries = [];
825
- for (const k in props) {
826
- if (!Object.hasOwn(props, k) || !k.startsWith("aria-")) continue;
827
- const v = props[k];
828
- if (!isString(v) && !isNumber(v) && typeof v !== "boolean") continue;
1180
+ iterate.forEachEntry(props, (k, v) => {
1181
+ if (!k.startsWith("aria-")) return;
1182
+ if (!isString(v) && !isNumber(v) && typeof v !== "boolean") return;
829
1183
  ariaEntries.push(`${k}:${String(v)}`);
830
- }
1184
+ });
831
1185
  if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
832
1186
  return parts.join("|");
833
1187
  }
834
1188
  static #computePlan(inputProps, resultProps) {
835
1189
  const removals = /* @__PURE__ */ new Set();
836
1190
  const updates = {};
837
- for (const key in inputProps) {
838
- if (Object.hasOwn(inputProps, key) && !(key in resultProps)) removals.add(key);
839
- }
840
- for (const key in resultProps) {
841
- if (!Object.hasOwn(resultProps, key)) continue;
842
- const resultVal = resultProps[key];
1191
+ iterate.forEachKey(inputProps, (key) => {
1192
+ if (!(key in resultProps)) removals.add(key);
1193
+ });
1194
+ iterate.forEachEntry(resultProps, (key, resultVal) => {
843
1195
  if (inputProps[key] !== resultVal) updates[key] = resultVal;
844
- }
1196
+ });
845
1197
  return { removals, updates };
846
1198
  }
847
1199
  static #applyPlan(props, removals, updates) {
@@ -849,15 +1201,15 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
849
1201
  const hasUpdates = Object.keys(updates).length > 0;
850
1202
  if (!hasRemovals && !hasUpdates) return props;
851
1203
  const next = {};
852
- for (const k in props) {
853
- if (Object.hasOwn(props, k) && !removals.has(k)) next[k] = props[k];
854
- }
1204
+ iterate.forEachEntry(props, (k, v) => {
1205
+ if (!removals.has(k)) next[k] = v;
1206
+ });
855
1207
  Object.assign(next, updates);
856
1208
  return next;
857
1209
  }
858
1210
  validate(tag, props) {
859
1211
  const key = _AriaPolicyEngine.#createPlanKey(tag, props);
860
- if (!isNull2(key)) {
1212
+ if (!isNull(key)) {
861
1213
  const cached = this.#planCache.get(key);
862
1214
  if (cached !== void 0) {
863
1215
  this.#planCache.delete(key);
@@ -871,7 +1223,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
871
1223
  }
872
1224
  const result = this.#extraRules.length ? _AriaPolicyEngine.#evaluateWithRules(tag, props, this.#extraRules) : _AriaPolicyEngine.evaluate(tag, props);
873
1225
  if (result.violations.length > 0) this.report(result.violations);
874
- if (!isNull2(key)) {
1226
+ if (!isNull(key)) {
875
1227
  const { removals, updates } = _AriaPolicyEngine.#computePlan(
876
1228
  props,
877
1229
  result.props
@@ -892,12 +1244,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
892
1244
  if (fixes.length === 0) return props;
893
1245
  const sorted = [...fixes].sort((a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity));
894
1246
  let next = props;
895
- for (const { apply } of sorted) {
1247
+ iterate.forEach(sorted, ({ apply }) => {
896
1248
  const effectiveRole = next.role ?? implicitRole;
897
1249
  const fixContext = { tag, implicitRole, effectiveRole, props: next };
898
1250
  const fixResult = apply(fixContext);
899
1251
  if (fixResult.applied) next = fixResult.next;
900
- }
1252
+ });
901
1253
  return next;
902
1254
  }
903
1255
  static #removeRole = {
@@ -925,10 +1277,26 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
925
1277
  _AriaPolicyEngine.#checkInvalidRoleOverride,
926
1278
  _AriaPolicyEngine.#checkRedundantRole,
927
1279
  _AriaPolicyEngine.#checkStandaloneRegion,
1280
+ _AriaPolicyEngine.#checkAriaAttributeValues,
928
1281
  _AriaPolicyEngine.#checkInvalidAriaAttributes,
1282
+ _AriaPolicyEngine.#checkRequiredAriaProperties,
1283
+ _AriaPolicyEngine.#checkNameRequiredRoles,
1284
+ _AriaPolicyEngine.#checkRedundantAriaLevel,
929
1285
  _AriaPolicyEngine.#checkMissingLiveRegion,
930
1286
  _AriaPolicyEngine.#checkMissingAtomic,
931
- _AriaPolicyEngine.#checkInvalidAriaRelevant
1287
+ _AriaPolicyEngine.#checkInvalidAriaRelevant,
1288
+ _AriaPolicyEngine.#checkAriaHiddenOnFocusable,
1289
+ _AriaPolicyEngine.#checkPresentationalAriaAttributes
1290
+ ];
1291
+ // Rules for elements with an implicit role but no explicit role (not a live region).
1292
+ static #implicitOnlyRules = [
1293
+ _AriaPolicyEngine.#checkAriaAttributeValues,
1294
+ _AriaPolicyEngine.#checkInvalidAriaAttributes,
1295
+ _AriaPolicyEngine.#checkRequiredAriaProperties,
1296
+ _AriaPolicyEngine.#checkNameRequiredRoles,
1297
+ _AriaPolicyEngine.#checkRedundantAriaLevel,
1298
+ _AriaPolicyEngine.#checkAriaHiddenOnFocusable,
1299
+ _AriaPolicyEngine.#checkPresentationalAriaAttributes
932
1300
  ];
933
1301
  static #checkInvalidRoleOverride({
934
1302
  tag,
@@ -944,7 +1312,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
944
1312
  fixable: true,
945
1313
  severity: "error",
946
1314
  fix: _AriaPolicyEngine.#removeRole,
947
- message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
1315
+ diagnostic: HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role)
948
1316
  }
949
1317
  ];
950
1318
  }
@@ -956,47 +1324,304 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
956
1324
  return [
957
1325
  {
958
1326
  valid: false,
959
- fixable: true,
1327
+ fixable: true,
1328
+ severity: "warning",
1329
+ fix: _AriaPolicyEngine.#removeRole,
1330
+ diagnostic: HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole)
1331
+ }
1332
+ ];
1333
+ }
1334
+ static #checkStandaloneRegion({ tag, props, implicitRole }) {
1335
+ const role = props.role;
1336
+ if (role !== "region") return VALID;
1337
+ if (!isStandaloneTag(tag)) return VALID;
1338
+ return [
1339
+ {
1340
+ valid: false,
1341
+ fixable: true,
1342
+ severity: "error",
1343
+ fix: _AriaPolicyEngine.#removeRole,
1344
+ diagnostic: HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag)
1345
+ }
1346
+ ];
1347
+ }
1348
+ static #checkInvalidAriaAttributes({
1349
+ tag,
1350
+ props,
1351
+ effectiveRole
1352
+ }) {
1353
+ if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1354
+ const results = [];
1355
+ iterate.forEachEntry(props, (key) => {
1356
+ if (!key.startsWith("aria-")) return;
1357
+ if (isGlobalAriaAttribute(key)) return;
1358
+ if (isAriaAttributeValidForRole(key, effectiveRole)) return;
1359
+ results.push({
1360
+ valid: false,
1361
+ severity: "warning",
1362
+ fixable: true,
1363
+ attribute: key,
1364
+ diagnostic: AriaDiagnostics.attributeInvalid(key, effectiveRole ?? tag),
1365
+ fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
1366
+ });
1367
+ });
1368
+ return results;
1369
+ }
1370
+ // ─── ARIA attribute value validation ──────────────────────────────────────
1371
+ // Accepted value shapes for typed ARIA attributes.
1372
+ // Attributes not in this map are unconstrained (arbitrary string values permitted).
1373
+ static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1374
+ // Boolean (true | false)
1375
+ ["aria-atomic", { kind: "boolean" }],
1376
+ ["aria-busy", { kind: "boolean" }],
1377
+ ["aria-disabled", { kind: "boolean" }],
1378
+ ["aria-expanded", { kind: "boolean" }],
1379
+ ["aria-hidden", { kind: "boolean" }],
1380
+ ["aria-modal", { kind: "boolean" }],
1381
+ ["aria-multiline", { kind: "boolean" }],
1382
+ ["aria-multiselectable", { kind: "boolean" }],
1383
+ ["aria-readonly", { kind: "boolean" }],
1384
+ ["aria-required", { kind: "boolean" }],
1385
+ ["aria-selected", { kind: "boolean" }],
1386
+ // Tristate (true | false | mixed)
1387
+ ["aria-checked", { kind: "tristate" }],
1388
+ ["aria-pressed", { kind: "tristate" }],
1389
+ // Numeric (any finite number)
1390
+ ["aria-valuenow", { kind: "number" }],
1391
+ ["aria-valuemin", { kind: "number" }],
1392
+ ["aria-valuemax", { kind: "number" }],
1393
+ // Integer with optional range
1394
+ ["aria-level", { kind: "integer", min: 1, max: 6 }],
1395
+ ["aria-posinset", { kind: "integer", min: 1 }],
1396
+ ["aria-setsize", { kind: "integer", min: -1 }],
1397
+ ["aria-rowcount", { kind: "integer", min: -1 }],
1398
+ ["aria-colcount", { kind: "integer", min: -1 }],
1399
+ ["aria-rowindex", { kind: "integer", min: 1 }],
1400
+ ["aria-colindex", { kind: "integer", min: 1 }],
1401
+ ["aria-rowspan", { kind: "integer", min: 0 }],
1402
+ ["aria-colspan", { kind: "integer", min: 0 }],
1403
+ // Enum (specific allowed tokens)
1404
+ ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1405
+ [
1406
+ "aria-current",
1407
+ {
1408
+ kind: "enum",
1409
+ values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1410
+ }
1411
+ ],
1412
+ [
1413
+ "aria-haspopup",
1414
+ {
1415
+ kind: "enum",
1416
+ values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1417
+ }
1418
+ ],
1419
+ ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1420
+ ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1421
+ [
1422
+ "aria-orientation",
1423
+ { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
1424
+ ],
1425
+ ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1426
+ ]);
1427
+ static #isValidAriaValue(value, type) {
1428
+ switch (type.kind) {
1429
+ case "boolean":
1430
+ return value === "true" || value === "false" || value === true || value === false;
1431
+ case "tristate":
1432
+ return value === "true" || value === "false" || value === "mixed" || value === true || value === false;
1433
+ case "number": {
1434
+ if (typeof value === "number") return Number.isFinite(value);
1435
+ if (typeof value !== "string") return false;
1436
+ const n = parseFloat(value);
1437
+ return Number.isFinite(n);
1438
+ }
1439
+ case "integer": {
1440
+ const n = typeof value === "number" ? value : typeof value === "string" ? parseInt(value, 10) : NaN;
1441
+ if (!Number.isFinite(n) || !Number.isInteger(n)) return false;
1442
+ if (type.min !== void 0 && n < type.min) return false;
1443
+ if (type.max !== void 0 && n > type.max) return false;
1444
+ return true;
1445
+ }
1446
+ case "enum":
1447
+ return typeof value === "string" && type.values.has(value);
1448
+ }
1449
+ }
1450
+ static #describeExpected(type) {
1451
+ switch (type.kind) {
1452
+ case "boolean":
1453
+ return '"true" or "false"';
1454
+ case "tristate":
1455
+ return '"true", "false", or "mixed"';
1456
+ case "number":
1457
+ return "a finite number";
1458
+ case "integer": {
1459
+ const parts = ["an integer"];
1460
+ if (type.min !== void 0 && type.max !== void 0)
1461
+ parts.push(`between ${type.min} and ${type.max}`);
1462
+ else if (type.min !== void 0) parts.push(`\u2265 ${type.min}`);
1463
+ else if (type.max !== void 0) parts.push(`\u2264 ${type.max}`);
1464
+ return parts.join(" ");
1465
+ }
1466
+ case "enum":
1467
+ return [...type.values].map((v) => `"${v}"`).join(", ");
1468
+ }
1469
+ }
1470
+ static #checkAriaAttributeValues({ props, effectiveRole }) {
1471
+ if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1472
+ const results = [];
1473
+ iterate.forEachEntry(props, (key, value) => {
1474
+ if (!key.startsWith("aria-")) return;
1475
+ const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1476
+ if (type == null) return;
1477
+ if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1478
+ results.push({
1479
+ valid: false,
1480
+ fixable: true,
1481
+ severity: "warning",
1482
+ attribute: key,
1483
+ diagnostic: AriaDiagnostics.invalidAttributeValue(
1484
+ key,
1485
+ value,
1486
+ _AriaPolicyEngine.#describeExpected(type)
1487
+ ),
1488
+ fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
1489
+ });
1490
+ });
1491
+ return results;
1492
+ }
1493
+ // ─── Heading implicit level ────────────────────────────────────────────────
1494
+ static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1495
+ ["h1", 1],
1496
+ ["h2", 2],
1497
+ ["h3", 3],
1498
+ ["h4", 4],
1499
+ ["h5", 5],
1500
+ ["h6", 6]
1501
+ ]);
1502
+ static #checkRedundantAriaLevel({
1503
+ tag,
1504
+ props,
1505
+ effectiveRole
1506
+ }) {
1507
+ if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1508
+ const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1509
+ if (implicitLevel == null) return VALID;
1510
+ const raw = props["aria-level"];
1511
+ if (raw == null) return VALID;
1512
+ const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1513
+ if (!Number.isFinite(n) || n !== implicitLevel) return VALID;
1514
+ return [
1515
+ {
1516
+ valid: false,
1517
+ fixable: true,
1518
+ severity: "warning",
1519
+ attribute: "aria-level",
1520
+ diagnostic: AriaDiagnostics.redundantAriaLevel(tag, implicitLevel),
1521
+ fix: _AriaPolicyEngine.#makeRemoveAttributeFix("aria-level")
1522
+ }
1523
+ ];
1524
+ }
1525
+ // ─── Name-required roles ───────────────────────────────────────────────────
1526
+ // Roles that always require an accessible name per WAI-ARIA APG.
1527
+ // Dialog and landmark names are enforced via contracts (ariaContract) rather than
1528
+ // the built-in pipeline so consumers can opt in; img is built in because role=img
1529
+ // on any element (including bare <img>) is definitionally useless without a name.
1530
+ static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1531
+ static #checkNameRequiredRoles({
1532
+ tag,
1533
+ props,
1534
+ effectiveRole
1535
+ }) {
1536
+ if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole)) return VALID;
1537
+ if ("aria-label" in props || "aria-labelledby" in props) return VALID;
1538
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return VALID;
1539
+ return [
1540
+ {
1541
+ valid: false,
1542
+ fixable: false,
960
1543
  severity: "warning",
961
- fix: _AriaPolicyEngine.#removeRole,
962
- message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
1544
+ diagnostic: AriaDiagnostics.missingAccessibleName(tag)
963
1545
  }
964
1546
  ];
965
1547
  }
966
- static #checkStandaloneRegion({ tag, props, implicitRole }) {
967
- const role = props.role;
968
- if (role !== "region") return VALID;
969
- if (!isStandaloneTag(tag)) return VALID;
1548
+ // WAI-ARIA 1.2 required states and properties, keyed by role.
1549
+ // Source: https://www.w3.org/TR/wai-aria-1.2/#requiredState
1550
+ static #REQUIRED_PROPERTIES = /* @__PURE__ */ new Map([
1551
+ ["combobox", ["aria-expanded"]],
1552
+ ["option", ["aria-selected"]],
1553
+ ["slider", ["aria-valuenow"]],
1554
+ ["scrollbar", ["aria-controls", "aria-valuenow"]],
1555
+ ["spinbutton", ["aria-valuenow"]]
1556
+ ]);
1557
+ static #checkRequiredAriaProperties({
1558
+ props,
1559
+ effectiveRole
1560
+ }) {
1561
+ if (!effectiveRole) return VALID;
1562
+ const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1563
+ if (required == null) return VALID;
1564
+ const results = [];
1565
+ iterate.forEach(required, (attr) => {
1566
+ if (attr in props) return;
1567
+ results.push({
1568
+ valid: false,
1569
+ fixable: false,
1570
+ severity: "warning",
1571
+ attribute: attr,
1572
+ diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
1573
+ });
1574
+ });
1575
+ return results;
1576
+ }
1577
+ // Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
1578
+ static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1579
+ "a",
1580
+ "button",
1581
+ "input",
1582
+ "select",
1583
+ "textarea"
1584
+ ]);
1585
+ // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1586
+ static #checkAriaHiddenOnFocusable({ tag, props }) {
1587
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return VALID;
1588
+ const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1589
+ if (!isInteractive) {
1590
+ const tabindex = props.tabindex;
1591
+ const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1592
+ if (!Number.isFinite(n) || n < 0) return VALID;
1593
+ }
970
1594
  return [
971
1595
  {
972
1596
  valid: false,
973
- fixable: true,
1597
+ fixable: false,
974
1598
  severity: "error",
975
- fix: _AriaPolicyEngine.#removeRole,
976
- message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
1599
+ attribute: "aria-hidden",
1600
+ diagnostic: AriaDiagnostics.ariaHiddenOnFocusable(tag)
977
1601
  }
978
1602
  ];
979
1603
  }
980
- static #checkInvalidAriaAttributes({
1604
+ // Presentational elements (role=none/presentation, including <img alt="">) are removed
1605
+ // from the accessibility tree — ARIA attributes on them are meaningless and misleading.
1606
+ static #checkPresentationalAriaAttributes({
981
1607
  tag,
982
1608
  props,
983
1609
  effectiveRole
984
1610
  }) {
1611
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return VALID;
985
1612
  const results = [];
986
- for (const key in props) {
987
- if (!Object.hasOwn(props, key)) continue;
988
- if (!key.startsWith("aria-")) continue;
989
- if (isGlobalAriaAttribute(key)) continue;
990
- if (isAriaAttributeValidForRole(key, effectiveRole)) continue;
1613
+ iterate.forEachEntry(props, (key) => {
1614
+ if (!key.startsWith("aria-")) return;
1615
+ if (key === "aria-hidden") return;
991
1616
  results.push({
992
1617
  valid: false,
993
- severity: "warning",
994
1618
  fixable: true,
1619
+ severity: "warning",
995
1620
  attribute: key,
996
- message: `"${key}" is not valid on role="${effectiveRole ?? tag}". It will be removed.`,
1621
+ diagnostic: AriaDiagnostics.attributeOnPresentational(key, tag),
997
1622
  fix: _AriaPolicyEngine.#makeRemoveAttributeFix(key)
998
1623
  });
999
- }
1624
+ });
1000
1625
  return results;
1001
1626
  }
1002
1627
  // WAI-ARIA live region roles and their implied aria-live politeness values.
@@ -1025,7 +1650,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1025
1650
  fixable: true,
1026
1651
  severity: "warning",
1027
1652
  fix: injectLive,
1028
- message: `role="${effectiveRole}" implies aria-live="${impliedLive}" but it is missing. It has been injected.`
1653
+ diagnostic: AriaDiagnostics.missingLiveRegion(effectiveRole, impliedLive)
1029
1654
  }
1030
1655
  ];
1031
1656
  }
@@ -1037,7 +1662,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1037
1662
  valid: false,
1038
1663
  fixable: false,
1039
1664
  severity: "warning",
1040
- 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.`
1665
+ diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
1041
1666
  }
1042
1667
  ];
1043
1668
  }
@@ -1066,7 +1691,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1066
1691
  fixable: true,
1067
1692
  severity: "warning",
1068
1693
  attribute: "aria-relevant",
1069
- message: `aria-relevant contains invalid token(s): ${invalid.map((t) => `"${t}"`).join(", ")}. Valid tokens are: additions, removals, text, all.`,
1694
+ diagnostic: AriaDiagnostics.relevantInvalidTokens(invalid),
1070
1695
  fix: _AriaPolicyEngine.#makeRemoveAttributeFix("aria-relevant")
1071
1696
  }
1072
1697
  ];
@@ -1078,7 +1703,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1078
1703
  fixable: true,
1079
1704
  severity: "warning",
1080
1705
  attribute: "aria-relevant",
1081
- message: `aria-relevant includes "all" alongside other tokens. "all" supersedes additions, removals, and text \u2014 use aria-relevant="all" alone.`,
1706
+ diagnostic: AriaDiagnostics.relevantSuperseded(),
1082
1707
  fix: _AriaPolicyEngine.#normalizeRelevantAllFix
1083
1708
  }
1084
1709
  ];
@@ -1086,6 +1711,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends StrictBase {
1086
1711
  return VALID;
1087
1712
  }
1088
1713
  };
1714
+
1715
+ // ../../lib/contract/src/children/get-type-name.ts
1089
1716
  function getTypeName(value) {
1090
1717
  if (value === null) return "null";
1091
1718
  if (value === void 0) return "undefined";
@@ -1096,39 +1723,8 @@ function getTypeName(value) {
1096
1723
  const name = value.constructor?.name;
1097
1724
  return typeof name === "string" && name !== "Object" ? name : "object";
1098
1725
  }
1099
- var MatchValidationErrorBuilder = class {
1100
- #prefix;
1101
- constructor(ctx = "") {
1102
- this.#prefix = ctx ? `${ctx}:
1103
- ` : "";
1104
- }
1105
- #template(typeName, index, prefix = "", suffix = "") {
1106
- const leadingStr = prefix ? `${prefix} ` : "";
1107
- const followingStr = suffix ? ` ${suffix}` : "";
1108
- return `${leadingStr}child "${typeName}" at index ${index}${followingStr}.`;
1109
- }
1110
- unexpectedChild(typeName, index) {
1111
- return this.#template(typeName, index, "unexpected");
1112
- }
1113
- multipleMatches(typeName, index, ruleNames) {
1114
- const quoted = ruleNames.map((n) => `"${n}"`);
1115
- return this.#template(
1116
- typeName,
1117
- index,
1118
- "",
1119
- `matches multiple child rules: ${quoted.join(" and ")}`
1120
- );
1121
- }
1122
- #format(errors) {
1123
- return this.#prefix + errors.join("\n");
1124
- }
1125
- toError(errors) {
1126
- if (errors.length === 0) {
1127
- return new Error(this.#prefix + "Unknown validation error.");
1128
- }
1129
- return new Error(this.#format(errors));
1130
- }
1131
- };
1726
+
1727
+ // ../../lib/contract/src/children/normalize-child-rule.ts
1132
1728
  function normalizeCardinality(input, impliesSingleton) {
1133
1729
  const min = input?.min ?? 0;
1134
1730
  const max = input?.max ?? (impliesSingleton ? 1 : Infinity);
@@ -1153,6 +1749,8 @@ function normalizeChildRule(rule) {
1153
1749
  cardinality: normalizeCardinality(rule.cardinality, impliesSingleton)
1154
1750
  };
1155
1751
  }
1752
+
1753
+ // ../../lib/contract/src/children/rules-matcher.ts
1156
1754
  function getChildType(child) {
1157
1755
  if (!isObject(child) || !("type" in child)) return void 0;
1158
1756
  return child.type;
@@ -1161,8 +1759,8 @@ function buildPartialIndex(rules) {
1161
1759
  const typeIndex = /* @__PURE__ */ new Map();
1162
1760
  const duplicateTypes = /* @__PURE__ */ new Set();
1163
1761
  const untypedIndices = [];
1164
- for (let ri = 0; ri < rules.length; ri++) {
1165
- const t = rules[ri].type;
1762
+ iterate.forEach(rules, (rule, ri) => {
1763
+ const t = rule.type;
1166
1764
  if (t === void 0) {
1167
1765
  untypedIndices.push(ri);
1168
1766
  } else if (typeIndex.has(t)) {
@@ -1170,12 +1768,14 @@ function buildPartialIndex(rules) {
1170
1768
  } else {
1171
1769
  typeIndex.set(t, ri);
1172
1770
  }
1173
- }
1771
+ });
1174
1772
  if (duplicateTypes.size > 0) {
1175
- for (const t of duplicateTypes) typeIndex.delete(t);
1176
- for (let ri = 0; ri < rules.length; ri++) {
1177
- if (duplicateTypes.has(rules[ri].type)) untypedIndices.push(ri);
1178
- }
1773
+ iterate.forEachSet(duplicateTypes, (t) => {
1774
+ typeIndex.delete(t);
1775
+ });
1776
+ iterate.forEach(rules, (rule, ri) => {
1777
+ if (duplicateTypes.has(rule.type)) untypedIndices.push(ri);
1778
+ });
1179
1779
  }
1180
1780
  return { typeIndex, untypedIndices };
1181
1781
  }
@@ -1194,10 +1794,10 @@ var RuleMatcher = class {
1194
1794
  const reverse = /* @__PURE__ */ new Map();
1195
1795
  const unexpectedIndices = /* @__PURE__ */ new Set();
1196
1796
  const ambiguousIndices = /* @__PURE__ */ new Set();
1197
- for (let ri = 0; ri < this.#rules.length; ri++) {
1797
+ iterate.forEach(this.#rules, (_, ri) => {
1198
1798
  reverse.set(ri, /* @__PURE__ */ new Set());
1199
- }
1200
- for (const [ci, child] of children.entries()) {
1799
+ });
1800
+ iterate.forEach(children, (child, ci) => {
1201
1801
  const t = getChildType(child);
1202
1802
  if (t !== void 0) {
1203
1803
  const ri = this.#typeIndex.get(t);
@@ -1211,8 +1811,8 @@ var RuleMatcher = class {
1211
1811
  reverse.get(ri).add(ci);
1212
1812
  }
1213
1813
  }
1214
- for (const ri of this.#untypedIndices) {
1215
- if (!this.#rules[ri].match(child)) continue;
1814
+ iterate.forEach(this.#untypedIndices, (ri) => {
1815
+ if (!this.#rules[ri].match(child)) return;
1216
1816
  let childEntry = forward.get(ci);
1217
1817
  if (!childEntry) {
1218
1818
  childEntry = /* @__PURE__ */ new Set();
@@ -1220,33 +1820,35 @@ var RuleMatcher = class {
1220
1820
  }
1221
1821
  childEntry.add(ri);
1222
1822
  reverse.get(ri).add(ci);
1223
- }
1823
+ });
1224
1824
  const entry = forward.get(ci);
1225
1825
  if (!entry) {
1226
1826
  unexpectedIndices.add(ci);
1227
1827
  } else if (entry.size > 1) {
1228
1828
  ambiguousIndices.add(ci);
1229
1829
  }
1230
- }
1830
+ });
1231
1831
  return { matrix: { childToRules: { forward, reverse } }, unexpectedIndices, ambiguousIndices };
1232
1832
  }
1233
1833
  };
1234
- var RuleValidator = class _RuleValidator extends StrictBase {
1834
+
1835
+ // ../../lib/contract/src/children/rule-validator.ts
1836
+ var RuleValidator = class _RuleValidator extends InvariantBase {
1235
1837
  #context;
1236
- constructor(context, strict) {
1237
- super(strict);
1838
+ constructor(context, diagnostics) {
1839
+ super(diagnostics);
1238
1840
  this.#context = context;
1239
1841
  }
1240
1842
  validate(rules, matrix, childCount) {
1241
1843
  const firstIndex = 0;
1242
1844
  const lastIndex = childCount - 1;
1243
- for (const [ri, rule] of rules.entries()) {
1845
+ iterate.forEach(rules, (rule, ri) => {
1244
1846
  const matches = matrix.childToRules.reverse.get(ri);
1245
1847
  const matchCount = matches.size;
1246
1848
  this.#validateCardinality(rule, matchCount);
1247
- if (matchCount === 0) continue;
1849
+ if (matchCount === 0) return;
1248
1850
  this.#validatePositions(rule, matches, firstIndex, lastIndex);
1249
- }
1851
+ });
1250
1852
  }
1251
1853
  #validateCardinality(rule, matchCount) {
1252
1854
  const { cardinality, name } = rule;
@@ -1255,21 +1857,21 @@ var RuleValidator = class _RuleValidator extends StrictBase {
1255
1857
  }
1256
1858
  const { min, max } = cardinality;
1257
1859
  if (matchCount < min) {
1258
- this.violate(`${this.#context}: "${name}" requires at least ${min}.`);
1860
+ this.violate(ContractDiagnostics.cardinalityMin(name, min, this.#context));
1259
1861
  return;
1260
1862
  }
1261
1863
  if (matchCount > max) {
1262
- this.violate(`${this.#context}: "${name}" allows at most ${max}.`);
1864
+ this.violate(ContractDiagnostics.cardinalityMax(name, max, this.#context));
1263
1865
  }
1264
1866
  }
1265
1867
  #validatePositions(rule, matches, firstIndex, lastIndex) {
1266
1868
  const { name, position } = rule;
1267
- for (const index of matches) {
1869
+ iterate.forEachSet(matches, (index) => {
1268
1870
  if (_RuleValidator.#isValidPosition(index, position, firstIndex, lastIndex)) {
1269
- continue;
1871
+ return;
1270
1872
  }
1271
- this.violate(`${this.#context}: "${name}" must be ${position}, got index ${index}`);
1272
- }
1873
+ this.violate(ContractDiagnostics.positionViolation(name, position, index, this.#context));
1874
+ });
1273
1875
  }
1274
1876
  static #isValidPosition(matchIndex, position, firstIndex, lastIndex) {
1275
1877
  switch (position) {
@@ -1284,48 +1886,50 @@ var RuleValidator = class _RuleValidator extends StrictBase {
1284
1886
  }
1285
1887
  }
1286
1888
  };
1287
- var ChildrenEvaluator = class extends StrictBase {
1889
+
1890
+ // ../../lib/contract/src/children/children-evaluator.ts
1891
+ var ChildrenEvaluator = class extends InvariantBase {
1892
+ #context;
1288
1893
  #rules;
1289
1894
  #ruleNames;
1290
1895
  #matcher;
1291
1896
  #ruleValidator;
1292
- #matchBuilder;
1293
- constructor(rules, strict = "warn", context = "Component") {
1294
- super(strict);
1295
- this.#rules = rules.map((r2) => normalizeChildRule(r2));
1296
- this.#ruleNames = this.#rules.map((r2) => r2.name);
1297
- for (const rule of this.#rules) {
1897
+ constructor(rules, diagnostics, context = "Component") {
1898
+ super(diagnostics);
1899
+ this.#context = context;
1900
+ this.#rules = rules.map((r) => normalizeChildRule(r));
1901
+ this.#ruleNames = this.#rules.map((r) => r.name);
1902
+ iterate.forEach(this.#rules, (rule) => {
1298
1903
  const { name, position, cardinality } = rule;
1299
1904
  if ((position === "first" || position === "last") && (cardinality.kind === "unbounded" || cardinality.max > 1)) {
1300
1905
  throw new RangeError(
1301
1906
  `ChildrenEvaluator [${context}]: rule "${name}" sets position="${position}" with an unbound or >1 max. position="first|last" implies max=1.`
1302
1907
  );
1303
1908
  }
1304
- }
1909
+ });
1305
1910
  this.#matcher = new RuleMatcher(this.#rules);
1306
- this.#ruleValidator = new RuleValidator(context, strict);
1307
- this.#matchBuilder = new MatchValidationErrorBuilder(context);
1911
+ this.#ruleValidator = new RuleValidator(context, diagnostics);
1308
1912
  }
1309
1913
  evaluate(children) {
1310
- if (!this.strict) return;
1914
+ if (!this.active) return;
1311
1915
  const { matrix, unexpectedIndices, ambiguousIndices } = this.#matcher.match(children);
1312
1916
  this.#ruleValidator.validate(this.#rules, matrix, children.length);
1313
1917
  if (unexpectedIndices.size === 0 && ambiguousIndices.size === 0) return;
1314
- const errors = [];
1315
1918
  const violating = [...unexpectedIndices, ...ambiguousIndices].sort((a, b) => a - b);
1316
- for (const ci of violating) {
1919
+ iterate.forEach(violating, (ci) => {
1317
1920
  const typeName = getTypeName(children[ci]);
1318
1921
  if (unexpectedIndices.has(ci)) {
1319
- errors.push(this.#matchBuilder.unexpectedChild(typeName, ci));
1922
+ this.violate(ContractDiagnostics.unexpectedChild(typeName, ci, this.#context));
1320
1923
  } else {
1321
1924
  const matches = matrix.childToRules.forward.get(ci);
1322
1925
  const names = [...matches].map((ri) => this.#ruleNames[ri] ?? `#${ri}`);
1323
- errors.push(this.#matchBuilder.multipleMatches(typeName, ci, names));
1926
+ this.violate(ContractDiagnostics.ambiguousChild(typeName, ci, names, this.#context));
1324
1927
  }
1325
- }
1326
- this.invariant(errors.length === 0, this.#matchBuilder.toError(errors).message);
1928
+ });
1327
1929
  }
1328
1930
  };
1931
+
1932
+ // ../../lib/contract/src/props/get-disabled-props.ts
1329
1933
  var disabledProps = ({
1330
1934
  disabled,
1331
1935
  "aria-disabled": ariaDisabled,
@@ -1337,6 +1941,8 @@ var disabledProps = ({
1337
1941
  ...dataDisabled === void 0 && { "data-disabled": "" }
1338
1942
  };
1339
1943
  };
1944
+
1945
+ // ../../lib/contract/src/props/get-invalid-props.ts
1340
1946
  var invalidProps = ({
1341
1947
  invalid,
1342
1948
  "aria-invalid": ariaInvalid,
@@ -1348,6 +1954,8 @@ var invalidProps = ({
1348
1954
  ...dataInvalid === void 0 && { "data-invalid": "" }
1349
1955
  };
1350
1956
  };
1957
+
1958
+ // ../../lib/contract/src/props/get-readonly-props.ts
1351
1959
  var readonlyProps = ({
1352
1960
  readOnly,
1353
1961
  "aria-readonly": ariaReadonly,
@@ -1363,6 +1971,8 @@ var readonlyProps = ({
1363
1971
  }
1364
1972
  };
1365
1973
  };
1974
+
1975
+ // ../core/src/html/aria-rules.ts
1366
1976
  var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
1367
1977
  var removeLandmarkRoleOverride = {
1368
1978
  kind: "removeRole",
@@ -1382,14 +1992,31 @@ function landmarkRoleRule({ tag, props, implicitRole }) {
1382
1992
  fixable: true,
1383
1993
  severity: "error",
1384
1994
  fix: removeLandmarkRoleOverride,
1385
- message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
1995
+ diagnostic: HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role)
1996
+ }
1997
+ ];
1998
+ }
1999
+ function requireAccessibleName({ tag, props }) {
2000
+ if ("aria-label" in props || "aria-labelledby" in props) return [];
2001
+ return [
2002
+ {
2003
+ valid: false,
2004
+ fixable: false,
2005
+ severity: "warning",
2006
+ diagnostic: AriaDiagnostics.missingAccessibleName(tag)
1386
2007
  }
1387
2008
  ];
1388
2009
  }
1389
- var HTML_ARIA_RULES = [landmarkRoleRule];
2010
+ var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
2011
+ function landmarkNameAdvisory(ctx) {
2012
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2013
+ return requireAccessibleName(ctx);
2014
+ }
2015
+
2016
+ // ../core/src/html/contracts.ts
1390
2017
  function isOpenContent(...blockedTags) {
1391
- const set = new Set(blockedTags);
1392
- return (child) => isObject(child) && "type" in child && (!isString(child.type) || !set.has(child.type));
2018
+ const set2 = new Set(blockedTags);
2019
+ return (child) => isObject(child) && "type" in child && (!isString(child.type) || !set2.has(child.type));
1393
2020
  }
1394
2021
  var METADATA_TAGS = ["script", "template"];
1395
2022
  var metadataMatch = isTag(...METADATA_TAGS);
@@ -1400,10 +2027,10 @@ function firstOptional(name, tag) {
1400
2027
  return { name, match: isTag(tag), cardinality: { max: 1 }, position: "first" };
1401
2028
  }
1402
2029
  function contract(children) {
1403
- return { strict: "warn", children };
2030
+ return { diagnostics: warnDiagnostics, children };
1404
2031
  }
1405
2032
  function ariaContract(aria) {
1406
- return { strict: "warn", aria };
2033
+ return { diagnostics: warnDiagnostics, aria };
1407
2034
  }
1408
2035
  function firstChildContract(name, tag) {
1409
2036
  return contract([firstOptional(name, tag), { name: "content", match: isOpenContent(tag) }]);
@@ -1490,7 +2117,15 @@ var textOnlyContract = contract([
1490
2117
  match: (child) => isString(child) || isNumber(child)
1491
2118
  }
1492
2119
  ]);
1493
- var landmarkContract = ariaContract([landmarkRoleRule]);
2120
+ var landmarkContract = ariaContract([landmarkRoleRule, landmarkNameAdvisory]);
2121
+ var dialogContract = ariaContract([requireAccessibleName]);
2122
+ var menuContract = ariaContract([requireAccessibleName]);
2123
+ var menubarContract = ariaContract([requireAccessibleName]);
2124
+ var treeContract = ariaContract([requireAccessibleName]);
2125
+ var gridContract = ariaContract([requireAccessibleName]);
2126
+ var listboxContract = ariaContract([requireAccessibleName]);
2127
+ var tablistContract = ariaContract([requireAccessibleName]);
2128
+ var radiogroupContract = ariaContract([requireAccessibleName]);
1494
2129
  var CONTRACT_GROUPS = [
1495
2130
  [VOID_TAGS, voidContract],
1496
2131
  [TEXT_ONLY_TAGS, textOnlyContract],
@@ -1517,19 +2152,28 @@ var htmlContracts = {
1517
2152
  figure: figureContract,
1518
2153
  details: detailsContract,
1519
2154
  fieldset: fieldsetContract,
2155
+ dialog: dialogContract,
1520
2156
  head: headContract,
1521
2157
  html: htmlContract
1522
2158
  };
2159
+
2160
+ // ../core/src/html/evaluators.ts
2161
+ var htmlDiagnostics = warnDiagnostics;
1523
2162
  function buildEvaluatorMap() {
1524
- const map = /* @__PURE__ */ new Map();
1525
- for (const [tag, { children }] of Object.entries(htmlContracts)) {
2163
+ const map2 = /* @__PURE__ */ new Map();
2164
+ iterate.forEachEntry(htmlContracts, (tag, { children }) => {
1526
2165
  if (children?.length) {
1527
- map.set(tag, new ChildrenEvaluator(children, "warn", `<${tag}>`));
2166
+ map2.set(tag, new ChildrenEvaluator(children, htmlDiagnostics, `<${tag}>`));
1528
2167
  }
1529
- }
1530
- return map;
2168
+ });
2169
+ return map2;
1531
2170
  }
1532
2171
  var HTML_EVALUATORS = buildEvaluatorMap();
2172
+ function getHtmlChildrenEvaluator(tag) {
2173
+ return typeof tag === "string" ? HTML_EVALUATORS.get(tag) : void 0;
2174
+ }
2175
+
2176
+ // ../core/src/html/prop-normalizers.ts
1533
2177
  var HTML_FORM_NORMALIZERS = /* @__PURE__ */ new Map([
1534
2178
  ["button", [disabledProps]],
1535
2179
  ["input", [disabledProps, readonlyProps, invalidProps]],
@@ -1542,436 +2186,567 @@ function getHtmlPropNormalizers(tag) {
1542
2186
  return typeof tag === "string" ? HTML_FORM_NORMALIZERS.get(tag) : void 0;
1543
2187
  }
1544
2188
 
1545
- // ../core/dist/chunk-3T4EM5FG.js
1546
- var falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
1547
- var cx = clsx;
1548
- var cva = (base, config) => (props) => {
1549
- var _config_compoundVariants;
1550
- 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);
1551
- const { variants, defaultVariants } = config;
1552
- const getVariantClassNames = Object.keys(variants).map((variant) => {
1553
- const variantProp = props === null || props === void 0 ? void 0 : props[variant];
1554
- const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
1555
- if (variantProp === null) return null;
1556
- const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
1557
- return variants[variant][variantKey];
1558
- });
1559
- const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
1560
- let [key, value] = param;
1561
- if (value === void 0) {
1562
- return acc;
1563
- }
1564
- acc[key] = value;
1565
- return acc;
1566
- }, {});
1567
- const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param) => {
1568
- let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;
1569
- return Object.entries(compoundVariantOptions).every((param2) => {
1570
- let [key, value] = param2;
1571
- return Array.isArray(value) ? value.includes({
1572
- ...defaultVariants,
1573
- ...propsWithoutUndefined
1574
- }[key]) : {
1575
- ...defaultVariants,
1576
- ...propsWithoutUndefined
1577
- }[key] === value;
1578
- }) ? [
1579
- ...acc,
1580
- cvClass,
1581
- cvClassName
1582
- ] : acc;
1583
- }, []);
1584
- return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
1585
- };
1586
- function cva2(base, config) {
1587
- const fn = cva(base, config);
1588
- return (props) => cn(fn(props));
1589
- }
1590
- var StaticClassResolver = class {
1591
- #baseClass;
1592
- #cache = /* @__PURE__ */ new Map();
1593
- #resolveTag;
1594
- constructor(baseClass, tagMap) {
1595
- this.#baseClass = Array.isArray(baseClass) ? baseClass.join(" ") : baseClass;
1596
- this.#resolveTag = tagMap ? (tag) => {
1597
- const extra = tagMap[tag];
1598
- if (!extra) return this.#baseClass;
1599
- const extraStr = Array.isArray(extra) ? extra.join(" ") : extra;
1600
- return `${this.#baseClass} ${extraStr}`;
1601
- } : () => this.#baseClass;
1602
- }
1603
- resolve(tag, skipTagMap = false) {
1604
- if (typeof tag !== "string" || skipTagMap) return this.#baseClass;
1605
- const cached = this.#cache.get(tag);
1606
- if (cached !== void 0) {
1607
- this.#cache.delete(tag);
1608
- this.#cache.set(tag, cached);
1609
- return cached;
1610
- }
1611
- const result = this.#resolveTag(tag);
1612
- this.#cache.set(tag, result);
1613
- if (this.#cache.size > 200) {
1614
- const lru = this.#cache.keys().next().value;
1615
- if (lru !== void 0) this.#cache.delete(lru);
1616
- }
1617
- return result;
1618
- }
1619
- };
1620
- var VariantClassResolver = class _VariantClassResolver {
1621
- #cvaFn;
1622
- #recipeMap;
1623
- #variantKeys;
1624
- #precomputedClasses;
1625
- #cache = /* @__PURE__ */ new Map();
1626
- constructor(cvaFn, recipeMap, variantKeys, precomputedClasses) {
1627
- this.#cvaFn = cvaFn ?? null;
1628
- this.#recipeMap = Object.freeze(recipeMap ?? {});
1629
- this.#variantKeys = variantKeys ?? null;
1630
- this.#precomputedClasses = precomputedClasses ?? null;
1631
- }
1632
- resolve({ props, recipe }) {
1633
- const normalizedKey = recipe ?? "__none__";
1634
- const cacheKey = this.#createCacheKey(props, normalizedKey);
1635
- if (this.#precomputedClasses !== null) {
1636
- const precomputed = this.#precomputedClasses[cacheKey];
1637
- if (precomputed !== void 0) return precomputed;
1638
- }
1639
- const cached = this.#cache.get(cacheKey);
1640
- if (cached !== void 0) {
1641
- this.#cache.delete(cacheKey);
1642
- this.#cache.set(cacheKey, cached);
1643
- return cached;
1644
- }
1645
- const result = this.#compute(props, recipe);
1646
- this.#cache.set(cacheKey, result);
1647
- if (this.#cache.size > 1e3) {
1648
- const lru = this.#cache.keys().next().value;
1649
- if (lru !== void 0) this.#cache.delete(lru);
1650
- }
1651
- return result;
1652
- }
1653
- #compute(props, recipe) {
1654
- if (!this.#cvaFn) return "";
1655
- if (!recipe) return this.#cvaFn(props);
1656
- const preset = this.#recipeMap[recipe];
1657
- if (!preset) return this.#cvaFn(props);
1658
- return this.#cvaFn({ ...preset, ...props });
1659
- }
1660
- // When variantKeys is provided, only those keys are included in the cache key — non-variant
1661
- // props (className, id, etc.) produce identical CVA output and must not fragment the cache.
1662
- // Iterating #variantKeys directly (fixed Set insertion order) avoids Object.keys + filter + sort.
1663
- // String is built incrementally to avoid a parts[] array allocation on every render.
1664
- #createCacheKey(props, recipe) {
1665
- if (this.#variantKeys !== null) {
1666
- let key2 = recipe;
1667
- for (const k of this.#variantKeys) {
1668
- if (k in props) key2 += `|${k}:${_VariantClassResolver.#serializeValue(props[k])}`;
1669
- }
1670
- return key2;
1671
- }
1672
- let key = recipe;
1673
- for (const k of Object.keys(props).sort()) {
1674
- key += `|${k}:${_VariantClassResolver.#serializeValue(props[k])}`;
2189
+ // ../../lib/styling/src/variant-pass/compile-variant-lookup.ts
2190
+ function buildPrecomputedKey(props) {
2191
+ return Object.entries(props).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`).join("|");
2192
+ }
2193
+
2194
+ // ../../lib/styling/src/variant-pass/variant-pass.ts
2195
+ function createVariantPass(activeProps, config) {
2196
+ return {
2197
+ name: "variant",
2198
+ execute() {
2199
+ const preset = typeof activeProps["preset"] === "string" ? activeProps["preset"] : void 0;
2200
+ const presetDefaults = preset !== void 0 && config.presets?.[preset] !== void 0 ? config.presets[preset] : {};
2201
+ const resolved = {
2202
+ ...config.defaults,
2203
+ ...presetDefaults,
2204
+ ...activeProps
2205
+ };
2206
+ const classes = [];
2207
+ iterate.forEachEntry(config.variants, (key, valueMap) => {
2208
+ const active = resolved[key];
2209
+ if (typeof active === "string") {
2210
+ const cls = valueMap[active];
2211
+ if (cls !== void 0) classes.push(cls);
2212
+ }
2213
+ });
2214
+ return { context: { classes } };
1675
2215
  }
1676
- return key;
1677
- }
1678
- static #serializeValue(value) {
1679
- if (value === void 0) return "u";
1680
- if (value === null) return "n";
1681
- if (typeof value === "boolean") return `b:${value}`;
1682
- if (typeof value === "string") return `s:${value}`;
1683
- return `x:${String(value)}`;
1684
- }
1685
- };
1686
- function createClassPipeline(resolved) {
1687
- const baseClass = resolved.baseClassName ?? "";
1688
- const cvaFn = resolved.variants ? cva2("", {
1689
- variants: resolved.variants,
1690
- defaultVariants: resolved.defaultVariants,
1691
- compoundVariants: resolved.compoundVariants
1692
- }) : null;
1693
- const variantKeys = resolved.variants ? new Set(Object.keys(resolved.variants)) : void 0;
1694
- const staticResolver = new StaticClassResolver(baseClass, resolved.tagMap);
1695
- const variantResolver = new VariantClassResolver(
1696
- cvaFn,
1697
- resolved.recipeMap,
1698
- variantKeys,
1699
- resolved.precomputedClasses
1700
- );
1701
- return function resolveClasses(tag, props, className, recipe) {
1702
- const staticClasses = staticResolver.resolve(tag, recipe !== void 0);
1703
- const variantClasses = variantResolver.resolve({ props, recipe });
1704
- if (!className)
1705
- return staticClasses && variantClasses ? `${staticClasses} ${variantClasses}` : staticClasses || variantClasses;
1706
- return cn(staticClasses, variantClasses, className);
1707
2216
  };
1708
2217
  }
1709
2218
 
1710
- // ../core/dist/index.js
1711
- var FULL_CAPABILITIES = {
1712
- createClassPipeline,
1713
- AriaEngine: AriaPolicyEngine,
1714
- htmlAriaRules: HTML_ARIA_RULES,
1715
- htmlPropNormalizersFn: getHtmlPropNormalizers
1716
- };
1717
- function createPolymorphic2(options = {}) {
1718
- return createPolymorphic(options, FULL_CAPABILITIES);
2219
+ // ../core/src/resolver/resolver.ts
2220
+ function enforceAllowedAs(tag, allowedAs, diagnostics, displayName) {
2221
+ if (allowedAs.includes(tag)) return;
2222
+ if (!diagnostics) return;
2223
+ const component = displayName ?? String(tag);
2224
+ diagnostics.error(ContractDiagnostics.allowedAsViolation(String(tag), allowedAs, component));
1719
2225
  }
1720
2226
 
1721
- // ../../lib/adapter-utils/src/build-core-runtime.ts
1722
- var EMPTY_SET = /* @__PURE__ */ new Set();
1723
- function buildCoreRuntime(normalized) {
1724
- const runtime = createPolymorphic2(normalized);
1725
- const ownedKeys = "classPlugin" in runtime ? runtime.classPlugin.ownedKeys ?? EMPTY_SET : EMPTY_SET;
1726
- return { runtime, ownedKeys };
2227
+ // ../../lib/adapter-utils/src/apply-prop-normalizers.ts
2228
+ function applyPropNormalizers(tag, props, additional) {
2229
+ const normalizers = [...getHtmlPropNormalizers(tag) ?? [], ...additional ?? []];
2230
+ if (normalizers.length === 0) return props;
2231
+ return normalizers.reduce((acc, fn) => ({ ...acc, ...fn(acc) }), props);
1727
2232
  }
1728
2233
 
1729
- // ../../lib/adapter-utils/src/build-engines.ts
1730
- function buildEngines(strict, childRules, context) {
1731
- return childRules?.length ? { childrenEvaluator: new ChildrenEvaluator(childRules, strict, context) } : {};
2234
+ // ../../lib/adapter-utils/src/define-component.ts
2235
+ function defineContractComponent(options) {
2236
+ return (factory) => factory(options);
1732
2237
  }
1733
2238
 
1734
- // ../../lib/adapter-utils/src/compose-filter.ts
1735
- function composeFilter(ownedKeys, filterProps) {
1736
- const defaultFilter = (key, variantKeys) => variantKeys.has(key) || ownedKeys.has(key);
1737
- if (!filterProps) {
1738
- return defaultFilter;
1739
- }
1740
- return (key, variantKeys) => defaultFilter(key, variantKeys) || filterProps(key, variantKeys);
2239
+ // ../../lib/adapter-utils/src/build-engines.ts
2240
+ function buildEngines(diagnostics, childRules, context) {
2241
+ return childRules?.length ? { childrenEvaluator: new ChildrenEvaluator(childRules, diagnostics, context) } : {};
1741
2242
  }
1742
2243
 
1743
2244
  // ../../lib/adapter-utils/src/resolve-adapter-common-options.ts
1744
- function resolveAdapterCommonOptions(options, defaultName = "PolymorphicComponent", defaultStrict = "throw") {
2245
+ function resolveAdapterCommonOptions(options, defaultName = "PolymorphicComponent", defaultDiagnostics = throwDiagnostics) {
1745
2246
  return {
1746
2247
  name: options.name ?? defaultName,
1747
- strict: options.enforcement?.strict ?? defaultStrict
2248
+ diagnostics: options.enforcement?.diagnostics ?? defaultDiagnostics
1748
2249
  };
1749
2250
  }
1750
2251
 
1751
2252
  // ../../lib/adapter-utils/src/slot-validator.ts
1752
- var SlotValidator = class extends StrictBase {
2253
+ var SlotValidator = class extends InvariantBase {
1753
2254
  #name;
1754
2255
  #elementTerm;
1755
- constructor(name, strict, elementTerm) {
1756
- super(strict);
2256
+ constructor(name, diagnostics, elementTerm) {
2257
+ super(diagnostics);
1757
2258
  this.#name = name;
1758
2259
  this.#elementTerm = elementTerm;
1759
2260
  }
1760
2261
  assertExclusive() {
1761
- this.violate(`${this.#name}: "as" and "asChild" are mutually exclusive`);
2262
+ this.violate(SlotDiagnostics.exclusive(this.#name));
1762
2263
  }
1763
2264
  warnDiscardedChildren(count) {
1764
- const suffix = count === 1 ? "" : "ren";
1765
- this.warn(
1766
- `${this.#name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${this.#elementTerm}s are valid asChild children.`
1767
- );
2265
+ this.warn(SlotDiagnostics.discardedChildren(this.#name, this.#elementTerm, count));
1768
2266
  }
1769
2267
  assertSingleChild(count) {
1770
- const msg = count === 0 ? `${this.#name}: asChild requires a ${this.#elementTerm} child` : `${this.#name}: asChild requires exactly one ${this.#elementTerm} child, got ${count}`;
1771
- this.violate(msg);
2268
+ this.violate(
2269
+ count === 0 ? SlotDiagnostics.singleChildRequired(this.#name, this.#elementTerm) : SlotDiagnostics.singleChildExceeded(this.#name, this.#elementTerm, count)
2270
+ );
1772
2271
  }
1773
2272
  };
1774
2273
 
1775
- // ../../adapters/vue/src/slot/invariant.ts
1776
- function panic2(message) {
1777
- throw new Error(message);
2274
+ // ../../lib/adapter-utils/src/build-definition.ts
2275
+ function buildDefinition(name, tag) {
2276
+ return {
2277
+ identity: {
2278
+ id: name,
2279
+ name,
2280
+ tag
2281
+ },
2282
+ capabilities: {},
2283
+ metadata: {},
2284
+ diagnostics: []
2285
+ };
1778
2286
  }
1779
- function invariant(condition, message) {
1780
- if (!condition) panic2(message);
2287
+
2288
+ // ../../lib/adapter-utils/src/build-variant-config.ts
2289
+ function flattenClassName(cls) {
2290
+ return Array.isArray(cls) ? cls.join(" ") : cls;
2291
+ }
2292
+ function mapVariantRecord(source, mapper) {
2293
+ return iterate.mapValues(
2294
+ source,
2295
+ (inner) => iterate.mapValues(inner, mapper)
2296
+ );
2297
+ }
2298
+ function buildVariantConfig(variants, presets, defaults, compounds) {
2299
+ return {
2300
+ variants: mapVariantRecord(variants ?? {}, flattenClassName),
2301
+ ...presets !== void 0 && Object.keys(presets).length > 0 && { presets },
2302
+ ...defaults !== void 0 && Object.keys(defaults).length > 0 && { defaults },
2303
+ ...compounds !== void 0 && compounds.length > 0 && {
2304
+ compounds
2305
+ }
2306
+ };
1781
2307
  }
1782
2308
 
1783
- // ../../adapters/vue/src/slot/slot-validator.ts
1784
- var SlotValidator2 = class extends SlotValidator {
1785
- constructor(name, strict) {
1786
- super(name, strict, "VNode");
1787
- }
1788
- };
2309
+ // ../../runtime/core/src/apply-attributes.ts
2310
+ function applyAttributes(nodeId, incoming, decoration, removedKeys) {
2311
+ const existing = decoration[nodeId] ?? {};
2312
+ const next = { ...existing.attributes };
2313
+ if (removedKeys !== void 0) {
2314
+ iterate.forEachSet(removedKeys, (key) => {
2315
+ delete next[key];
2316
+ });
2317
+ }
2318
+ Object.assign(next, incoming);
2319
+ const { attributes: _dropped, ...rest } = existing;
2320
+ const nextDecoration = Object.keys(next).length > 0 ? { ...rest, attributes: next } : rest;
2321
+ return { ...decoration, [nodeId]: nextDecoration };
2322
+ }
1789
2323
 
1790
- // ../../adapters/vue/src/slot/Slottable.ts
1791
- import { defineComponent, h, Fragment } from "vue";
1792
- var Slottable = defineComponent({
1793
- name: "Slottable",
1794
- setup(_, { slots }) {
1795
- return () => h(Fragment, null, slots.default?.());
1796
- }
1797
- });
2324
+ // ../../runtime/core/src/get-active-props.ts
2325
+ function getActiveProps(nodeId, decoration) {
2326
+ const dec = decoration[nodeId];
2327
+ return { ...dec?.attributes ?? {}, ...dec?.variants ?? {} };
2328
+ }
1798
2329
 
1799
- // ../../adapters/vue/src/slot/extractSlottable.ts
1800
- import { h as h2, Fragment as Fragment2 } from "vue";
1801
- function isSlottableVNode(vnode) {
1802
- return vnode.type === Slottable;
2330
+ // ../../runtime/core/src/render-component.ts
2331
+ function renderComponent(definition, tree, render, backend) {
2332
+ return backend.render({ definition, tree, render });
1803
2333
  }
1804
- function extractSlottable(children) {
1805
- const slottables = children.filter(isSlottableVNode);
1806
- invariant(slottables.length <= 1, "Slot: multiple Slottable children are not allowed");
1807
- if (slottables.length === 0) return null;
1808
- const [slottable] = slottables;
1809
- invariant(slottable !== void 0, "Slottable element is undefined");
1810
- const slots = slottable.children;
1811
- const defaultChildren = slots?.default?.() ?? [];
1812
- invariant(
1813
- defaultChildren.length === 1,
1814
- `Slottable expects exactly one VNode child, received ${defaultChildren.length}`
2334
+
2335
+ // ../../runtime/core/src/build-tree-context.ts
2336
+ function buildNode(input, slotAssignments, seenIds, diagnostics) {
2337
+ if (seenIds.has(input.id)) {
2338
+ diagnostics.push({
2339
+ code: "duplicate-node-id",
2340
+ message: `Duplicate node id "${input.id}"`,
2341
+ severity: "error"
2342
+ });
2343
+ } else {
2344
+ seenIds.add(input.id);
2345
+ }
2346
+ if (input.slot !== void 0) {
2347
+ slotAssignments.set(input.id, input.slot);
2348
+ }
2349
+ const children = Object.freeze(
2350
+ (input.children ?? []).map((child) => buildNode(child, slotAssignments, seenIds, diagnostics))
1815
2351
  );
1816
- const child = defaultChildren[0];
1817
- invariant(child !== void 0, "Slottable child is undefined");
1818
- const index = children.indexOf(slottable);
1819
- return {
1820
- child,
1821
- rebuild(merged) {
1822
- const out = children.map((node, i) => i === index ? merged : node);
1823
- return h2(Fragment2, null, out);
2352
+ if (input.kind === "native") {
2353
+ return Object.freeze({ kind: "native", id: input.id, tag: input.tag, children });
2354
+ }
2355
+ return Object.freeze({ kind: "component", id: input.id, children });
2356
+ }
2357
+ function buildTreeContext(root) {
2358
+ const slotAssignments = /* @__PURE__ */ new Map();
2359
+ const diagnostics = [];
2360
+ const rootNode = buildNode(root, slotAssignments, /* @__PURE__ */ new Set(), diagnostics);
2361
+ return Object.freeze({ root: rootNode, slotAssignments, diagnostics });
2362
+ }
2363
+
2364
+ // ../../runtime/core/src/build-render-context.ts
2365
+ function buildRenderContext(decoration = {}) {
2366
+ return { decoration: new Map(Object.entries(decoration)) };
2367
+ }
2368
+
2369
+ // ../../lib/adapter-utils/src/resolve-compounds.ts
2370
+ function matchesCompound(active, compound) {
2371
+ const { class: _, ...conditions } = compound;
2372
+ return Object.entries(conditions).every(([key, expected]) => {
2373
+ const actual = active[key];
2374
+ return Array.isArray(expected) ? expected.includes(actual) : actual === expected;
2375
+ });
2376
+ }
2377
+ function resolveCompounds(active, compounds) {
2378
+ if (!compounds?.length) return [];
2379
+ const out = [];
2380
+ iterate.forEach(compounds, (compound) => {
2381
+ if (matchesCompound(active, compound)) {
2382
+ out.push(flattenClassName(compound.class));
1824
2383
  }
1825
- };
2384
+ });
2385
+ return out;
1826
2386
  }
1827
2387
 
1828
- // ../../adapters/vue/src/build-runtime.ts
1829
- function normalizeOptions(options) {
1830
- return { ...options, ...resolveAdapterCommonOptions(options) };
2388
+ // ../../lib/adapter-utils/src/resolve-classes.ts
2389
+ function resolveClasses(decoration, variantConfig, variantDefaults, recipe, compounds, variantLookup) {
2390
+ const active = getActiveProps("root", decoration);
2391
+ if (variantLookup !== void 0 && recipe === void 0) {
2392
+ const activeVariants = {};
2393
+ iterate.forEachKey(variantConfig.variants, (key) => {
2394
+ const value = active[key];
2395
+ if (typeof value === "string") activeVariants[key] = value;
2396
+ });
2397
+ const lookupKey = buildPrecomputedKey(activeVariants);
2398
+ const precomputedValue = variantLookup[lookupKey];
2399
+ if (precomputedValue !== void 0) {
2400
+ return {
2401
+ variantClasses: precomputedValue !== "" ? [precomputedValue] : [],
2402
+ compoundClasses: []
2403
+ };
2404
+ }
2405
+ }
2406
+ const passInput = recipe !== void 0 ? { ...active, preset: recipe } : active;
2407
+ const { context } = createVariantPass(passInput, variantConfig).execute({ classes: [] });
2408
+ const variantClasses = context.classes;
2409
+ const presetOverrides = recipe !== void 0 ? variantConfig.presets?.[recipe] ?? {} : {};
2410
+ const resolvedValues = {
2411
+ ...variantDefaults,
2412
+ ...presetOverrides,
2413
+ ...active
2414
+ };
2415
+ const compoundClasses = resolveCompounds(resolvedValues, compounds);
2416
+ return { variantClasses, compoundClasses };
1831
2417
  }
1832
- function buildRuntime(options) {
1833
- const normalized = normalizeOptions(options);
1834
- const { runtime, ownedKeys } = buildCoreRuntime(normalized);
1835
- const slotValidator = new SlotValidator2(normalized.name, normalized.strict);
1836
- const { childrenEvaluator } = buildEngines(
1837
- normalized.strict,
1838
- normalized.enforcement?.children,
1839
- normalized.name
1840
- );
1841
- const filterProps = composeFilter(ownedKeys, normalized.filterProps);
2418
+
2419
+ // ../../lib/adapter-utils/src/build-pipeline.ts
2420
+ function buildStylePipeline(variants, presets, defaults, compounds, variantLookup) {
2421
+ if (variants === void 0 && compounds === void 0 && presets === void 0 && variantLookup === void 0) {
2422
+ return void 0;
2423
+ }
2424
+ const variantConfig = buildVariantConfig(variants, presets, defaults, compounds);
1842
2425
  return {
1843
- runtime,
1844
- slotValidator,
1845
- filterProps,
1846
- ...childrenEvaluator !== void 0 && { childrenEvaluator }
2426
+ execute(decoration, recipe) {
2427
+ return resolveClasses(decoration, variantConfig, defaults, recipe, compounds, variantLookup);
2428
+ }
1847
2429
  };
1848
2430
  }
1849
2431
 
1850
- // ../../adapters/vue/src/render.ts
1851
- import { cloneVNode, h as h3 } from "vue";
2432
+ // ../../lib/adapter-utils/src/join-classes.ts
2433
+ function joinClasses(...classes) {
2434
+ return classes.filter(Boolean).join(" ");
2435
+ }
1852
2436
 
1853
- // ../../adapters/vue/src/normalize-children.ts
1854
- import { isVNode } from "vue";
1855
- function normalizeChildren(slots) {
1856
- const raw = slots.default?.() ?? [];
1857
- const vnodes = raw.filter(isVNode);
1858
- return { vnodes, discarded: raw.length - vnodes.length };
2437
+ // ../../lib/adapter-utils/src/decoration-utils.ts
2438
+ function withAttributes(dec, attributes) {
2439
+ const { attributes: _a, ...rest } = dec;
2440
+ return attributes !== void 0 ? { ...rest, attributes } : rest;
1859
2441
  }
1860
2442
 
1861
- // ../../adapters/vue/src/render.ts
1862
- function buildDirectives(as, asChild) {
2443
+ // ../../lib/adapter-utils/src/apply-aria.ts
2444
+ function applyAria(decoration, tag, ariaEngine) {
2445
+ if (ariaEngine === void 0) return decoration;
2446
+ const dec = decoration["root"];
2447
+ if (dec?.attributes === void 0) return decoration;
2448
+ const result = ariaEngine.validate(tag, dec.attributes);
2449
+ const cleaned = result.props;
1863
2450
  return {
1864
- ...as !== void 0 && { as },
1865
- ...asChild !== void 0 && { asChild }
2451
+ ...decoration,
2452
+ root: withAttributes(dec, Object.keys(cleaned).length > 0 ? cleaned : void 0)
1866
2453
  };
1867
2454
  }
1868
- function resolveRenderState(runtime, attrs, filterProps) {
1869
- const { as, asChild, class: className, recipe, ...rest } = attrs;
1870
- const tag = runtime.resolveTag(as);
1871
- const mergedProps = runtime.resolveProps(rest);
1872
- const baseProps = runtime.options.normalizeFn ? runtime.options.normalizeFn(mergedProps) : mergedProps;
1873
- const htmlNormalizers = runtime.options.htmlPropNormalizersFn?.(tag);
1874
- const normalizedProps = htmlNormalizers?.length ? htmlNormalizers.reduce((acc, fn) => ({ ...acc, ...fn(acc) }), baseProps) : baseProps;
1875
- const resolvedClass = runtime.resolveClasses(
1876
- tag,
1877
- normalizedProps,
1878
- typeof className === "string" ? className : void 0,
1879
- typeof recipe === "string" ? recipe : void 0
2455
+
2456
+ // ../../lib/adapter-utils/src/apply-filter-props.ts
2457
+ function applyFilterProps(decoration, filterFn, variantKeys) {
2458
+ if (filterFn === void 0) return decoration;
2459
+ const dec = decoration["root"];
2460
+ if (dec?.attributes === void 0) return decoration;
2461
+ const kept = Object.fromEntries(
2462
+ Object.entries(dec.attributes).filter(([k]) => !filterFn(k, variantKeys))
1880
2463
  );
1881
- const filteredProps = applyFilter(normalizedProps, filterProps, runtime.options.variantKeys);
1882
2464
  return {
1883
- tag,
1884
- directives: buildDirectives(as, asChild),
1885
- className: resolvedClass,
1886
- props: filteredProps
2465
+ ...decoration,
2466
+ root: withAttributes(dec, Object.keys(kept).length > 0 ? kept : void 0)
1887
2467
  };
1888
2468
  }
1889
- function validateSlotDirectives(directives, validator) {
1890
- const { as, asChild } = directives;
1891
- if (!asChild) return false;
1892
- if (as !== void 0) {
1893
- validator.assertExclusive();
1894
- return false;
2469
+
2470
+ // ../../adapters/vue/src/backend/extract-decoration.ts
2471
+ function isStyleValue(v) {
2472
+ return isString(v) || isNumber(v);
2473
+ }
2474
+ function isAttributeValue(v) {
2475
+ return isString(v) || isNumber(v) || typeof v === "boolean";
2476
+ }
2477
+ function assignIfNotEmpty(target, key, value) {
2478
+ if (Object.keys(value).length > 0) {
2479
+ target[key] = value;
1895
2480
  }
2481
+ }
2482
+ function extractStyles(value, styles) {
2483
+ if (!isObject(value)) return false;
2484
+ iterate.forEachEntry(value, (k, v) => {
2485
+ if (isStyleValue(v)) {
2486
+ styles[k] = v;
2487
+ }
2488
+ });
1896
2489
  return true;
1897
2490
  }
1898
- function tryRenderAsChild(state, children, discarded, validator) {
1899
- if (!validateSlotDirectives(state.directives, validator)) return null;
1900
- if (discarded > 0) validator.warnDiscardedChildren(discarded);
1901
- const extraction = extractSlottable(children);
1902
- if (extraction) {
1903
- const merged = cloneVNode(extraction.child, { ...state.props, class: state.className });
1904
- return extraction.rebuild(merged);
2491
+ function extractListener(key, value, listeners) {
2492
+ if (!key.startsWith("on") || !isFunction(value)) {
2493
+ return false;
1905
2494
  }
1906
- if (children.length === 1) {
1907
- const child = children[0];
1908
- if (child === void 0) return null;
1909
- return cloneVNode(child, { ...state.props, class: state.className });
2495
+ listeners[key] = value;
2496
+ return true;
2497
+ }
2498
+ function extractAttributeOrVariant(key, value, attributes, variants, variantKeys) {
2499
+ if (variantKeys?.has(key)) {
2500
+ variants[key] = value;
2501
+ } else {
2502
+ attributes[key] = value;
2503
+ }
2504
+ }
2505
+ function extractDecoration(props, variantKeys) {
2506
+ const attributes = {};
2507
+ const styles = {};
2508
+ const listeners = {};
2509
+ const variants = {};
2510
+ const dec = {};
2511
+ iterate.forEachEntry(props, (key, value) => {
2512
+ if (key === "children" || key === "slot" || key === "ref") return;
2513
+ if (key === "style" && extractStyles(value, styles)) return;
2514
+ if (extractListener(key, value, listeners)) return;
2515
+ if (isAttributeValue(value)) {
2516
+ extractAttributeOrVariant(key, value, attributes, variants, variantKeys);
2517
+ }
2518
+ });
2519
+ assignIfNotEmpty(dec, "attributes", attributes);
2520
+ assignIfNotEmpty(dec, "styles", styles);
2521
+ assignIfNotEmpty(dec, "listeners", listeners);
2522
+ assignIfNotEmpty(dec, "variants", variants);
2523
+ const ref = props.ref;
2524
+ if (ref !== void 0) {
2525
+ dec.ref = ref;
1910
2526
  }
1911
- validator.assertSingleChild(children.length);
1912
- return null;
2527
+ return dec;
1913
2528
  }
2529
+
2530
+ // ../../adapters/vue/src/helpers/render-normally.ts
2531
+ import { h as h2 } from "vue";
2532
+
2533
+ // ../../adapters/vue/src/backend/vue-backend.ts
2534
+ import { Fragment, h } from "vue";
2535
+
2536
+ // ../../adapters/vue/src/backend/build-props.ts
2537
+ function buildPropsFromDecoration(id, decoration) {
2538
+ return {
2539
+ key: id,
2540
+ ...decoration?.attributes,
2541
+ ...decoration?.styles !== void 0 ? { style: decoration.styles } : {},
2542
+ ...decoration?.listeners,
2543
+ ...decoration?.ref !== void 0 ? { ref: decoration.ref } : {}
2544
+ };
2545
+ }
2546
+
2547
+ // ../../adapters/vue/src/backend/vue-backend.ts
1914
2548
  var MULTI_WORD_EVENT_RE = /^on[A-Z][a-z]+[A-Z]/;
1915
- function normalizeEventKeys(props) {
2549
+ function normalizeListenerKeys(listeners) {
1916
2550
  const out = {};
1917
- for (const k in props) {
1918
- out[MULTI_WORD_EVENT_RE.test(k) ? "on" + k.slice(2).toLowerCase() : k] = props[k];
2551
+ for (const k in listeners) {
2552
+ out[MULTI_WORD_EVENT_RE.test(k) ? "on" + k.slice(2).toLowerCase() : k] = listeners[k];
1919
2553
  }
1920
2554
  return out;
1921
2555
  }
1922
- function buildDomProps(runtime, props, className, tag) {
1923
- const { role, ...rest } = normalizeEventKeys(props);
1924
- const base = {
2556
+ function renderNode(node, render, isRoot) {
2557
+ const children = node.children.map((child) => renderNode(child, render, false));
2558
+ if (node.kind === "component") {
2559
+ return h(Fragment, isRoot ? null : { key: node.id }, children);
2560
+ }
2561
+ const decoration = render.decoration.get(node.id);
2562
+ const base = buildPropsFromDecoration(isRoot ? "" : node.id, decoration);
2563
+ const { key: _key, ...rest } = base;
2564
+ const props = {
2565
+ ...isRoot ? {} : { key: node.id },
1925
2566
  ...rest,
1926
- class: className,
1927
- ...isKnownAriaRole(role) && { role }
2567
+ ...decoration?.listeners !== void 0 ? normalizeListenerKeys(decoration.listeners) : {}
2568
+ };
2569
+ return h(node.tag, props, children.length > 0 ? children : void 0);
2570
+ }
2571
+ var vueBackend = {
2572
+ render(context) {
2573
+ return renderNode(context.tree.root, context.render, true);
2574
+ }
2575
+ };
2576
+
2577
+ // ../../adapters/vue/src/helpers/render-normally.ts
2578
+ function renderNormally(definition, tag, decoration, slots) {
2579
+ const treeCtx = buildTreeContext({ kind: "native", tag, id: "root", children: [] });
2580
+ const rendered = renderComponent(definition, treeCtx, buildRenderContext(decoration), vueBackend);
2581
+ if (slots?.default === void 0) return rendered;
2582
+ const { key: _key, ...props } = rendered.props ?? {};
2583
+ return h2(tag, props, { default: slots.default });
2584
+ }
2585
+
2586
+ // ../../adapters/vue/src/normalize-children.ts
2587
+ import { isVNode } from "vue";
2588
+ function normalizeChildren(slots) {
2589
+ const raw = slots.default?.() ?? [];
2590
+ const vnodes = raw.filter(isVNode);
2591
+ return { vnodes, discarded: raw.length - vnodes.length };
2592
+ }
2593
+
2594
+ // ../../adapters/vue/src/slot/Slottable.ts
2595
+ import { defineComponent, h as h3, Fragment as Fragment2 } from "vue";
2596
+ var Slottable = defineComponent({
2597
+ name: "Slottable",
2598
+ setup(_, { slots }) {
2599
+ return () => h3(Fragment2, null, slots.default?.());
2600
+ }
2601
+ });
2602
+
2603
+ // ../../adapters/vue/src/slot/extractSlottable.ts
2604
+ import { h as h4, Fragment as Fragment3 } from "vue";
2605
+ function isSlottableVNode(vnode) {
2606
+ return vnode.type === Slottable;
2607
+ }
2608
+ function extractSlottable(children) {
2609
+ const slottables = children.filter(isSlottableVNode);
2610
+ invariant(slottables.length <= 1, "Slot: multiple Slottable children are not allowed");
2611
+ if (slottables.length === 0) return null;
2612
+ const [slottable] = slottables;
2613
+ invariant(slottable !== void 0, "Slottable element is undefined");
2614
+ const slots = slottable.children;
2615
+ const defaultChildren = slots?.default?.() ?? [];
2616
+ invariant(
2617
+ defaultChildren.length === 1,
2618
+ `Slottable expects exactly one VNode child, received ${defaultChildren.length}`
2619
+ );
2620
+ const child = defaultChildren[0];
2621
+ invariant(child !== void 0, "Slottable child is undefined");
2622
+ const index = children.indexOf(slottable);
2623
+ return {
2624
+ child,
2625
+ rebuild(merged) {
2626
+ const out = children.map((node, i) => i === index ? merged : node);
2627
+ return h4(Fragment3, null, out);
2628
+ }
1928
2629
  };
1929
- if (typeof tag !== "string") return base;
1930
- const validated = runtime.resolveAria(tag, base);
1931
- const {
1932
- className: _className,
1933
- ref: _ref,
1934
- children: _children,
1935
- ...validatedRest
1936
- } = validated.props;
1937
- return { ...validatedRest, class: className };
1938
- }
1939
- function render({
1940
- runtime,
1941
- attrs,
1942
- slots,
1943
- filterProps,
1944
- slotValidator,
1945
- childrenEvaluator,
1946
- resolvedState
1947
- }) {
1948
- const state = resolvedState ?? resolveRenderState(runtime, attrs, filterProps);
1949
- const { vnodes: children, discarded } = normalizeChildren(slots);
1950
- if (process.env.NODE_ENV !== "production") childrenEvaluator?.evaluate(children);
1951
- const slotResult = tryRenderAsChild(state, children, discarded, slotValidator);
1952
- if (slotResult !== null) return slotResult;
1953
- const domProps = buildDomProps(runtime, state.props, state.className, state.tag);
1954
- return h3(state.tag, domProps, slots.default ? { default: slots.default } : void 0);
1955
2630
  }
1956
2631
 
1957
2632
  // ../../adapters/vue/src/create-contract-component.ts
2633
+ var EMPTY_SET = /* @__PURE__ */ new Set();
1958
2634
  function createContractComponent(options) {
1959
- const bundle = buildRuntime(options);
2635
+ const resolved = resolveAdapterCommonOptions(options);
2636
+ const displayName = resolved.name;
2637
+ const defaultTag = options.tag ?? "div";
2638
+ const definition = buildDefinition(displayName, defaultTag);
2639
+ const variantKeys = new Set(Object.keys(options.styling?.variants ?? {}));
2640
+ const domDefaults = options.defaults;
2641
+ const stylePipeline = buildStylePipeline(
2642
+ options.styling?.variants,
2643
+ options.styling?.presets,
2644
+ options.styling?.defaults ?? {},
2645
+ options.styling?.compounds,
2646
+ void 0
2647
+ );
2648
+ const base = options.styling?.base !== void 0 ? flattenClassName(options.styling.base) : void 0;
2649
+ const filterFn = options.filterProps;
2650
+ const allowedAs = options.enforcement?.allowedAs;
2651
+ const normalizeFn = options.normalize;
2652
+ const enforcementNormalizers = options.enforcement?.props;
2653
+ const classPlugin = options.styling?.plugin !== void 0 ? options.styling.plugin(
2654
+ options.styling,
2655
+ resolved.diagnostics
2656
+ ) : void 0;
2657
+ const pluginKeys = classPlugin?.ownedKeys ?? EMPTY_SET;
2658
+ const { childrenEvaluator: explicitEvaluator } = buildEngines(
2659
+ resolved.diagnostics,
2660
+ options.enforcement?.children,
2661
+ displayName
2662
+ );
2663
+ const ariaEngine = options.enforcement !== void 0 ? new AriaPolicyEngine(resolved.diagnostics) : void 0;
2664
+ const slotValidator = new SlotValidator(displayName, resolved.diagnostics, "VNode");
1960
2665
  const Component = defineComponent2({
1961
- name: options.name ?? "PolymorphicComponent",
1962
- // Without inheritAttrs: false Vue would double-bind attrs onto the root element
1963
- // before our render pipeline has a chance to filter, transform, and re-apply them.
2666
+ name: displayName,
1964
2667
  inheritAttrs: false,
1965
2668
  setup(_, { attrs, slots }) {
1966
- const resolvedState = computed(
1967
- () => resolveRenderState(bundle.runtime, attrs, bundle.filterProps)
1968
- );
1969
- return () => render({ ...bundle, attrs, slots, resolvedState: resolvedState.value });
2669
+ const decorationState = computed(() => {
2670
+ const {
2671
+ as,
2672
+ asChild,
2673
+ class: callerClass,
2674
+ recipe,
2675
+ ...ownProps
2676
+ } = attrs;
2677
+ const tag = typeof as === "string" ? as : defaultTag;
2678
+ if (allowedAs !== void 0)
2679
+ enforceAllowedAs(tag, allowedAs, resolved.diagnostics, displayName);
2680
+ const rawBaseProps = domDefaults !== void 0 ? { ...domDefaults, ...ownProps } : ownProps;
2681
+ const normalizedProps = applyPropNormalizers(tag, rawBaseProps, enforcementNormalizers);
2682
+ const baseProps = normalizeFn !== void 0 ? normalizeFn(normalizedProps) : normalizedProps;
2683
+ const pluginProps = {};
2684
+ const propsForExtraction = pluginKeys.size > 0 ? Object.fromEntries(
2685
+ Object.entries(baseProps).filter(([k]) => {
2686
+ if (pluginKeys.has(k)) {
2687
+ pluginProps[k] = baseProps[k];
2688
+ return false;
2689
+ }
2690
+ return true;
2691
+ })
2692
+ ) : baseProps;
2693
+ const rootDec = extractDecoration(propsForExtraction, variantKeys);
2694
+ const decoration = applyAria(
2695
+ Object.keys(rootDec).length > 0 ? { root: rootDec } : {},
2696
+ tag,
2697
+ ariaEngine
2698
+ );
2699
+ const recipeName = typeof recipe === "string" ? recipe : void 0;
2700
+ const { variantClasses, compoundClasses } = stylePipeline ? stylePipeline.execute(decoration, recipeName) : { variantClasses: [], compoundClasses: [] };
2701
+ const rawClassName = joinClasses(
2702
+ base,
2703
+ ...variantClasses,
2704
+ ...compoundClasses,
2705
+ typeof callerClass === "string" ? callerClass : void 0
2706
+ );
2707
+ const className = classPlugin !== void 0 ? classPlugin.pipeline(tag, pluginProps, rawClassName, recipeName) : rawClassName;
2708
+ let finalDecoration = className ? applyAttributes("root", { className }, decoration, variantKeys) : decoration;
2709
+ finalDecoration = applyFilterProps(finalDecoration, filterFn, variantKeys);
2710
+ return { tag, asChild, as, finalDecoration, className };
2711
+ });
2712
+ return () => {
2713
+ const { tag, asChild, as, finalDecoration, className } = decorationState.value;
2714
+ const { vnodes: children, discarded } = normalizeChildren(slots);
2715
+ if (process.env.NODE_ENV !== "production") {
2716
+ const childEval = explicitEvaluator ?? getHtmlChildrenEvaluator(tag);
2717
+ childEval?.evaluate(children);
2718
+ }
2719
+ if (asChild === true) {
2720
+ if (typeof as === "string") {
2721
+ slotValidator.assertExclusive();
2722
+ } else {
2723
+ if (discarded > 0) slotValidator.warnDiscardedChildren(discarded);
2724
+ const extraction = extractSlottable(children);
2725
+ if (extraction) {
2726
+ return extraction.rebuild(
2727
+ cloneVNode(extraction.child, {
2728
+ ...finalDecoration["root"]?.attributes,
2729
+ class: className
2730
+ })
2731
+ );
2732
+ }
2733
+ if (children.length === 1 && children[0] !== void 0) {
2734
+ return cloneVNode(children[0], {
2735
+ ...finalDecoration["root"]?.attributes,
2736
+ class: className
2737
+ });
2738
+ }
2739
+ slotValidator.assertSingleChild(children.length);
2740
+ return null;
2741
+ }
2742
+ }
2743
+ return renderNormally(definition, tag, finalDecoration, slots);
2744
+ };
1970
2745
  }
1971
2746
  });
1972
2747
  applyDisplayName(Component, options.name);
1973
- if (typeof bundle.runtime.options.defaultTag === "string") {
1974
- Object.assign(Component, { [COMPONENT_DEFAULT_TAG]: bundle.runtime.options.defaultTag });
2748
+ if (typeof defaultTag === "string") {
2749
+ Object.assign(Component, { [COMPONENT_DEFAULT_TAG]: defaultTag });
1975
2750
  }
1976
2751
  return Component;
1977
2752
  }