stimeo-ui 0.4.0 → 0.5.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/controllers/announcer_controller.js.map +1 -1
  3. package/dist/controllers/aspect_ratio_controller.d.ts +2 -2
  4. package/dist/controllers/aspect_ratio_controller.js +1 -1
  5. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  6. package/dist/controllers/breadcrumb_controller.js +5 -1
  7. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  8. package/dist/controllers/carousel_controller.js +5 -1
  9. package/dist/controllers/carousel_controller.js.map +1 -1
  10. package/dist/controllers/clipboard_controller.js +8 -3
  11. package/dist/controllers/clipboard_controller.js.map +1 -1
  12. package/dist/controllers/collapsible_controller.d.ts +1 -1
  13. package/dist/controllers/collapsible_controller.js +4 -1
  14. package/dist/controllers/collapsible_controller.js.map +1 -1
  15. package/dist/controllers/color_picker_controller.d.ts +2 -2
  16. package/dist/controllers/color_picker_controller.js +6 -2
  17. package/dist/controllers/color_picker_controller.js.map +1 -1
  18. package/dist/controllers/combobox_controller.js.map +1 -1
  19. package/dist/controllers/command_palette_controller.js.map +1 -1
  20. package/dist/controllers/context_menu_controller.d.ts +1 -1
  21. package/dist/controllers/context_menu_controller.js +2 -2
  22. package/dist/controllers/context_menu_controller.js.map +1 -1
  23. package/dist/controllers/countdown_controller.d.ts +5 -3
  24. package/dist/controllers/countdown_controller.js +5 -1
  25. package/dist/controllers/countdown_controller.js.map +1 -1
  26. package/dist/controllers/date_range_picker_controller.js +5 -1
  27. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  28. package/dist/controllers/direct_upload_controller.d.ts +1 -1
  29. package/dist/controllers/direct_upload_controller.js +3 -3
  30. package/dist/controllers/direct_upload_controller.js.map +1 -1
  31. package/dist/controllers/empty_state_controller.d.ts +27 -7
  32. package/dist/controllers/empty_state_controller.js +107 -16
  33. package/dist/controllers/empty_state_controller.js.map +1 -1
  34. package/dist/controllers/flash_controller.d.ts +25 -5
  35. package/dist/controllers/flash_controller.js +161 -21
  36. package/dist/controllers/flash_controller.js.map +1 -1
  37. package/dist/controllers/form_validation_controller.js +8 -2
  38. package/dist/controllers/form_validation_controller.js.map +1 -1
  39. package/dist/controllers/frame_loading_controller.d.ts +19 -2
  40. package/dist/controllers/frame_loading_controller.js +94 -22
  41. package/dist/controllers/frame_loading_controller.js.map +1 -1
  42. package/dist/controllers/highlight_controller.d.ts +8 -4
  43. package/dist/controllers/highlight_controller.js +38 -1
  44. package/dist/controllers/highlight_controller.js.map +1 -1
  45. package/dist/controllers/idle_controller.d.ts +2 -1
  46. package/dist/controllers/idle_controller.js +13 -2
  47. package/dist/controllers/idle_controller.js.map +1 -1
  48. package/dist/controllers/listbox_controller.js.map +1 -1
  49. package/dist/controllers/local_time_controller.js +2 -0
  50. package/dist/controllers/local_time_controller.js.map +1 -1
  51. package/dist/controllers/masonry_controller.d.ts +1 -1
  52. package/dist/controllers/masonry_controller.js +1 -1
  53. package/dist/controllers/masonry_controller.js.map +1 -1
  54. package/dist/controllers/meter_controller.js +3 -1
  55. package/dist/controllers/meter_controller.js.map +1 -1
  56. package/dist/controllers/multi_select_controller.js.map +1 -1
  57. package/dist/controllers/network_status_controller.d.ts +15 -13
  58. package/dist/controllers/network_status_controller.js +1 -3
  59. package/dist/controllers/network_status_controller.js.map +1 -1
  60. package/dist/controllers/number_input_controller.d.ts +8 -0
  61. package/dist/controllers/number_input_controller.js +191 -24
  62. package/dist/controllers/number_input_controller.js.map +1 -1
  63. package/dist/controllers/overflow_menu_controller.js +1 -1
  64. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  65. package/dist/controllers/pagination_controller.js +5 -1
  66. package/dist/controllers/pagination_controller.js.map +1 -1
  67. package/dist/controllers/password_strength_controller.d.ts +1 -1
  68. package/dist/controllers/password_strength_controller.js +1 -1
  69. package/dist/controllers/password_strength_controller.js.map +1 -1
  70. package/dist/controllers/pointer_drag_controller.js +10 -0
  71. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  72. package/dist/controllers/portal_controller.js +10 -0
  73. package/dist/controllers/portal_controller.js.map +1 -1
  74. package/dist/controllers/progress_controller.d.ts +1 -1
  75. package/dist/controllers/progress_controller.js +7 -3
  76. package/dist/controllers/progress_controller.js.map +1 -1
  77. package/dist/controllers/range_slider_controller.d.ts +23 -0
  78. package/dist/controllers/range_slider_controller.js +385 -94
  79. package/dist/controllers/range_slider_controller.js.map +1 -1
  80. package/dist/controllers/rating_controller.js +2 -0
  81. package/dist/controllers/rating_controller.js.map +1 -1
  82. package/dist/controllers/relative_time_controller.js +2 -0
  83. package/dist/controllers/relative_time_controller.js.map +1 -1
  84. package/dist/controllers/scroll_area_controller.d.ts +1 -1
  85. package/dist/controllers/scroll_area_controller.js +1 -1
  86. package/dist/controllers/scroll_area_controller.js.map +1 -1
  87. package/dist/controllers/separator_controller.js +13 -17
  88. package/dist/controllers/separator_controller.js.map +1 -1
  89. package/dist/controllers/skeleton_controller.d.ts +2 -2
  90. package/dist/controllers/skeleton_controller.js +71 -3
  91. package/dist/controllers/skeleton_controller.js.map +1 -1
  92. package/dist/controllers/slider_controller.d.ts +17 -1
  93. package/dist/controllers/slider_controller.js +325 -48
  94. package/dist/controllers/slider_controller.js.map +1 -1
  95. package/dist/controllers/spinner_controller.d.ts +15 -10
  96. package/dist/controllers/spinner_controller.js +18 -3
  97. package/dist/controllers/spinner_controller.js.map +1 -1
  98. package/dist/controllers/step_indicator_controller.d.ts +1 -1
  99. package/dist/controllers/step_indicator_controller.js +3 -1
  100. package/dist/controllers/step_indicator_controller.js.map +1 -1
  101. package/dist/controllers/stepper_controller.js +2 -0
  102. package/dist/controllers/stepper_controller.js.map +1 -1
  103. package/dist/controllers/switch_controller.d.ts +12 -8
  104. package/dist/controllers/switch_controller.js +162 -18
  105. package/dist/controllers/switch_controller.js.map +1 -1
  106. package/dist/controllers/textarea_autosize_controller.js +1 -1
  107. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  108. package/dist/controllers/time_picker_controller.d.ts +3 -0
  109. package/dist/controllers/time_picker_controller.js +6 -3
  110. package/dist/controllers/time_picker_controller.js.map +1 -1
  111. package/dist/controllers/tree_view_controller.d.ts +1 -2
  112. package/dist/controllers/tree_view_controller.js +19 -1
  113. package/dist/controllers/tree_view_controller.js.map +1 -1
  114. package/dist/index.js +1214 -307
  115. package/dist/index.js.map +1 -1
  116. package/dist/inspector/cli.d.ts +76 -1
  117. package/dist/inspector/cli.js +152 -0
  118. package/dist/inspector/cli.js.map +1 -1
  119. package/dist/inspector/cli_bin.js +212 -2
  120. package/dist/inspector/cli_bin.js.map +1 -1
  121. package/dist/inspector/examples.json +17 -17
  122. package/dist/inspector/manifest.json +408 -26
  123. package/package.json +3 -3
@@ -23,6 +23,14 @@ interface ControllerManifest {
23
23
  readonly targets: readonly string[];
24
24
  /** Value names (camelCase) declared via `static values`. */
25
25
  readonly values: readonly string[];
26
+ /**
27
+ * Authoring constraints for literal Stimulus Values (stage 1). Reflection can
28
+ * expose a Value's name and decoder, but not semantic bounds such as a
29
+ * strictly positive step, so those contracts stay explicit and reviewable.
30
+ */
31
+ readonly valueConstraints: readonly ValueConstraint[];
32
+ /** Statically decidable relationships between literal Stimulus Values. */
33
+ readonly valueRelations: readonly ValueRelation[];
26
34
  /**
27
35
  * Public action method names declared via `static actions`, wired by
28
36
  * consumers as `data-action="…-><identifier>#<action>"`.
@@ -57,6 +65,13 @@ interface ControllerManifest {
57
65
  * toolbar, radio group, …) are deliberately excluded.
58
66
  */
59
67
  readonly keyboard: readonly KeyboardRequirement[];
68
+ /**
69
+ * Host-element contracts (stage 3): scope or target elements that must avoid
70
+ * native interactive semantics the controller cannot safely compose with.
71
+ * A rule may explicitly admit non-submitting buttons when the controller
72
+ * delegates their native activation to the browser.
73
+ */
74
+ readonly hosts: readonly HostRequirement[];
60
75
  /**
61
76
  * Author-futile attributes (stage 3): ARIA the controller recomputes
62
77
  * wholesale at runtime, where an authored value can neither
@@ -328,6 +343,27 @@ interface KeyboardRequirement {
328
343
  /** Human-readable fix suggestion shown by the CLI (stage 4). */
329
344
  readonly suggestion: string;
330
345
  }
346
+ /**
347
+ * A host-element contract (stage 3): the element carrying a controller or one
348
+ * of its targets must use semantics the controller can own without competing
349
+ * with another native interaction.
350
+ *
351
+ * `"non-interactive"` admits only generic, non-interactive elements.
352
+ * `"non-interactive-or-button"` additionally admits a `<button>` whose
353
+ * literal `type` is listed in {@link buttonTypes}. The explicit type matters:
354
+ * a missing or invalid button type defaults to `submit`, so treating it as an
355
+ * ordinary action button would silently submit an enclosing form.
356
+ */
357
+ interface HostRequirement {
358
+ /** Target name; the empty string `""` means the controller scope element. */
359
+ readonly target: string;
360
+ /** Static host shapes the controller supports. */
361
+ readonly mode: "non-interactive" | "non-interactive-or-button";
362
+ /** Literal button types admitted by `"non-interactive-or-button"`. */
363
+ readonly buttonTypes?: readonly string[];
364
+ /** Human-readable fix suggestion shown by the CLI (stage 4). */
365
+ readonly suggestion: string;
366
+ }
331
367
  /**
332
368
  * An author-futile attribute rule (stage 3): authoring any of {@link attrs} on
333
369
  * the {@link target} element draws a `managed-aria` **warning** — the
@@ -564,6 +600,45 @@ interface ConditionalTargetRule {
564
600
  /** Human-readable fix suggestion shown by the CLI (stage 4). */
565
601
  readonly suggestion: string;
566
602
  }
603
+ /**
604
+ * A statically checkable numeric contract on one declared Stimulus Value.
605
+ *
606
+ * The Inspector decodes literals like Stimulus (`Number(raw.replace(/_/g,
607
+ * ""))`) before applying these bounds. ERB-generated values are undecidable and
608
+ * skipped rather than guessed.
609
+ */
610
+ interface ValueConstraint {
611
+ /** Value name (camelCase, as declared in `static values`). */
612
+ readonly value: string;
613
+ /** Decoder family. Numeric bounds are the first supported semantic family. */
614
+ readonly type: "number";
615
+ /** Reject `NaN` and infinities when true. */
616
+ readonly finite?: boolean;
617
+ /** Require a decoded number strictly greater than this bound. */
618
+ readonly greaterThan?: number;
619
+ /** Human-readable fix suggestion shown by the CLI (stage 4). */
620
+ readonly suggestion: string;
621
+ }
622
+ /**
623
+ * A statically checkable relationship between two numeric Stimulus Values.
624
+ *
625
+ * Missing attributes use the controller's public defaults. If either authored
626
+ * value is dynamic, the Inspector skips the relation rather than guessing.
627
+ */
628
+ interface ValueRelation {
629
+ /** Value on the left side of the comparison (camelCase). */
630
+ readonly left: string;
631
+ /** Supported numeric comparison. */
632
+ readonly operator: "less-than-or-equal";
633
+ /** Value on the right side of the comparison (camelCase). */
634
+ readonly right: string;
635
+ /** Effective left value when its attribute is absent. */
636
+ readonly leftDefault: number;
637
+ /** Effective right value when its attribute is absent. */
638
+ readonly rightDefault: number;
639
+ /** Human-readable fix suggestion shown by the CLI (stage 4). */
640
+ readonly suggestion: string;
641
+ }
567
642
  /** Severity of a diagnostic. Only `error` affects the process exit code. */
568
643
  type DiagnosticSeverity = "error" | "warning";
569
644
  /**
@@ -571,7 +646,7 @@ type DiagnosticSeverity = "error" | "warning";
571
646
  * for testing, machine-readable output, and `data-stimeo-ignore` suppression
572
647
  * lists (which is why the set also exists as a runtime array).
573
648
  */
574
- declare const DIAGNOSTIC_CODES: readonly ["unknown-controller", "unknown-target", "unknown-value", "unknown-action-controller", "unknown-action-method", "orphan-target", "missing-required-target", "missing-conditional-target", "missing-aria", "invalid-aria-value", "keyboard-inaccessible", "unresolved-idref", "managed-aria", "composition-mismatch", "missing-companion", "undeclared-target", "cardinality-violation", "forbidden-aria", "unknown-ignore-code"];
649
+ declare const DIAGNOSTIC_CODES: readonly ["unknown-controller", "unknown-target", "unknown-value", "invalid-value", "unknown-action-controller", "unknown-action-method", "orphan-target", "missing-required-target", "missing-conditional-target", "missing-aria", "invalid-aria-value", "keyboard-inaccessible", "invalid-host", "unresolved-idref", "managed-aria", "composition-mismatch", "missing-companion", "undeclared-target", "cardinality-violation", "forbidden-aria", "unknown-ignore-code"];
575
650
  /** See {@link DIAGNOSTIC_CODES}. */
576
651
  type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[number];
577
652
  /**
@@ -610,6 +610,26 @@ function readEntry(code, start, base) {
610
610
  };
611
611
  }
612
612
  }
613
+ const number = /^[+-]?\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?/.exec(
614
+ code.slice(valueStart)
615
+ )?.[0];
616
+ if (number !== void 0) {
617
+ const end = valueStart + number.length;
618
+ const after = skipTrivia(code, end);
619
+ const whole = after >= code.length || code[after] === "," || code[after] === "}";
620
+ if (whole) {
621
+ return {
622
+ attr: {
623
+ name,
624
+ value: number,
625
+ ...anchor,
626
+ valueStart: base + valueStart,
627
+ valueEnd: base + end
628
+ },
629
+ end
630
+ };
631
+ }
632
+ }
613
633
  return { attr: { name, value: null, ...anchor }, end: skipValue(code, valueStart) };
614
634
  }
615
635
  function readKey(code, start) {
@@ -926,6 +946,7 @@ var DIAGNOSTIC_CODES = [
926
946
  "unknown-controller",
927
947
  "unknown-target",
928
948
  "unknown-value",
949
+ "invalid-value",
929
950
  "unknown-action-controller",
930
951
  "unknown-action-method",
931
952
  "orphan-target",
@@ -934,6 +955,7 @@ var DIAGNOSTIC_CODES = [
934
955
  "missing-aria",
935
956
  "invalid-aria-value",
936
957
  "keyboard-inaccessible",
958
+ "invalid-host",
937
959
  "unresolved-idref",
938
960
  "managed-aria",
939
961
  "composition-mismatch",
@@ -965,6 +987,36 @@ function checkSource(source, manifest) {
965
987
  if (hostAttr && isDynamicValue(hostAttr)) return null;
966
988
  return when.equals.includes(hostAttr ? hostAttr.value.trim() : when.default);
967
989
  };
990
+ const isContentEditable2 = (element) => {
991
+ let current = element;
992
+ while (current && current.tag !== "#root") {
993
+ const attr = current.attrs.find((candidate) => candidate.name === "contenteditable");
994
+ if (attr) {
995
+ if (isDynamicValue(attr)) return null;
996
+ const value = attr.value.trim().toLowerCase();
997
+ if (value === "false") return false;
998
+ if (value === "" || value === "true" || value === "plaintext-only") return true;
999
+ }
1000
+ current = current.parent;
1001
+ }
1002
+ return false;
1003
+ };
1004
+ const hostIsAllowed = (element, rule) => {
1005
+ if (element.tag === "button") {
1006
+ if (rule.mode === "non-interactive") return false;
1007
+ const type = element.attrs.find((attr) => attr.name === "type");
1008
+ if (type && isDynamicValue(type)) return null;
1009
+ const effectiveType = type?.value.trim().toLowerCase() || "submit";
1010
+ return (rule.buttonTypes ?? []).includes(effectiveType);
1011
+ }
1012
+ if (ALWAYS_INTERACTIVE_HOSTS.has(element.tag)) return false;
1013
+ if ((element.tag === "a" || element.tag === "area") && hasAttr(element, "href")) return false;
1014
+ if ((element.tag === "audio" || element.tag === "video") && hasAttr(element, "controls")) {
1015
+ return false;
1016
+ }
1017
+ const editable = isContentEditable2(element);
1018
+ return editable === null ? null : !editable;
1019
+ };
968
1020
  const suppressed = (node, code) => {
969
1021
  if (code === "unknown-ignore-code") return false;
970
1022
  let current = node;
@@ -1072,6 +1124,7 @@ function checkSource(source, manifest) {
1072
1124
  focusableRoleTally.set(role, (focusableRoleTally.get(role) ?? 0) + 1);
1073
1125
  }
1074
1126
  }
1127
+ const authoredValueIdentifiers = /* @__PURE__ */ new Set();
1075
1128
  for (const attr of node.attrs) {
1076
1129
  if (attr.name === IGNORE_ATTR) {
1077
1130
  for (const token of attr.value.split(/\s+/).filter((t) => t.length > 0)) {
@@ -1196,6 +1249,7 @@ function checkSource(source, manifest) {
1196
1249
  attr
1197
1250
  );
1198
1251
  } else {
1252
+ authoredValueIdentifiers.add(parsed.identifier);
1199
1253
  const controller = known[parsed.identifier];
1200
1254
  const valid = controller?.values.some((v) => dasherize(v) === parsed.valueToken) ?? false;
1201
1255
  if (!valid) {
@@ -1207,6 +1261,20 @@ function checkSource(source, manifest) {
1207
1261
  attr,
1208
1262
  didYouMean(parsed.valueToken, (controller?.values ?? []).map(dasherize))
1209
1263
  );
1264
+ } else if (!isDynamicValue(attr)) {
1265
+ const constraint = controller?.valueConstraints.find(
1266
+ (candidate) => dasherize(candidate.value) === parsed.valueToken
1267
+ );
1268
+ if (constraint && !valueSatisfiesConstraint(attr.value, constraint)) {
1269
+ report(
1270
+ node,
1271
+ "invalid-value",
1272
+ "error",
1273
+ `Invalid value "${attr.value.trim()}" for "${parsed.identifier}.${parsed.valueToken}". Expected ${describeValueConstraint(constraint)}.`,
1274
+ attr,
1275
+ constraint.suggestion
1276
+ );
1277
+ }
1210
1278
  }
1211
1279
  }
1212
1280
  continue;
@@ -1241,6 +1309,34 @@ function checkSource(source, manifest) {
1241
1309
  }
1242
1310
  }
1243
1311
  }
1312
+ for (const identifier of authoredValueIdentifiers) {
1313
+ const controller = known[identifier];
1314
+ if (!controller) continue;
1315
+ for (const relation of controller.valueRelations) {
1316
+ const leftAttr = node.attrs.find(
1317
+ (attr) => attr.name === `data-${identifier}-${dasherize(relation.left)}-value`
1318
+ );
1319
+ const rightAttr = node.attrs.find(
1320
+ (attr) => attr.name === `data-${identifier}-${dasherize(relation.right)}-value`
1321
+ );
1322
+ const at = rightAttr ?? leftAttr;
1323
+ if (!at || leftAttr && isDynamicValue(leftAttr) || rightAttr && isDynamicValue(rightAttr)) {
1324
+ continue;
1325
+ }
1326
+ const left = leftAttr ? decodeNumericValue(leftAttr.value) : relation.leftDefault;
1327
+ const right = rightAttr ? decodeNumericValue(rightAttr.value) : relation.rightDefault;
1328
+ if (!Number.isFinite(left) || !Number.isFinite(right)) continue;
1329
+ if (relation.operator === "less-than-or-equal" && left <= right) continue;
1330
+ report(
1331
+ node,
1332
+ "invalid-value",
1333
+ "error",
1334
+ `Invalid values for "${identifier}": "${relation.left}" (${left}) must be less than or equal to "${relation.right}" (${right}).`,
1335
+ at,
1336
+ relation.suggestion
1337
+ );
1338
+ }
1339
+ }
1244
1340
  });
1245
1341
  const scopeIsComplete = (container, scope, identifier) => container.origin === "markup" && !targetsUnreadable(container, scope, identifier);
1246
1342
  for (const { node, identifier } of scopes) {
@@ -1318,6 +1414,28 @@ function checkSource(source, manifest) {
1318
1414
  }
1319
1415
  }
1320
1416
  }
1417
+ for (const { node, identifier } of scopes) {
1418
+ const controller = known[identifier];
1419
+ if (!controller) continue;
1420
+ for (const rule of controller.hosts) {
1421
+ const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
1422
+ const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
1423
+ for (const element of elements) {
1424
+ if (!hasReadableMarkup(element)) continue;
1425
+ if (hostIsAllowed(element, rule) !== false) continue;
1426
+ const type = element.attrs.find((attr) => attr.name === "type");
1427
+ const detail = element.tag === "button" ? `the unsupported ${type ? `type="${type.value}"` : 'implicit type="submit"'}` : `an unsupported interactive <${element.tag}>`;
1428
+ report(
1429
+ element,
1430
+ "invalid-host",
1431
+ "error",
1432
+ `The ${where} of "${identifier}" uses ${detail} host.`,
1433
+ type ?? element,
1434
+ rule.suggestion
1435
+ );
1436
+ }
1437
+ }
1438
+ }
1321
1439
  for (const { node, identifier } of scopes) {
1322
1440
  const controller = known[identifier];
1323
1441
  if (!controller) continue;
@@ -1559,6 +1677,23 @@ function checkSource(source, manifest) {
1559
1677
  diagnostics.sort((a, b) => a.line - b.line || a.column - b.column);
1560
1678
  return diagnostics;
1561
1679
  }
1680
+ function valueSatisfiesConstraint(raw, constraint) {
1681
+ const value = decodeNumericValue(raw);
1682
+ if (Number.isNaN(value)) return false;
1683
+ if (constraint.finite && !Number.isFinite(value)) return false;
1684
+ if (constraint.greaterThan !== void 0 && !(value > constraint.greaterThan)) return false;
1685
+ return true;
1686
+ }
1687
+ function decodeNumericValue(raw) {
1688
+ return Number(raw.replace(/_/g, ""));
1689
+ }
1690
+ function describeValueConstraint(constraint) {
1691
+ const parts = [constraint.finite ? "a finite number" : "a number"];
1692
+ if (constraint.greaterThan !== void 0) {
1693
+ parts.push(`greater than ${constraint.greaterThan}`);
1694
+ }
1695
+ return parts.join(" ");
1696
+ }
1562
1697
  var UNRESOLVED_TARGET_HINT = "Part of this scope is rendered by a helper or a non-literal data: hash, so the target may exist at runtime.";
1563
1698
  var UNRESOLVED_HOST_HINT = "An enclosing helper's data: option is not a literal hash, so the host controller may be declared at runtime.";
1564
1699
  function hasReadableMarkup(el) {
@@ -1631,6 +1766,20 @@ function describeCounted(target, attr, values) {
1631
1766
  return `"${target}" targets with ${attr}${constrained}`;
1632
1767
  }
1633
1768
  var NATIVE_FOCUSABLE_TAGS = /* @__PURE__ */ new Set(["button", "select", "textarea", "summary"]);
1769
+ var ALWAYS_INTERACTIVE_HOSTS = /* @__PURE__ */ new Set([
1770
+ "input",
1771
+ "select",
1772
+ "textarea",
1773
+ "label",
1774
+ "summary",
1775
+ "details",
1776
+ "iframe",
1777
+ "object",
1778
+ "embed"
1779
+ ]);
1780
+ function hasAttr(element, name) {
1781
+ return element.attrs.some((attr) => attr.name === name);
1782
+ }
1634
1783
  function isNativelyFocusable(el) {
1635
1784
  if (NATIVE_FOCUSABLE_TAGS.has(el.tag)) return true;
1636
1785
  if (el.tag === "input") {
@@ -1781,12 +1930,15 @@ function isCheckableFile(path) {
1781
1930
  var CONTROLLER_ARRAY_FIELDS = [
1782
1931
  "targets",
1783
1932
  "values",
1933
+ "valueConstraints",
1934
+ "valueRelations",
1784
1935
  "actions",
1785
1936
  "events",
1786
1937
  "requiredTargets",
1787
1938
  "conditionalTargets",
1788
1939
  "a11y",
1789
1940
  "keyboard",
1941
+ "hosts",
1790
1942
  "managedAria",
1791
1943
  "compositions",
1792
1944
  "companions",