@wcstack/lint 1.31.0 → 1.33.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/cli.cjs CHANGED
@@ -96,6 +96,9 @@ var WcsDiagnosticCode = {
96
96
  // 同名 tag / filter の後勝ち禁止(§5-3)。override:true が無い再定義もこの collision で表す。
97
97
  ManifestTagCollision: "wcs/manifest-tag-collision",
98
98
  ManifestFilterCollision: "wcs/manifest-filter-collision",
99
+ // 同名 state の stateSchema が複数の application artifact に宣言されている(§5-3 の
100
+ // application 版・D8)。勝者なし: その state は未宣言扱い(schema 検証は沈黙)。
101
+ ManifestStateCollision: "wcs/manifest-state-collision",
99
102
  // 明示 override:true(§5-4)。衝突ではなく意図的な shadow の告知(info)。
100
103
  ManifestOverride: "wcs/manifest-override",
101
104
  // --- sidecar vs live declaration drift ---
@@ -118,7 +121,7 @@ var WcsDiagnosticCode = {
118
121
  TokenMisconfigured: "wcs/token-misconfigured",
119
122
  NestedAssign: "wcs/nested-assign",
120
123
  // --- 意味論(構文・存在検査では捕まらない取り違え。service/semanticValidator.ts) ---
121
- // `$getAll` / `$resolve` の添字の本数がパスの `*` の本数と噛み合わない。
124
+ // `$getAll` / `$setAll` / `$resolve` の添字の本数がパスの `*` の本数と噛み合わない。
122
125
  // ランタイムは同じ code で raiseError する(超過は以前は黙って無視されていた)。
123
126
  IndexArity: "wcs/index-arity",
124
127
  // ワイルドカードの階数がスコープの段数を超える(`matrix.*.*` を 1 段の for で読む、
@@ -157,13 +160,22 @@ var WcsDiagnosticCode = {
157
160
  TriggerSeededTruthy: "wcs/trigger-seeded-truthy",
158
161
  // 非 manual <wcs-storage> value バインド先の空値シード(初期書き戻しが保存値を上書き)。
159
162
  StorageSeedClobber: "wcs/storage-seed-clobber",
163
+ // --- accessibility (docs/a11y-design.md §8 / D9) ---
164
+ // `attr.aria-*` バインドの属性名が WAI-ARIA に存在しない(タイポ)。
165
+ // setAttribute はそのまま書き、支援技術は黙って無視する。severity は warning
166
+ // (error 昇格時は packages/lint/scripts/smoke-test.mjs の対ケース更新が必須)。
167
+ AriaAttrUnknown: "wcs/aria-attr-unknown",
160
168
  // --- document-level load configuration ---
161
169
  // @wcstack/state/auto より後に他 wcstack /auto が読まれている。
162
170
  ScriptOrder: "wcs/script-order",
163
171
  // router/auto があるのに <base href> がない(SPA の basename 誤導出)。
164
172
  BaseHrefMissing: "wcs/base-href-missing",
165
173
  // @wcstack/signals と /dom エントリの同一ページ混在(リアクティブコア二重化)。
166
- SignalsDualEntry: "wcs/signals-dual-entry"
174
+ SignalsDualEntry: "wcs/signals-dual-entry",
175
+ // --- deprecations ---
176
+ // 名前付き State(`<wcs-state name>` / `path@name`)。v2 でマウント(`mount=` と接頭辞付きパス)に
177
+ // 置き換わる(docs/state-mount-design.md D16)。1.x では warning、v2 では parse error と同時に error。
178
+ NamedStateDeprecated: "wcs/named-state-deprecated"
167
179
  };
168
180
  function sortDiagnostics(diagnostics) {
169
181
  const severityRank = { error: 0, warning: 1, info: 2 };
@@ -391,12 +403,12 @@ var fix = (options) => {
391
403
  };
392
404
  };
393
405
  var locale = (options) => {
394
- const opt = options?.[0] ?? config.locale;
406
+ const explicit = options?.[0];
395
407
  return (value) => {
396
408
  if (typeof value !== "number") {
397
409
  valueMustBeNumber("locale");
398
410
  }
399
- return value.toLocaleString(opt);
411
+ return value.toLocaleString(explicit ?? config.locale);
400
412
  };
401
413
  };
402
414
  var uc = (_options) => {
@@ -576,30 +588,30 @@ var truncate = (options) => {
576
588
  };
577
589
  };
578
590
  var date = (options) => {
579
- const opt = options?.[0] ?? config.locale;
591
+ const explicit = options?.[0];
580
592
  return (value) => {
581
593
  if (!(value instanceof Date)) {
582
594
  valueMustBeDate("date");
583
595
  }
584
- return value.toLocaleDateString(opt);
596
+ return value.toLocaleDateString(explicit ?? config.locale);
585
597
  };
586
598
  };
587
599
  var time = (options) => {
588
- const opt = options?.[0] ?? config.locale;
600
+ const explicit = options?.[0];
589
601
  return (value) => {
590
602
  if (!(value instanceof Date)) {
591
603
  valueMustBeDate("time");
592
604
  }
593
- return value.toLocaleTimeString(opt);
605
+ return value.toLocaleTimeString(explicit ?? config.locale);
594
606
  };
595
607
  };
596
608
  var datetime = (options) => {
597
- const opt = options?.[0] ?? config.locale;
609
+ const explicit = options?.[0];
598
610
  return (value) => {
599
611
  if (!(value instanceof Date)) {
600
612
  valueMustBeDate("datetime");
601
613
  }
602
- return value.toLocaleString(opt);
614
+ return value.toLocaleString(explicit ?? config.locale);
603
615
  };
604
616
  };
605
617
  var ymd = (options) => {
@@ -904,226 +916,6 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET].map((name) => ({
904
916
  ...STRUCTURAL_DIRECTIVE_INFO[name]
905
917
  }));
906
918
 
907
- // src/language/htmlParse.ts
908
- function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
909
- const blocks = [];
910
- let pos = 0;
911
- const len = html.length;
912
- while (pos < len) {
913
- if (html.startsWith("<!--", pos)) {
914
- const commentEnd = html.indexOf("-->", pos + 4);
915
- if (commentEnd === -1) break;
916
- pos = commentEnd + 3;
917
- continue;
918
- }
919
- const wcsMatch = matchOpenTag(html, pos, stateTagName);
920
- if (wcsMatch === null) {
921
- pos++;
922
- continue;
923
- }
924
- const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
925
- pos = wcsMatch.end;
926
- const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
927
- const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
928
- while (pos < wcsEnd) {
929
- if (html.startsWith("<!--", pos)) {
930
- const commentEnd = html.indexOf("-->", pos + 4);
931
- if (commentEnd === -1) break;
932
- pos = commentEnd + 3;
933
- continue;
934
- }
935
- const scriptMatch = matchOpenTag(html, pos, "script");
936
- if (scriptMatch === null) {
937
- pos++;
938
- continue;
939
- }
940
- const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
941
- if (typeAttr?.toLowerCase() !== "module") {
942
- pos = scriptMatch.end;
943
- continue;
944
- }
945
- const contentStart = scriptMatch.end;
946
- const scriptCloseIdx = findCloseTag(html, contentStart, "script");
947
- if (scriptCloseIdx === -1) {
948
- pos = contentStart;
949
- break;
950
- }
951
- const contentEnd = scriptCloseIdx;
952
- blocks.push({
953
- contentStart,
954
- contentEnd,
955
- content: html.slice(contentStart, contentEnd),
956
- stateName
957
- });
958
- pos = html.indexOf(">", scriptCloseIdx) + 1;
959
- if (pos === 0) break;
960
- }
961
- pos = wcsEnd;
962
- if (wcsCloseIdx !== -1) {
963
- const closeEnd = html.indexOf(">", wcsCloseIdx);
964
- if (closeEnd !== -1) pos = closeEnd + 1;
965
- }
966
- }
967
- return blocks;
968
- }
969
- function parseWcsStateElements(html, stateTagName = "wcs-state") {
970
- const elements = [];
971
- let pos = 0;
972
- const len = html.length;
973
- while (pos < len) {
974
- if (html.startsWith("<!--", pos)) {
975
- const commentEnd = html.indexOf("-->", pos + 4);
976
- if (commentEnd === -1) break;
977
- pos = commentEnd + 3;
978
- continue;
979
- }
980
- const wcsMatch = matchOpenTag(html, pos, stateTagName);
981
- if (wcsMatch === null) {
982
- pos++;
983
- continue;
984
- }
985
- const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
986
- const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
987
- const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
988
- const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
989
- const tagStart = pos;
990
- const tagEnd = wcsMatch.end;
991
- pos = wcsMatch.end;
992
- const scriptBlocks = [];
993
- const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
994
- const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
995
- while (pos < wcsEnd) {
996
- if (html.startsWith("<!--", pos)) {
997
- const commentEnd = html.indexOf("-->", pos + 4);
998
- if (commentEnd === -1) break;
999
- pos = commentEnd + 3;
1000
- continue;
1001
- }
1002
- const scriptMatch = matchOpenTag(html, pos, "script");
1003
- if (scriptMatch === null) {
1004
- pos++;
1005
- continue;
1006
- }
1007
- const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
1008
- if (typeAttr?.toLowerCase() !== "module") {
1009
- pos = scriptMatch.end;
1010
- continue;
1011
- }
1012
- const contentStart = scriptMatch.end;
1013
- const scriptCloseIdx = findCloseTag(html, contentStart, "script");
1014
- if (scriptCloseIdx === -1) {
1015
- pos = contentStart;
1016
- break;
1017
- }
1018
- scriptBlocks.push({
1019
- contentStart,
1020
- contentEnd: scriptCloseIdx,
1021
- content: html.slice(contentStart, scriptCloseIdx),
1022
- stateName
1023
- });
1024
- pos = html.indexOf(">", scriptCloseIdx) + 1;
1025
- if (pos === 0) break;
1026
- }
1027
- elements.push({ stateName, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
1028
- pos = wcsEnd;
1029
- if (wcsCloseIdx !== -1) {
1030
- const closeEnd = html.indexOf(">", wcsCloseIdx);
1031
- if (closeEnd !== -1) pos = closeEnd + 1;
1032
- }
1033
- }
1034
- return elements;
1035
- }
1036
- function findScriptJsonById(html, id2) {
1037
- let pos = 0;
1038
- const len = html.length;
1039
- while (pos < len) {
1040
- if (html.startsWith("<!--", pos)) {
1041
- const commentEnd = html.indexOf("-->", pos + 4);
1042
- if (commentEnd === -1) break;
1043
- pos = commentEnd + 3;
1044
- continue;
1045
- }
1046
- const scriptMatch = matchOpenTag(html, pos, "script");
1047
- if (scriptMatch === null) {
1048
- pos++;
1049
- continue;
1050
- }
1051
- const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
1052
- const idAttr = extractAttribute(scriptMatch.tagContent, "id");
1053
- if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
1054
- const contentStart = scriptMatch.end;
1055
- const scriptCloseIdx = findCloseTag(html, contentStart, "script");
1056
- if (scriptCloseIdx === -1) return null;
1057
- return html.slice(contentStart, scriptCloseIdx);
1058
- }
1059
- pos = scriptMatch.end;
1060
- }
1061
- return null;
1062
- }
1063
- function matchOpenTag(html, pos, tagName) {
1064
- if (html[pos] !== "<") return null;
1065
- const nameStart = pos + 1;
1066
- const nameEnd = nameStart + tagName.length;
1067
- if (nameEnd > html.length) return null;
1068
- const slice3 = html.slice(nameStart, nameEnd);
1069
- if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
1070
- const charAfter = html[nameEnd];
1071
- if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
1072
- return null;
1073
- }
1074
- let i = nameEnd;
1075
- let inSingleQuote = false;
1076
- let inDoubleQuote = false;
1077
- while (i < html.length) {
1078
- const ch = html[i];
1079
- if (inSingleQuote) {
1080
- if (ch === "'") inSingleQuote = false;
1081
- } else if (inDoubleQuote) {
1082
- if (ch === '"') inDoubleQuote = false;
1083
- } else if (ch === "'") {
1084
- inSingleQuote = true;
1085
- } else if (ch === '"') {
1086
- inDoubleQuote = true;
1087
- } else if (ch === ">") {
1088
- return {
1089
- start: pos,
1090
- end: i + 1,
1091
- tagContent: html.slice(nameEnd, i)
1092
- };
1093
- }
1094
- i++;
1095
- }
1096
- return null;
1097
- }
1098
- function findCloseTag(html, startPos, tagName) {
1099
- const pattern = "</" + tagName;
1100
- const patternLower = pattern.toLowerCase();
1101
- const htmlLower = html.toLowerCase();
1102
- let pos = startPos;
1103
- while (pos < html.length) {
1104
- const idx = htmlLower.indexOf(patternLower, pos);
1105
- if (idx === -1) return -1;
1106
- const afterIdx = idx + pattern.length;
1107
- if (afterIdx < html.length) {
1108
- const ch = html[afterIdx];
1109
- if (ch === ">" || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
1110
- return idx;
1111
- }
1112
- }
1113
- pos = idx + 1;
1114
- }
1115
- return -1;
1116
- }
1117
- function extractAttribute(tagContent, attrName) {
1118
- const regex = new RegExp(
1119
- `(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
1120
- "i"
1121
- );
1122
- const match = tagContent.match(regex);
1123
- if (!match) return null;
1124
- return match[1] ?? match[2] ?? match[3] ?? null;
1125
- }
1126
-
1127
919
  // src/service/stateAnalyzer.ts
1128
920
  var RESERVED_STREAMS_KEY = "$streams";
1129
921
  var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
@@ -1580,42 +1372,371 @@ function extractJsDocType(content, propIndex) {
1580
1372
  const typeExpr = jsdocMatch[1].trim();
1581
1373
  return normalizeJsDocType(typeExpr);
1582
1374
  }
1583
- function normalizeJsDocType(typeExpr) {
1584
- const parts = typeExpr.split("|").map((p) => p.trim());
1585
- const normalized = parts.map((p) => {
1586
- const lower = p.toLowerCase();
1587
- if (lower === "string") return "string";
1588
- if (lower === "number") return "number";
1589
- if (lower === "boolean") return "boolean";
1590
- if (lower === "null") return "null";
1591
- if (lower === "undefined") return "null";
1592
- if (lower.endsWith("[]") || lower.startsWith("array")) return "array";
1593
- if (lower === "object") return "object";
1375
+ function normalizeJsDocType(typeExpr) {
1376
+ const parts = typeExpr.split("|").map((p) => p.trim());
1377
+ const normalized = parts.map((p) => {
1378
+ const lower = p.toLowerCase();
1379
+ if (lower === "string") return "string";
1380
+ if (lower === "number") return "number";
1381
+ if (lower === "boolean") return "boolean";
1382
+ if (lower === "null") return "null";
1383
+ if (lower === "undefined") return "null";
1384
+ if (lower.endsWith("[]") || lower.startsWith("array")) return "array";
1385
+ if (lower === "object") return "object";
1386
+ return null;
1387
+ }).filter((p) => p !== null);
1388
+ if (normalized.length === 0) return void 0;
1389
+ const unique = [...new Set(normalized)].sort();
1390
+ return unique.join("|");
1391
+ }
1392
+ function isEscaped(text, i) {
1393
+ let backslashCount = 0;
1394
+ let j = i - 1;
1395
+ while (j >= 0 && text[j] === "\\") {
1396
+ backslashCount++;
1397
+ j--;
1398
+ }
1399
+ return backslashCount % 2 === 1;
1400
+ }
1401
+ function inferTypeHint(valueStart) {
1402
+ const v = valueStart.trim().replace(/,\s*$/, "");
1403
+ if (/^-?\d+\.\d/.test(v)) return "number";
1404
+ if (/^-?\d/.test(v)) return "number";
1405
+ if (/^["'`]/.test(v)) return "string";
1406
+ if (v === "true" || v === "false") return "boolean";
1407
+ if (v === "null") return "null";
1408
+ if (v.startsWith("[")) return "array";
1409
+ if (v.startsWith("{")) return "object";
1410
+ return void 0;
1411
+ }
1412
+ function analyzeSchemaPaths(schema, stateName = "default") {
1413
+ const paths = [];
1414
+ const defs = schema.$defs ?? {};
1415
+ collectSchemaObjectPaths(schema, "", paths, stateName, defs, 0);
1416
+ return paths;
1417
+ }
1418
+ function mergeSchemaCandidates(candidates, applicationStates) {
1419
+ if (applicationStates === void 0 || applicationStates.size === 0) return candidates;
1420
+ const schemaCandidates = [];
1421
+ const schemaKeys = /* @__PURE__ */ new Set();
1422
+ for (const [stateName, schema] of applicationStates) {
1423
+ for (const p of analyzeSchemaPaths(schema, stateName)) {
1424
+ schemaCandidates.push(p);
1425
+ schemaKeys.add(`${stateName} ${p.path}`);
1426
+ }
1427
+ }
1428
+ const kept = candidates.filter((p) => !schemaKeys.has(`${p.stateName} ${p.path}`));
1429
+ return [...kept, ...schemaCandidates];
1430
+ }
1431
+ function derefSchemaNodes(node, defs) {
1432
+ const out = [];
1433
+ const stack = [{ node, chain: /* @__PURE__ */ new Set() }];
1434
+ while (stack.length > 0) {
1435
+ const { node: n, chain } = stack.pop();
1436
+ if (n === null || typeof n !== "object") continue;
1437
+ if (typeof n.$ref === "string") {
1438
+ const match = /^#\/\$defs\/(.+)$/.exec(n.$ref);
1439
+ if (match === null || chain.has(n.$ref)) continue;
1440
+ const target = defs[match[1].replace(/~1/g, "/").replace(/~0/g, "~")];
1441
+ if (target === void 0) continue;
1442
+ stack.push({ node: target, chain: /* @__PURE__ */ new Set([...chain, n.$ref]) });
1443
+ continue;
1444
+ }
1445
+ if (Array.isArray(n.anyOf)) {
1446
+ for (let i = n.anyOf.length - 1; i >= 0; i--) stack.push({ node: n.anyOf[i], chain });
1447
+ continue;
1448
+ }
1449
+ out.push(n);
1450
+ }
1451
+ return out;
1452
+ }
1453
+ function schemaTypeHint(nodes) {
1454
+ const hints = /* @__PURE__ */ new Set();
1455
+ for (const n of nodes) {
1456
+ const types = typeof n.type === "string" ? [n.type] : Array.isArray(n.type) ? n.type : [];
1457
+ if (types.length > 0) {
1458
+ for (const t of types) {
1459
+ if (t === "null") continue;
1460
+ hints.add(t === "integer" ? "number" : t);
1461
+ }
1462
+ continue;
1463
+ }
1464
+ if (Array.isArray(n.enum)) {
1465
+ for (const v of n.enum) {
1466
+ const h = inferJsonTypeHint(v);
1467
+ if (h !== void 0 && h !== "null") hints.add(h);
1468
+ }
1469
+ } else if (n.const !== void 0) {
1470
+ const h = inferJsonTypeHint(n.const);
1471
+ if (h !== void 0 && h !== "null") hints.add(h);
1472
+ } else if (n.properties !== void 0) {
1473
+ hints.add("object");
1474
+ } else if (n.items !== void 0) {
1475
+ hints.add("array");
1476
+ }
1477
+ }
1478
+ return hints.size === 0 ? void 0 : [...hints].join("|");
1479
+ }
1480
+ function collectSchemaObjectPaths(node, prefix, paths, stateName, defs, depth) {
1481
+ if (depth >= MAX_OBJECT_NEST_DEPTH) return;
1482
+ const seen = /* @__PURE__ */ new Set();
1483
+ for (const n of derefSchemaNodes(node, defs)) {
1484
+ for (const [key, child] of Object.entries(n.properties ?? {})) {
1485
+ if (prefix === "" && key.startsWith("$")) continue;
1486
+ if (seen.has(key)) continue;
1487
+ seen.add(key);
1488
+ const path = prefix ? `${prefix}.${key}` : key;
1489
+ pushSchemaValuePaths(path, child, paths, stateName, defs, depth);
1490
+ }
1491
+ }
1492
+ }
1493
+ function pushSchemaValuePaths(path, node, paths, stateName, defs, depth) {
1494
+ const nodes = derefSchemaNodes(node, defs);
1495
+ const typeHint = schemaTypeHint(nodes);
1496
+ paths.push(withHint({ path, kind: "data", stateName, fromSchema: true }, typeHint));
1497
+ const items = nodes.map((n) => n.items).find((i) => i !== void 0 && i !== null && typeof i === "object");
1498
+ const isArray = items !== void 0 || (typeHint?.split("|").includes("array") ?? false);
1499
+ if (isArray) {
1500
+ const itemNodes = items !== void 0 ? derefSchemaNodes(items, defs) : [];
1501
+ paths.push(withHint({ path: `${path}.*`, kind: "list", stateName, fromSchema: true }, schemaTypeHint(itemNodes)));
1502
+ paths.push({ path: `${path}.length`, kind: "data", typeHint: "number", stateName, fromSchema: true });
1503
+ if (depth >= MAX_OBJECT_NEST_DEPTH) return;
1504
+ const seen = /* @__PURE__ */ new Set();
1505
+ for (const n of itemNodes) {
1506
+ for (const [childKey, childNode] of Object.entries(n.properties ?? {})) {
1507
+ if (seen.has(childKey)) continue;
1508
+ seen.add(childKey);
1509
+ pushSchemaValuePaths(`${path}.*.${childKey}`, childNode, paths, stateName, defs, depth + 1);
1510
+ }
1511
+ }
1512
+ return;
1513
+ }
1514
+ if (nodes.some((n) => n.properties !== void 0)) {
1515
+ collectSchemaObjectPaths(node, path, paths, stateName, defs, depth + 1);
1516
+ }
1517
+ }
1518
+ function withHint(candidate, typeHint) {
1519
+ return typeHint === void 0 ? candidate : { ...candidate, typeHint };
1520
+ }
1521
+
1522
+ // src/language/htmlParse.ts
1523
+ function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
1524
+ const blocks = [];
1525
+ let pos = 0;
1526
+ const len = html.length;
1527
+ while (pos < len) {
1528
+ if (html.startsWith("<!--", pos)) {
1529
+ const commentEnd = html.indexOf("-->", pos + 4);
1530
+ if (commentEnd === -1) break;
1531
+ pos = commentEnd + 3;
1532
+ continue;
1533
+ }
1534
+ const wcsMatch = matchOpenTag(html, pos, stateTagName);
1535
+ if (wcsMatch === null) {
1536
+ pos++;
1537
+ continue;
1538
+ }
1539
+ const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
1540
+ pos = wcsMatch.end;
1541
+ const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
1542
+ const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
1543
+ while (pos < wcsEnd) {
1544
+ if (html.startsWith("<!--", pos)) {
1545
+ const commentEnd = html.indexOf("-->", pos + 4);
1546
+ if (commentEnd === -1) break;
1547
+ pos = commentEnd + 3;
1548
+ continue;
1549
+ }
1550
+ const scriptMatch = matchOpenTag(html, pos, "script");
1551
+ if (scriptMatch === null) {
1552
+ pos++;
1553
+ continue;
1554
+ }
1555
+ const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
1556
+ if (typeAttr?.toLowerCase() !== "module") {
1557
+ pos = scriptMatch.end;
1558
+ continue;
1559
+ }
1560
+ const contentStart = scriptMatch.end;
1561
+ const scriptCloseIdx = findCloseTag(html, contentStart, "script");
1562
+ if (scriptCloseIdx === -1) {
1563
+ pos = contentStart;
1564
+ break;
1565
+ }
1566
+ const contentEnd = scriptCloseIdx;
1567
+ blocks.push({
1568
+ contentStart,
1569
+ contentEnd,
1570
+ content: html.slice(contentStart, contentEnd),
1571
+ stateName
1572
+ });
1573
+ pos = html.indexOf(">", scriptCloseIdx) + 1;
1574
+ if (pos === 0) break;
1575
+ }
1576
+ pos = wcsEnd;
1577
+ if (wcsCloseIdx !== -1) {
1578
+ const closeEnd = html.indexOf(">", wcsCloseIdx);
1579
+ if (closeEnd !== -1) pos = closeEnd + 1;
1580
+ }
1581
+ }
1582
+ return blocks;
1583
+ }
1584
+ function parseWcsStateElements(html, stateTagName = "wcs-state") {
1585
+ const elements = [];
1586
+ let pos = 0;
1587
+ const len = html.length;
1588
+ while (pos < len) {
1589
+ if (html.startsWith("<!--", pos)) {
1590
+ const commentEnd = html.indexOf("-->", pos + 4);
1591
+ if (commentEnd === -1) break;
1592
+ pos = commentEnd + 3;
1593
+ continue;
1594
+ }
1595
+ const wcsMatch = matchOpenTag(html, pos, stateTagName);
1596
+ if (wcsMatch === null) {
1597
+ pos++;
1598
+ continue;
1599
+ }
1600
+ const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
1601
+ const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
1602
+ const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
1603
+ const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
1604
+ const tagStart = pos;
1605
+ const tagEnd = wcsMatch.end;
1606
+ pos = wcsMatch.end;
1607
+ const scriptBlocks = [];
1608
+ const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
1609
+ const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
1610
+ while (pos < wcsEnd) {
1611
+ if (html.startsWith("<!--", pos)) {
1612
+ const commentEnd = html.indexOf("-->", pos + 4);
1613
+ if (commentEnd === -1) break;
1614
+ pos = commentEnd + 3;
1615
+ continue;
1616
+ }
1617
+ const scriptMatch = matchOpenTag(html, pos, "script");
1618
+ if (scriptMatch === null) {
1619
+ pos++;
1620
+ continue;
1621
+ }
1622
+ const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
1623
+ if (typeAttr?.toLowerCase() !== "module") {
1624
+ pos = scriptMatch.end;
1625
+ continue;
1626
+ }
1627
+ const contentStart = scriptMatch.end;
1628
+ const scriptCloseIdx = findCloseTag(html, contentStart, "script");
1629
+ if (scriptCloseIdx === -1) {
1630
+ pos = contentStart;
1631
+ break;
1632
+ }
1633
+ scriptBlocks.push({
1634
+ contentStart,
1635
+ contentEnd: scriptCloseIdx,
1636
+ content: html.slice(contentStart, scriptCloseIdx),
1637
+ stateName
1638
+ });
1639
+ pos = html.indexOf(">", scriptCloseIdx) + 1;
1640
+ if (pos === 0) break;
1641
+ }
1642
+ elements.push({ stateName, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
1643
+ pos = wcsEnd;
1644
+ if (wcsCloseIdx !== -1) {
1645
+ const closeEnd = html.indexOf(">", wcsCloseIdx);
1646
+ if (closeEnd !== -1) pos = closeEnd + 1;
1647
+ }
1648
+ }
1649
+ return elements;
1650
+ }
1651
+ function findScriptJsonById(html, id2) {
1652
+ let pos = 0;
1653
+ const len = html.length;
1654
+ while (pos < len) {
1655
+ if (html.startsWith("<!--", pos)) {
1656
+ const commentEnd = html.indexOf("-->", pos + 4);
1657
+ if (commentEnd === -1) break;
1658
+ pos = commentEnd + 3;
1659
+ continue;
1660
+ }
1661
+ const scriptMatch = matchOpenTag(html, pos, "script");
1662
+ if (scriptMatch === null) {
1663
+ pos++;
1664
+ continue;
1665
+ }
1666
+ const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
1667
+ const idAttr = extractAttribute(scriptMatch.tagContent, "id");
1668
+ if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
1669
+ const contentStart = scriptMatch.end;
1670
+ const scriptCloseIdx = findCloseTag(html, contentStart, "script");
1671
+ if (scriptCloseIdx === -1) return null;
1672
+ return html.slice(contentStart, scriptCloseIdx);
1673
+ }
1674
+ pos = scriptMatch.end;
1675
+ }
1676
+ return null;
1677
+ }
1678
+ function matchOpenTag(html, pos, tagName) {
1679
+ if (html[pos] !== "<") return null;
1680
+ const nameStart = pos + 1;
1681
+ const nameEnd = nameStart + tagName.length;
1682
+ if (nameEnd > html.length) return null;
1683
+ const slice3 = html.slice(nameStart, nameEnd);
1684
+ if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
1685
+ const charAfter = html[nameEnd];
1686
+ if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
1594
1687
  return null;
1595
- }).filter((p) => p !== null);
1596
- if (normalized.length === 0) return void 0;
1597
- const unique = [...new Set(normalized)].sort();
1598
- return unique.join("|");
1688
+ }
1689
+ let i = nameEnd;
1690
+ let inSingleQuote = false;
1691
+ let inDoubleQuote = false;
1692
+ while (i < html.length) {
1693
+ const ch = html[i];
1694
+ if (inSingleQuote) {
1695
+ if (ch === "'") inSingleQuote = false;
1696
+ } else if (inDoubleQuote) {
1697
+ if (ch === '"') inDoubleQuote = false;
1698
+ } else if (ch === "'") {
1699
+ inSingleQuote = true;
1700
+ } else if (ch === '"') {
1701
+ inDoubleQuote = true;
1702
+ } else if (ch === ">") {
1703
+ return {
1704
+ start: pos,
1705
+ end: i + 1,
1706
+ tagContent: html.slice(nameEnd, i)
1707
+ };
1708
+ }
1709
+ i++;
1710
+ }
1711
+ return null;
1599
1712
  }
1600
- function isEscaped(text, i) {
1601
- let backslashCount = 0;
1602
- let j = i - 1;
1603
- while (j >= 0 && text[j] === "\\") {
1604
- backslashCount++;
1605
- j--;
1713
+ function findCloseTag(html, startPos, tagName) {
1714
+ const pattern = "</" + tagName;
1715
+ const patternLower = pattern.toLowerCase();
1716
+ const htmlLower = html.toLowerCase();
1717
+ let pos = startPos;
1718
+ while (pos < html.length) {
1719
+ const idx = htmlLower.indexOf(patternLower, pos);
1720
+ if (idx === -1) return -1;
1721
+ const afterIdx = idx + pattern.length;
1722
+ if (afterIdx < html.length) {
1723
+ const ch = html[afterIdx];
1724
+ if (ch === ">" || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
1725
+ return idx;
1726
+ }
1727
+ }
1728
+ pos = idx + 1;
1606
1729
  }
1607
- return backslashCount % 2 === 1;
1730
+ return -1;
1608
1731
  }
1609
- function inferTypeHint(valueStart) {
1610
- const v = valueStart.trim().replace(/,\s*$/, "");
1611
- if (/^-?\d+\.\d/.test(v)) return "number";
1612
- if (/^-?\d/.test(v)) return "number";
1613
- if (/^["'`]/.test(v)) return "string";
1614
- if (v === "true" || v === "false") return "boolean";
1615
- if (v === "null") return "null";
1616
- if (v.startsWith("[")) return "array";
1617
- if (v.startsWith("{")) return "object";
1618
- return void 0;
1732
+ function extractAttribute(tagContent, attrName) {
1733
+ const regex = new RegExp(
1734
+ `(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
1735
+ "i"
1736
+ );
1737
+ const match = tagContent.match(regex);
1738
+ if (!match) return null;
1739
+ return match[1] ?? match[2] ?? match[3] ?? null;
1619
1740
  }
1620
1741
 
1621
1742
  // src/service/statePathResolver.ts
@@ -1807,6 +1928,8 @@ var ja = {
1807
1928
  commandTokenUndeclared: (t) => `\u30B3\u30DE\u30F3\u30C9\u30C8\u30FC\u30AF\u30F3 "${t}" \u306F $commandTokens \u306B\u5BA3\u8A00\u3055\u308C\u3066\u3044\u307E\u305B\u3093`,
1808
1929
  streamPathMissing: (p) => `\u30D1\u30B9 "${p}" \u306F $streams \u5BA3\u8A00\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
1809
1930
  pathMissing: (p) => `\u30D1\u30B9 "${p}" \u306F\u72B6\u614B\u5B9A\u7FA9\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
1931
+ pathNonexistent: (p) => `\u30D1\u30B9 "${p}" \u306F\u5BA3\u8A00\u3055\u308C\u305F stateSchema \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
1932
+ pathTypeMismatch: (p, label, expected, actual) => `\u30D1\u30B9 "${p}" \u306F stateSchema \u4E0A\u3067 ${actual} \u578B\u3067\u3059\u304C\u3001${label} \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059`,
1810
1933
  expansionSuffix: (x) => `\uFF08\u5C55\u958B: ${x}\uFF09`,
1811
1934
  patternPathOutsideFor: (p) => `\u30D1\u30BF\u30FC\u30F3\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
1812
1935
  omittedPathOutsideFor: (p) => `\u7701\u7565\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
@@ -1839,13 +1962,16 @@ var ja = {
1839
1962
  tagCommandUnknown: (name, tag, declared) => `"${name}" \u306F <${tag}> \u306E command \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
1840
1963
  spreadNoBindable: (tag) => `'...'\uFF08spread\uFF09\u306F <${tag}> \u306B\u6709\u52B9\u306A wcBindable \u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059 \u2014 \u3053\u306E\u30BF\u30B0\u306F\u5BA3\u8A00\u3092\u6301\u305F\u306A\u3044\u305F\u3081\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u30A8\u30E9\u30FC\u3092\u9001\u51FA\u3057\u307E\u3059`,
1841
1964
  tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken \u306E\u30AD\u30FC "${name}" \u306F <${tag}> \u306E wcBindable \u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u751F DOM \u30A4\u30D9\u30F3\u30C8\u540D\u306F\u767A\u706B\u3057\u307E\u305B\u3093 \u2014 \u30D7\u30ED\u30D1\u30C6\u30A3\u540D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
1965
+ ariaAttrUnknown: (name) => `"${name}" \u306F WAI-ARIA \u306E\u5C5E\u6027\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002setAttribute \u306F\u305D\u306E\u307E\u307E\u66F8\u304D\u8FBC\u307F\u307E\u3059\u304C\u3001\u652F\u63F4\u6280\u8853\u306B\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059`,
1842
1966
  didYouMean: (c) => `\u3002\u3082\u3057\u304B\u3057\u3066: "${c}"`,
1843
1967
  none: () => `\u306A\u3057`,
1844
1968
  triggerSeededTruthy: (path) => `trigger \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C true \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002trigger \u306F\u30A8\u30C3\u30B8\u691C\u51FA\u306A\u3057\uFF08truthy \u66F8\u304D\u8FBC\u307F\u3067\u5373\u767A\u706B\u30FBmanual \u3082\u30D0\u30A4\u30D1\u30B9\uFF09\u306E\u305F\u3081\u3001\u30D0\u30A4\u30F3\u30C9\u6642\u306B\u5373\u767A\u706B\u3057\u307E\u3059\u3002false \u3067\u30B7\u30FC\u30C9\u3057\u3066\u304F\u3060\u3055\u3044`,
1845
1969
  storageSeedClobber: (path, raw) => `<wcs-storage> \u306E value \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C ${raw} \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u521D\u671F\u66F8\u304D\u623B\u3057\u304C\u4FDD\u5B58\u5024\u3092\u4E0A\u66F8\u304D\u3057\u307E\u3059 \u2014 undefined \u3067\u30B7\u30FC\u30C9\uFF08\`${path}: undefined\`\uFF09\u3059\u308B\u304B manual \u3092\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044`,
1846
1970
  devtoolsAfterState: () => `@wcstack/devtools/auto \u306F @wcstack/state/auto \u3088\u308A\u5148\u306B\u8AAD\u307F\u8FBC\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u5F8C\u3060\u3068\u914D\u7DDA\u53F0\u5E33\u304C\u30E9\u30A4\u30D6\u3067 captured \u3055\u308C\u307E\u305B\u3093\uFF09`,
1847
1971
  baseHrefMissing: () => `@wcstack/router \u3092\u4F7F\u3046 SPA \u306B\u306F <head> \u5185\u306E <base href="/"> \u304C\u5FC5\u8981\u3067\u3059\uFF08\u7121\u3044\u3068\u30C7\u30A3\u30FC\u30D7\u30EA\u30F3\u30AF\u3067 basename \u304C\u8AA4\u5C0E\u51FA\u3055\u308C\u307E\u3059\uFF09`,
1848
- signalsDualEntry: () => `@wcstack/signals \u3068 @wcstack/signals/dom \u304C\u540C\u4E00\u30DA\u30FC\u30B8\u304B\u3089 import \u3055\u308C\u3066\u3044\u307E\u3059\u3002CDN \u3067\u306F\u5404\u30A8\u30F3\u30C8\u30EA\u304C\u81EA\u5DF1\u5B8C\u7D50\u30D0\u30F3\u30C9\u30EB\u306E\u305F\u3081\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u30B3\u30A2\u304C\u4E8C\u91CD\u5316\u3057\u3001\u5883\u754C\u3067\u53CD\u5FDC\u304C\u58CA\u308C\u307E\u3059 \u2014 \u3059\u3079\u3066 /dom \u30A8\u30F3\u30C8\u30EA\u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044`
1972
+ signalsDualEntry: () => `@wcstack/signals \u3068 @wcstack/signals/dom \u304C\u540C\u4E00\u30DA\u30FC\u30B8\u304B\u3089 import \u3055\u308C\u3066\u3044\u307E\u3059\u3002CDN \u3067\u306F\u5404\u30A8\u30F3\u30C8\u30EA\u304C\u81EA\u5DF1\u5B8C\u7D50\u30D0\u30F3\u30C9\u30EB\u306E\u305F\u3081\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u30B3\u30A2\u304C\u4E8C\u91CD\u5316\u3057\u3001\u5883\u754C\u3067\u53CD\u5FDC\u304C\u58CA\u308C\u307E\u3059 \u2014 \u3059\u3079\u3066 /dom \u30A8\u30F3\u30C8\u30EA\u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044`,
1973
+ namedStateAttrDeprecated: (name) => `<wcs-state name="${name}"> \u306F v2 \u3067\u5EC3\u6B62\u3055\u308C\u307E\u3059\u3002\u30EB\u30FC\u30C8\u30C4\u30EA\u30FC\u3078\u306E\u30DE\u30A6\u30F3\u30C8 <wcs-state mount="${name}"> \u306B\u7F6E\u304D\u63DB\u3048\u3001\u30D1\u30B9\u306F "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`,
1974
+ namedStatePathDeprecated: (name) => name === "default" ? `"@default" \u306F\u4E0D\u8981\u3067\u3001v2 \u3067\u5EC3\u6B62\u3055\u308C\u307E\u3059\u3002"@default" \u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09` : `"@${name}" \u306B\u3088\u308B state \u6307\u5B9A\u306F v2 \u3067\u5EC3\u6B62\u3055\u308C\u307E\u3059\u3002\u30DE\u30A6\u30F3\u30C8\u3057\u305F\u30C4\u30EA\u30FC\u3092 "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`
1849
1975
  };
1850
1976
  var EN_EXPECTED_LABEL = {
1851
1977
  array: "an array-typed path",
@@ -1861,6 +1987,8 @@ var en = {
1861
1987
  commandTokenUndeclared: (t) => `Command token "${t}" is not declared in $commandTokens`,
1862
1988
  streamPathMissing: (p) => `Path "${p}" does not exist in the $streams declaration`,
1863
1989
  pathMissing: (p) => `Path "${p}" does not exist in the state definition`,
1990
+ pathNonexistent: (p) => `Path "${p}" does not exist in the declared stateSchema`,
1991
+ pathTypeMismatch: (p, label, expected, actual) => `Path "${p}" is ${actual} in the stateSchema, but ${label} requires ${expected === "array" ? "an array" : expected === "boolean" ? "a boolean" : "a string"}`,
1864
1992
  expansionSuffix: (x) => ` (expanded: ${x})`,
1865
1993
  patternPathOutsideFor: (p) => `Pattern path "${p}" cannot be used outside a <template for>`,
1866
1994
  omittedPathOutsideFor: (p) => `Shorthand path "${p}" cannot be used outside a <template for>`,
@@ -1893,25 +2021,220 @@ var en = {
1893
2021
  tagCommandUnknown: (name, tag, declared) => `"${name}" is not a command of <${tag}> (declared: ${declared})`,
1894
2022
  spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
1895
2023
  tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken key "${name}" is not a wcBindable property of <${tag}>. Raw DOM event names never fire \u2014 use the property name (declared: ${declared})`,
2024
+ ariaAttrUnknown: (name) => `"${name}" is not a WAI-ARIA attribute. setAttribute writes it anyway, and assistive technology silently ignores it`,
1896
2025
  didYouMean: (c) => `. Did you mean "${c}"?`,
1897
2026
  none: () => `none`,
1898
2027
  triggerSeededTruthy: (path) => `The trigger-bound slot "${path}" is seeded with true. trigger has no edge detection (any truthy write fires, and it bypasses manual), so it fires immediately at bind. Seed it with false`,
1899
2028
  storageSeedClobber: (path, raw) => `The <wcs-storage> value-bound slot "${path}" is seeded with ${raw}. The initial write-back overwrites the persisted value \u2014 seed it with undefined (\`${path}: undefined\`) or add manual`,
1900
2029
  devtoolsAfterState: () => `Load @wcstack/devtools/auto BEFORE @wcstack/state/auto (otherwise the wiring ledger is not captured live)`,
1901
2030
  baseHrefMissing: () => `An SPA using @wcstack/router needs <base href="/"> in <head> (without it, deep links misderive the basename)`,
1902
- signalsDualEntry: () => `Both @wcstack/signals and @wcstack/signals/dom are imported on this page. On a CDN each entry is a self-contained bundle, so the reactive core is duplicated and reactivity breaks at the seam \u2014 import everything from the single /dom entry`
2031
+ signalsDualEntry: () => `Both @wcstack/signals and @wcstack/signals/dom are imported on this page. On a CDN each entry is a self-contained bundle, so the reactive core is duplicated and reactivity breaks at the seam \u2014 import everything from the single /dom entry`,
2032
+ namedStateAttrDeprecated: (name) => `<wcs-state name="${name}"> is deprecated and will be removed in v2. Mount the state onto the root tree with <wcs-state mount="${name}"> and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
2033
+ namedStatePathDeprecated: (name) => name === "default" ? `The "@default" selector is redundant and will be removed in v2; drop it (docs/state-mount-design.md \xA79)` : `The "@${name}" state selector is deprecated and will be removed in v2. Read the mounted tree as "${name}.<path>" instead (docs/state-mount-design.md \xA79)`
1903
2034
  };
1904
2035
  var CATALOGS = { ja, en };
1905
2036
  function getMessages(locale3) {
1906
2037
  return CATALOGS[resolveLocale(locale3)];
1907
2038
  }
1908
2039
 
2040
+ // src/core/sidecar/schemaSubset.ts
2041
+ var ALLOWED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
2042
+ "type",
2043
+ "properties",
2044
+ "required",
2045
+ "items",
2046
+ "enum",
2047
+ "const",
2048
+ "anyOf",
2049
+ "$defs",
2050
+ "$ref"
2051
+ ]);
2052
+ var DiagnosticContext = class {
2053
+ constructor(spans) {
2054
+ this.spans = spans;
2055
+ }
2056
+ diagnostics = [];
2057
+ add(code, pointer2, message, severity, extra = {}, useKeySpan = false) {
2058
+ const span = this.spans.get(pointer2);
2059
+ const start = span === void 0 ? 0 : useKeySpan ? span.keyStart ?? span.start : span.start;
2060
+ const end = span === void 0 ? 0 : useKeySpan ? span.keyEnd ?? span.end : span.end;
2061
+ this.diagnostics.push({ code, start, end, message, severity, ...extra });
2062
+ }
2063
+ };
2064
+ function isSchemaObject(value) {
2065
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2066
+ }
2067
+ function isSchemaMap(value) {
2068
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2069
+ }
2070
+ function validateSchemaSubset(schema, pointerBase, ctx, rootDefs) {
2071
+ walkKeywords(schema, pointerBase, ctx, rootDefs);
2072
+ const safe = /* @__PURE__ */ new Set();
2073
+ detectCycles(schema, pointerBase, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
2074
+ for (const [name, def] of Object.entries(rootDefs)) {
2075
+ detectCycles(def, `${pointerBase}/$defs/${escape(name)}`, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
2076
+ }
2077
+ }
2078
+ function walkKeywords(node, ptr, ctx, rootDefs) {
2079
+ if (!isSchemaObject(node)) return;
2080
+ for (const keyword of Object.keys(node)) {
2081
+ if (!ALLOWED_SCHEMA_KEYWORDS.has(keyword)) {
2082
+ ctx.add(
2083
+ WcsDiagnosticCode.ManifestUnknownKeyword,
2084
+ `${ptr}/${escape(keyword)}`,
2085
+ `Unsupported schema keyword "${keyword}". Allowed: ${[...ALLOWED_SCHEMA_KEYWORDS].join(", ")}.`,
2086
+ "warning",
2087
+ {},
2088
+ true
2089
+ );
2090
+ }
2091
+ }
2092
+ if (typeof node.$ref === "string") {
2093
+ if (!node.$ref.startsWith("#/")) {
2094
+ ctx.add(
2095
+ WcsDiagnosticCode.ManifestExternalRef,
2096
+ `${ptr}/$ref`,
2097
+ `External $ref "${node.$ref}" is forbidden; only local "#/$defs/..." references are allowed.`,
2098
+ "error"
2099
+ );
2100
+ } else if (resolveLocalRef(node.$ref, rootDefs) === void 0) {
2101
+ ctx.add(
2102
+ WcsDiagnosticCode.ManifestRefUnresolved,
2103
+ `${ptr}/$ref`,
2104
+ `Unresolved local $ref "${node.$ref}".`,
2105
+ "error"
2106
+ );
2107
+ }
2108
+ }
2109
+ if (isSchemaMap(node.properties)) {
2110
+ for (const [name, child] of Object.entries(node.properties)) {
2111
+ walkKeywords(child, `${ptr}/properties/${escape(name)}`, ctx, rootDefs);
2112
+ }
2113
+ }
2114
+ if (node.items !== void 0 && isSchemaObject(node.items)) {
2115
+ walkKeywords(node.items, `${ptr}/items`, ctx, rootDefs);
2116
+ }
2117
+ if (Array.isArray(node.anyOf)) {
2118
+ node.anyOf.forEach((child, i) => walkKeywords(child, `${ptr}/anyOf/${i}`, ctx, rootDefs));
2119
+ }
2120
+ if (isSchemaMap(node.$defs)) {
2121
+ for (const [name, child] of Object.entries(node.$defs)) {
2122
+ walkKeywords(child, `${ptr}/$defs/${escape(name)}`, ctx, rootDefs);
2123
+ }
2124
+ }
2125
+ }
2126
+ function detectCycles(node, ptr, ctx, rootDefs, refStack, safe) {
2127
+ if (!isSchemaObject(node)) return;
2128
+ if (typeof node.$ref === "string") {
2129
+ const ref = node.$ref;
2130
+ if (!ref.startsWith("#/")) return;
2131
+ if (refStack.has(ref)) {
2132
+ ctx.add(WcsDiagnosticCode.ManifestRefCycle, `${ptr}/$ref`, `Cyclic $ref detected at "${ref}".`, "error");
2133
+ return;
2134
+ }
2135
+ if (safe.has(ref)) return;
2136
+ const target = resolveLocalRef(ref, rootDefs);
2137
+ if (target === void 0) return;
2138
+ refStack.add(ref);
2139
+ detectCycles(target, ptr, ctx, rootDefs, refStack, safe);
2140
+ refStack.delete(ref);
2141
+ safe.add(ref);
2142
+ return;
2143
+ }
2144
+ if (isSchemaMap(node.properties)) {
2145
+ for (const child of Object.values(node.properties)) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
2146
+ }
2147
+ if (node.items !== void 0 && isSchemaObject(node.items)) {
2148
+ detectCycles(node.items, ptr, ctx, rootDefs, refStack, safe);
2149
+ }
2150
+ if (Array.isArray(node.anyOf)) {
2151
+ for (const child of node.anyOf) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
2152
+ }
2153
+ }
2154
+ function resolveLocalRef(ref, rootDefs) {
2155
+ const match = /^#\/\$defs\/(.+)$/.exec(ref);
2156
+ if (match === null) return void 0;
2157
+ const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
2158
+ return rootDefs[name];
2159
+ }
2160
+ function resolveSchemaPath(root, rootDefs, segments) {
2161
+ let current = root;
2162
+ for (let depth = 0; depth < segments.length; depth++) {
2163
+ const segment = segments[depth];
2164
+ const resolved = derefUnion(current, rootDefs);
2165
+ if (resolved.kind === "ref-error") return resolved;
2166
+ const candidates = resolved.nodes;
2167
+ if (segment === "*") {
2168
+ const items = firstDefined(candidates, (n) => isSchemaObject(n.items) ? n.items : void 0);
2169
+ if (items === void 0) {
2170
+ return { kind: "unknown" };
2171
+ }
2172
+ current = items;
2173
+ continue;
2174
+ }
2175
+ if (segment === "length" && candidates.some((n) => hasType(n, "array"))) {
2176
+ current = { type: "number" };
2177
+ continue;
2178
+ }
2179
+ const child = firstDefined(candidates, (n) => isSchemaMap(n.properties) ? n.properties[segment] : void 0);
2180
+ if (child !== void 0) {
2181
+ current = child;
2182
+ continue;
2183
+ }
2184
+ const anyObject = candidates.some((n) => hasType(n, "object") || isSchemaMap(n.properties));
2185
+ if (anyObject) {
2186
+ return { kind: "nonexistent", segment, depth };
2187
+ }
2188
+ return { kind: "unknown" };
2189
+ }
2190
+ const final = derefUnion(current, rootDefs);
2191
+ if (final.kind === "ref-error") return final;
2192
+ return { kind: "resolved", schema: final.nodes.length === 1 ? final.nodes[0] : current };
2193
+ }
2194
+ function derefUnion(node, rootDefs) {
2195
+ const out = [];
2196
+ const stack = [{ node, chain: /* @__PURE__ */ new Set() }];
2197
+ while (stack.length > 0) {
2198
+ const { node: n, chain } = stack.pop();
2199
+ if (typeof n.$ref === "string") {
2200
+ if (!n.$ref.startsWith("#/") || chain.has(n.$ref)) {
2201
+ return { kind: "ref-error", ref: n.$ref };
2202
+ }
2203
+ const target = resolveLocalRef(n.$ref, rootDefs);
2204
+ if (target === void 0) return { kind: "ref-error", ref: n.$ref };
2205
+ stack.push({ node: target, chain: /* @__PURE__ */ new Set([...chain, n.$ref]) });
2206
+ continue;
2207
+ }
2208
+ if (Array.isArray(n.anyOf)) {
2209
+ for (const branch of n.anyOf) stack.push({ node: branch, chain });
2210
+ continue;
2211
+ }
2212
+ out.push(n);
2213
+ }
2214
+ return { kind: "ok", nodes: out };
2215
+ }
2216
+ function firstDefined(nodes, pick) {
2217
+ for (const n of nodes) {
2218
+ const v = pick(n);
2219
+ if (v !== void 0) return v;
2220
+ }
2221
+ return void 0;
2222
+ }
2223
+ function hasType(node, t) {
2224
+ const type = node.type;
2225
+ if (type === void 0) return false;
2226
+ return Array.isArray(type) ? type.includes(t) : type === t;
2227
+ }
2228
+ function escape(key) {
2229
+ return key.replace(/~/g, "~0").replace(/\//g, "~1");
2230
+ }
2231
+
1909
2232
  // src/service/bindingValidator.ts
1910
2233
  var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
1911
- function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader) {
2234
+ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader, applicationStates) {
1912
2235
  const diagnostics = [];
1913
2236
  const msgs = getMessages(locale3);
1914
- const statePaths = getStatePathsFromHtml(html, stateTagName, fileReader);
2237
+ const statePaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationStates);
1915
2238
  const pathsByState = /* @__PURE__ */ new Map();
1916
2239
  for (const p of statePaths) {
1917
2240
  const list = pathsByState.get(p.stateName) ?? [];
@@ -2034,16 +2357,17 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
2034
2357
  }
2035
2358
  }
2036
2359
  if (checkPath) {
2037
- const message = validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs);
2038
- if (message) {
2360
+ const schema = applicationStates?.get(parsed.targetState);
2361
+ const verdict = schema !== void 0 ? validateSchemaPathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, schema, msgs) : toMissingVerdict(validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs));
2362
+ if (verdict) {
2039
2363
  const pathOffset = binding.indexOf(parsed.path);
2040
2364
  const pathStart = bindingStart + pathOffset;
2041
2365
  diagnostics.push({
2042
- code: WcsDiagnosticCode.BindingPathMissing,
2366
+ code: verdict.code,
2043
2367
  start: pathStart,
2044
2368
  end: pathStart + pathTrimmed.length,
2045
- message: `${message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
2046
- severity: "warning"
2369
+ message: `${verdict.message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
2370
+ severity: verdict.severity
2047
2371
  });
2048
2372
  }
2049
2373
  }
@@ -2162,12 +2486,13 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
2162
2486
  if (typeReq && resultType !== typeReq.expected) {
2163
2487
  const pathOffset = binding.indexOf(parsed.path);
2164
2488
  const pathStart = bindingStart + pathOffset;
2489
+ const schemaDefinite = typeReq.expected === "array" && parsed.filters.length === 0 && applicationStates?.has(parsed.targetState) === true && scopedPaths.some((p) => p.path === pathTrimmed && p.fromSchema === true);
2165
2490
  diagnostics.push({
2166
- code: WcsDiagnosticCode.BindingTypeExpectation,
2491
+ code: schemaDefinite ? WcsDiagnosticCode.PathTypeMismatch : WcsDiagnosticCode.BindingTypeExpectation,
2167
2492
  start: pathStart,
2168
2493
  end: pathStart + pathTrimmed.length,
2169
- message: msgs.typeExpectation(typeReq.label, typeReq.expected, resultType),
2170
- severity: typeReq.severity
2494
+ message: schemaDefinite ? msgs.pathTypeMismatch(pathTrimmed, typeReq.label, typeReq.expected, resultType) : msgs.typeExpectation(typeReq.label, typeReq.expected, resultType),
2495
+ severity: schemaDefinite ? "error" : typeReq.severity
2171
2496
  });
2172
2497
  }
2173
2498
  }
@@ -2338,6 +2663,20 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
2338
2663
  }
2339
2664
  return null;
2340
2665
  }
2666
+ function toMissingVerdict(message) {
2667
+ return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
2668
+ }
2669
+ function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, schema, msgs) {
2670
+ if (checkPath.startsWith("$")) {
2671
+ return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
2672
+ }
2673
+ if (scopedPathSet.has(checkPath)) return null;
2674
+ const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
2675
+ if (resolution.kind === "nonexistent") {
2676
+ return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
2677
+ }
2678
+ return null;
2679
+ }
2341
2680
  function collectStructuralTemplates(html, attrName) {
2342
2681
  const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2343
2682
  const attrRegex = new RegExp(`${escaped}\\s*=\\s*(["'])`, "i");
@@ -2739,10 +3078,19 @@ function isInsideTag(html, offset, tagName) {
2739
3078
  }
2740
3079
 
2741
3080
  // src/service/templateSyntaxValidator.ts
2742
- function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader) {
3081
+ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader, applicationStates) {
2743
3082
  const diagnostics = [];
2744
3083
  const msgs = getMessages(locale3);
2745
- const allPaths = getStatePathsFromHtml(html, stateTagName, fileReader);
3084
+ const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationStates);
3085
+ const defaultSchema = applicationStates?.get("default");
3086
+ const missingVerdict = (path, displayPath, pathSet2, scoped) => {
3087
+ if (isValidTemplatePath(path, pathSet2, scoped)) return null;
3088
+ if (defaultSchema !== void 0 && !path.startsWith("$")) {
3089
+ const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
3090
+ return resolution.kind === "nonexistent" ? { code: WcsDiagnosticCode.PathNonexistent, severity: "error", message: msgs.pathNonexistent(displayPath) } : null;
3091
+ }
3092
+ return { code: WcsDiagnosticCode.BindingPathMissing, severity: "warning", message: msgs.pathMissing(displayPath) };
3093
+ };
2746
3094
  if (allPaths.length === 0) return diagnostics;
2747
3095
  const defaultPaths = allPaths.filter((p) => p.stateName === "default");
2748
3096
  const pathSet = new Set(defaultPaths.map((p) => p.path));
@@ -2822,24 +3170,28 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
2822
3170
  const forPath = insideFor ? getInnermostForPath(html, item.matchStart, bindAttrName) : null;
2823
3171
  if (forPath && !forPath.startsWith(".")) {
2824
3172
  const expandedPath = pathPart === "." ? `${forPath}.*` : `${forPath}.*.${pathPart.slice(1)}`;
2825
- if (!isValidTemplatePath(expandedPath, pathSet, defaultPaths)) {
3173
+ const verdict = missingVerdict(expandedPath, pathPart, pathSet, defaultPaths);
3174
+ if (verdict) {
2826
3175
  diagnostics.push({
2827
- code: WcsDiagnosticCode.BindingPathMissing,
3176
+ code: verdict.code,
2828
3177
  start: item.exprStart,
2829
3178
  end: item.exprStart + pathPart.length,
2830
- message: msgs.pathMissing(pathPart) + msgs.expansionSuffix(expandedPath),
2831
- severity: "warning"
3179
+ message: verdict.message + msgs.expansionSuffix(expandedPath),
3180
+ severity: verdict.severity
2832
3181
  });
2833
3182
  }
2834
3183
  }
2835
- } else if (!isValidTemplatePath(pathPart, pathSet, defaultPaths)) {
2836
- diagnostics.push({
2837
- code: WcsDiagnosticCode.BindingPathMissing,
2838
- start: item.exprStart,
2839
- end: item.exprStart + pathPart.length,
2840
- message: msgs.pathMissing(pathPart),
2841
- severity: "warning"
2842
- });
3184
+ } else {
3185
+ const verdict = missingVerdict(pathPart, pathPart, pathSet, defaultPaths);
3186
+ if (verdict) {
3187
+ diagnostics.push({
3188
+ code: verdict.code,
3189
+ start: item.exprStart,
3190
+ end: item.exprStart + pathPart.length,
3191
+ message: verdict.message,
3192
+ severity: verdict.severity
3193
+ });
3194
+ }
2843
3195
  }
2844
3196
  }
2845
3197
  for (let i = 1; i < parts.length; i++) {
@@ -3725,11 +4077,14 @@ var BUILTIN_TAGS = {
3725
4077
  "wcs-raf": {
3726
4078
  "package": "raf",
3727
4079
  "hasWcBindable": true,
3728
- "observedAttributes": [],
4080
+ "observedAttributes": [
4081
+ "reduced-motion"
4082
+ ],
3729
4083
  "inputs": {
3730
4084
  "once": "once",
3731
4085
  "repeat": "repeat",
3732
4086
  "manual": "manual",
4087
+ "reducedMotion": "reduced-motion",
3733
4088
  "trigger": null
3734
4089
  },
3735
4090
  "properties": [
@@ -4006,6 +4361,35 @@ var BUILTIN_TAGS = {
4006
4361
  "abort"
4007
4362
  ]
4008
4363
  },
4364
+ "wcs-view-transition": {
4365
+ "package": "view-transition",
4366
+ "hasWcBindable": true,
4367
+ "observedAttributes": [
4368
+ "mode",
4369
+ "naming",
4370
+ "naming-limit",
4371
+ "reduced-motion",
4372
+ "types",
4373
+ "disabled",
4374
+ "for"
4375
+ ],
4376
+ "inputs": {
4377
+ "disabled": "disabled",
4378
+ "mode": "mode",
4379
+ "naming": "naming",
4380
+ "namingLimit": "naming-limit",
4381
+ "reducedMotion": "reduced-motion",
4382
+ "types": "types",
4383
+ "participants": "for"
4384
+ },
4385
+ "properties": [
4386
+ "active",
4387
+ "error"
4388
+ ],
4389
+ "commands": [
4390
+ "skip"
4391
+ ]
4392
+ },
4009
4393
  "wcs-wakelock": {
4010
4394
  "package": "wakelock",
4011
4395
  "hasWcBindable": true,
@@ -4318,6 +4702,98 @@ function hasBooleanAttribute(attrsText, attrName) {
4318
4702
  return new RegExp(`(?:^|\\s)${attrName}(?:\\s|=|$)`, "i").test(attrsText);
4319
4703
  }
4320
4704
 
4705
+ // src/service/ariaValidator.ts
4706
+ var ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
4707
+ // widget attributes
4708
+ "aria-autocomplete",
4709
+ "aria-checked",
4710
+ "aria-disabled",
4711
+ "aria-errormessage",
4712
+ "aria-expanded",
4713
+ "aria-haspopup",
4714
+ "aria-hidden",
4715
+ "aria-invalid",
4716
+ "aria-label",
4717
+ "aria-level",
4718
+ "aria-modal",
4719
+ "aria-multiline",
4720
+ "aria-multiselectable",
4721
+ "aria-orientation",
4722
+ "aria-placeholder",
4723
+ "aria-pressed",
4724
+ "aria-readonly",
4725
+ "aria-required",
4726
+ "aria-selected",
4727
+ "aria-sort",
4728
+ "aria-valuemax",
4729
+ "aria-valuemin",
4730
+ "aria-valuenow",
4731
+ "aria-valuetext",
4732
+ // live region attributes
4733
+ "aria-busy",
4734
+ "aria-live",
4735
+ "aria-relevant",
4736
+ "aria-atomic",
4737
+ // drag-and-drop (deprecated in 1.1, still valid names)
4738
+ "aria-dropeffect",
4739
+ "aria-grabbed",
4740
+ // relationship attributes
4741
+ "aria-activedescendant",
4742
+ "aria-colcount",
4743
+ "aria-colindex",
4744
+ "aria-colindextext",
4745
+ "aria-colspan",
4746
+ "aria-controls",
4747
+ "aria-describedby",
4748
+ "aria-details",
4749
+ "aria-flowto",
4750
+ "aria-labelledby",
4751
+ "aria-owns",
4752
+ "aria-posinset",
4753
+ "aria-rowcount",
4754
+ "aria-rowindex",
4755
+ "aria-rowindextext",
4756
+ "aria-rowspan",
4757
+ "aria-setsize",
4758
+ // global additions
4759
+ "aria-current",
4760
+ "aria-keyshortcuts",
4761
+ "aria-roledescription",
4762
+ // 1.3 additions with broad implementation
4763
+ "aria-braillelabel",
4764
+ "aria-brailleroledescription",
4765
+ "aria-description"
4766
+ ]);
4767
+ function validateAriaAttributes(html, bindAttribute = "data-wcs", locale3) {
4768
+ const diagnostics = [];
4769
+ const msgs = getMessages(locale3);
4770
+ for (const attr of findAllBindAttributes(html, bindAttribute)) {
4771
+ let exprOffset = 0;
4772
+ for (const expr of splitBindingExpressions(attr.value)) {
4773
+ const exprStart = attr.valueStart + exprOffset;
4774
+ exprOffset += expr.length + 1;
4775
+ const property = parseBindingExpression(expr).property;
4776
+ if (!property) continue;
4777
+ const bare = property.split("#")[0];
4778
+ if (!bare.toLowerCase().startsWith("attr.aria-")) continue;
4779
+ const ariaName = bare.slice("attr.".length).toLowerCase();
4780
+ if (ARIA_ATTRIBUTES.has(ariaName)) continue;
4781
+ const propIndex = expr.indexOf(property);
4782
+ const start = propIndex === -1 ? exprStart : exprStart + propIndex;
4783
+ const end = propIndex === -1 ? exprStart + expr.length : start + property.length;
4784
+ diagnostics.push({
4785
+ code: WcsDiagnosticCode.AriaAttrUnknown,
4786
+ start,
4787
+ end,
4788
+ severity: "warning",
4789
+ member: ariaName,
4790
+ message: msgs.ariaAttrUnknown(ariaName) + suggestion(ariaName, [...ARIA_ATTRIBUTES], msgs)
4791
+ });
4792
+ }
4793
+ }
4794
+ return diagnostics;
4795
+ }
4796
+
4321
4797
  // src/service/documentEnvValidator.ts
4322
4798
  function validateDocumentEnv(html, locale3) {
4323
4799
  const diagnostics = [];
@@ -4489,6 +4965,78 @@ function validateEntry(entry, pathSet, msgs) {
4489
4965
  return null;
4490
4966
  }
4491
4967
 
4968
+ // src/service/namedStateValidator.ts
4969
+ function findStateSelector(expr, embedded = false) {
4970
+ const colon = embedded ? -1 : expr.indexOf(":");
4971
+ const from = colon + 1;
4972
+ let depth = 0;
4973
+ let end = expr.length;
4974
+ for (let i = from; i < expr.length; i++) {
4975
+ const ch = expr[i];
4976
+ if (ch === "(") depth++;
4977
+ else if (ch === ")") depth = Math.max(0, depth - 1);
4978
+ else if (ch === "|" && depth === 0) {
4979
+ end = i;
4980
+ break;
4981
+ }
4982
+ }
4983
+ const at = expr.indexOf("@", from);
4984
+ if (at === -1 || at >= end) return null;
4985
+ const raw = expr.slice(at + 1, end);
4986
+ const name = raw.trim();
4987
+ const nameStart = at + 1 + (raw.length - raw.trimStart().length);
4988
+ return { start: at, end: name.length === 0 ? at + 1 : nameStart + name.length, name: name.length === 0 ? "default" : name };
4989
+ }
4990
+ function validateNamedState(html, attrName, stateTagName = "wcs-state", locale3) {
4991
+ const msgs = getMessages(locale3);
4992
+ const diagnostics = [];
4993
+ for (const element of parseWcsStateElements(html, stateTagName)) {
4994
+ const tagText = html.slice(element.tagStart, element.tagEnd);
4995
+ if (/\sbind-component(?=[\s=>/])/i.test(tagText)) continue;
4996
+ const match = /(?:^|\s)name\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tagText);
4997
+ if (match === null) continue;
4998
+ const value = match[1] ?? match[2] ?? match[3] ?? "";
4999
+ const quoted = match[1] !== void 0 || match[2] !== void 0;
5000
+ const valueEnd = element.tagStart + match.index + match[0].length - (quoted ? 1 : 0);
5001
+ diagnostics.push({
5002
+ code: WcsDiagnosticCode.NamedStateDeprecated,
5003
+ start: valueEnd - value.length,
5004
+ end: valueEnd,
5005
+ message: msgs.namedStateAttrDeprecated(value),
5006
+ severity: "warning"
5007
+ });
5008
+ }
5009
+ for (const attr of findAllBindAttributes(html, attrName)) {
5010
+ let pos = 0;
5011
+ for (const expr of splitBindingExpressions(attr.value)) {
5012
+ const selector = findStateSelector(expr);
5013
+ if (selector !== null) {
5014
+ diagnostics.push({
5015
+ code: WcsDiagnosticCode.NamedStateDeprecated,
5016
+ start: attr.valueStart + pos + selector.start,
5017
+ end: attr.valueStart + pos + selector.end,
5018
+ message: msgs.namedStatePathDeprecated(selector.name),
5019
+ severity: "warning"
5020
+ });
5021
+ }
5022
+ pos += expr.length + 1;
5023
+ }
5024
+ }
5025
+ for (const mustache of findAllMustacheSyntax(html)) {
5026
+ const selector = findStateSelector(mustache.expression, true);
5027
+ if (selector !== null) {
5028
+ diagnostics.push({
5029
+ code: WcsDiagnosticCode.NamedStateDeprecated,
5030
+ start: mustache.exprStart + selector.start,
5031
+ end: mustache.exprStart + selector.end,
5032
+ message: msgs.namedStatePathDeprecated(selector.name),
5033
+ severity: "warning"
5034
+ });
5035
+ }
5036
+ }
5037
+ return diagnostics;
5038
+ }
5039
+
4492
5040
  // ../state/dist/parser.esm.js
4493
5041
  var DELIMITER2 = ".";
4494
5042
  var WILDCARD2 = "*";
@@ -4863,12 +5411,12 @@ var fix2 = (options) => {
4863
5411
  };
4864
5412
  };
4865
5413
  var locale2 = (options) => {
4866
- const opt = options?.[0] ?? config2.locale;
5414
+ const explicit = options?.[0];
4867
5415
  return (value) => {
4868
5416
  if (typeof value !== "number") {
4869
5417
  valueMustBeNumber2("locale");
4870
5418
  }
4871
- return value.toLocaleString(opt);
5419
+ return value.toLocaleString(explicit ?? config2.locale);
4872
5420
  };
4873
5421
  };
4874
5422
  var uc2 = (_options) => {
@@ -5048,30 +5596,30 @@ var truncate2 = (options) => {
5048
5596
  };
5049
5597
  };
5050
5598
  var date2 = (options) => {
5051
- const opt = options?.[0] ?? config2.locale;
5599
+ const explicit = options?.[0];
5052
5600
  return (value) => {
5053
5601
  if (!(value instanceof Date)) {
5054
5602
  valueMustBeDate2("date");
5055
5603
  }
5056
- return value.toLocaleDateString(opt);
5604
+ return value.toLocaleDateString(explicit ?? config2.locale);
5057
5605
  };
5058
5606
  };
5059
5607
  var time2 = (options) => {
5060
- const opt = options?.[0] ?? config2.locale;
5608
+ const explicit = options?.[0];
5061
5609
  return (value) => {
5062
5610
  if (!(value instanceof Date)) {
5063
5611
  valueMustBeDate2("time");
5064
5612
  }
5065
- return value.toLocaleTimeString(opt);
5613
+ return value.toLocaleTimeString(explicit ?? config2.locale);
5066
5614
  };
5067
5615
  };
5068
5616
  var datetime2 = (options) => {
5069
- const opt = options?.[0] ?? config2.locale;
5617
+ const explicit = options?.[0];
5070
5618
  return (value) => {
5071
5619
  if (!(value instanceof Date)) {
5072
5620
  valueMustBeDate2("datetime");
5073
5621
  }
5074
- return value.toLocaleString(opt);
5622
+ return value.toLocaleString(explicit ?? config2.locale);
5075
5623
  };
5076
5624
  };
5077
5625
  var ymd2 = (options) => {
@@ -5352,6 +5900,7 @@ function parseStatePart(statePart) {
5352
5900
  } else {
5353
5901
  stateAndPath = statePart.trim();
5354
5902
  }
5903
+ if (stateAndPath.indexOf(STATE_NAME_SEPARATOR3) !== -1) ;
5355
5904
  const [statePathName, stateName = "default"] = stateAndPath.split(STATE_NAME_SEPARATOR3).map(trimFn);
5356
5905
  const pathInfo = getPathInfo(statePathName);
5357
5906
  return {
@@ -5663,7 +6212,7 @@ function buildReferenceIndex(html, options = {}) {
5663
6212
 
5664
6213
  // src/service/semanticValidator.ts
5665
6214
  var STATE_UPDATED_CALLBACK = "$updatedCallback";
5666
- var API_CALL = /\.\s*\$(getAll|resolve)\s*\(/g;
6215
+ var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
5667
6216
  var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
5668
6217
  function splitCallArgs(source, open) {
5669
6218
  const args = [];
@@ -5949,153 +6498,6 @@ function validateSemantics(html, stateTagName = "wcs-state", locale3, bindAttrNa
5949
6498
  return out;
5950
6499
  }
5951
6500
 
5952
- // src/core/validateDocument.ts
5953
- function validateDocument(text, options = {}) {
5954
- const bindAttribute = options.bindAttribute ?? "data-wcs";
5955
- const stateTagName = options.stateTagName ?? "wcs-state";
5956
- const locale3 = options.locale;
5957
- const fileReader = options.fileReader;
5958
- const out = [];
5959
- out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader));
5960
- out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader));
5961
- out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
5962
- out.push(...validateDocumentEnv(text, locale3));
5963
- out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
5964
- out.push(...validateArrayMutations(text, stateTagName, locale3));
5965
- out.push(...validateWatchDeclarations(text, stateTagName, locale3));
5966
- for (const d of validateStateTypes(text, stateTagName, locale3)) {
5967
- out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
5968
- }
5969
- for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
5970
- out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
5971
- }
5972
- return sortDiagnostics(out);
5973
- }
5974
-
5975
- // src/core/sidecar/schemaSubset.ts
5976
- var ALLOWED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
5977
- "type",
5978
- "properties",
5979
- "required",
5980
- "items",
5981
- "enum",
5982
- "const",
5983
- "anyOf",
5984
- "$defs",
5985
- "$ref"
5986
- ]);
5987
- var DiagnosticContext = class {
5988
- constructor(spans) {
5989
- this.spans = spans;
5990
- }
5991
- diagnostics = [];
5992
- add(code, pointer2, message, severity, extra = {}, useKeySpan = false) {
5993
- const span = this.spans.get(pointer2);
5994
- const start = span === void 0 ? 0 : useKeySpan ? span.keyStart ?? span.start : span.start;
5995
- const end = span === void 0 ? 0 : useKeySpan ? span.keyEnd ?? span.end : span.end;
5996
- this.diagnostics.push({ code, start, end, message, severity, ...extra });
5997
- }
5998
- };
5999
- function isSchemaObject(value) {
6000
- return value !== null && typeof value === "object" && !Array.isArray(value);
6001
- }
6002
- function isSchemaMap(value) {
6003
- return value !== null && typeof value === "object" && !Array.isArray(value);
6004
- }
6005
- function validateSchemaSubset(schema, pointerBase, ctx, rootDefs) {
6006
- walkKeywords(schema, pointerBase, ctx, rootDefs);
6007
- const safe = /* @__PURE__ */ new Set();
6008
- detectCycles(schema, pointerBase, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
6009
- for (const [name, def] of Object.entries(rootDefs)) {
6010
- detectCycles(def, `${pointerBase}/$defs/${escape(name)}`, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
6011
- }
6012
- }
6013
- function walkKeywords(node, ptr, ctx, rootDefs) {
6014
- if (!isSchemaObject(node)) return;
6015
- for (const keyword of Object.keys(node)) {
6016
- if (!ALLOWED_SCHEMA_KEYWORDS.has(keyword)) {
6017
- ctx.add(
6018
- WcsDiagnosticCode.ManifestUnknownKeyword,
6019
- `${ptr}/${escape(keyword)}`,
6020
- `Unsupported schema keyword "${keyword}". Allowed: ${[...ALLOWED_SCHEMA_KEYWORDS].join(", ")}.`,
6021
- "warning",
6022
- {},
6023
- true
6024
- );
6025
- }
6026
- }
6027
- if (typeof node.$ref === "string") {
6028
- if (!node.$ref.startsWith("#/")) {
6029
- ctx.add(
6030
- WcsDiagnosticCode.ManifestExternalRef,
6031
- `${ptr}/$ref`,
6032
- `External $ref "${node.$ref}" is forbidden; only local "#/$defs/..." references are allowed.`,
6033
- "error"
6034
- );
6035
- } else if (resolveLocalRef(node.$ref, rootDefs) === void 0) {
6036
- ctx.add(
6037
- WcsDiagnosticCode.ManifestRefUnresolved,
6038
- `${ptr}/$ref`,
6039
- `Unresolved local $ref "${node.$ref}".`,
6040
- "error"
6041
- );
6042
- }
6043
- }
6044
- if (isSchemaMap(node.properties)) {
6045
- for (const [name, child] of Object.entries(node.properties)) {
6046
- walkKeywords(child, `${ptr}/properties/${escape(name)}`, ctx, rootDefs);
6047
- }
6048
- }
6049
- if (node.items !== void 0 && isSchemaObject(node.items)) {
6050
- walkKeywords(node.items, `${ptr}/items`, ctx, rootDefs);
6051
- }
6052
- if (Array.isArray(node.anyOf)) {
6053
- node.anyOf.forEach((child, i) => walkKeywords(child, `${ptr}/anyOf/${i}`, ctx, rootDefs));
6054
- }
6055
- if (isSchemaMap(node.$defs)) {
6056
- for (const [name, child] of Object.entries(node.$defs)) {
6057
- walkKeywords(child, `${ptr}/$defs/${escape(name)}`, ctx, rootDefs);
6058
- }
6059
- }
6060
- }
6061
- function detectCycles(node, ptr, ctx, rootDefs, refStack, safe) {
6062
- if (!isSchemaObject(node)) return;
6063
- if (typeof node.$ref === "string") {
6064
- const ref = node.$ref;
6065
- if (!ref.startsWith("#/")) return;
6066
- if (refStack.has(ref)) {
6067
- ctx.add(WcsDiagnosticCode.ManifestRefCycle, `${ptr}/$ref`, `Cyclic $ref detected at "${ref}".`, "error");
6068
- return;
6069
- }
6070
- if (safe.has(ref)) return;
6071
- const target = resolveLocalRef(ref, rootDefs);
6072
- if (target === void 0) return;
6073
- refStack.add(ref);
6074
- detectCycles(target, ptr, ctx, rootDefs, refStack, safe);
6075
- refStack.delete(ref);
6076
- safe.add(ref);
6077
- return;
6078
- }
6079
- if (isSchemaMap(node.properties)) {
6080
- for (const child of Object.values(node.properties)) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
6081
- }
6082
- if (node.items !== void 0 && isSchemaObject(node.items)) {
6083
- detectCycles(node.items, ptr, ctx, rootDefs, refStack, safe);
6084
- }
6085
- if (Array.isArray(node.anyOf)) {
6086
- for (const child of node.anyOf) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
6087
- }
6088
- }
6089
- function resolveLocalRef(ref, rootDefs) {
6090
- const match = /^#\/\$defs\/(.+)$/.exec(ref);
6091
- if (match === null) return void 0;
6092
- const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
6093
- return rootDefs[name];
6094
- }
6095
- function escape(key) {
6096
- return key.replace(/~/g, "~0").replace(/\//g, "~1");
6097
- }
6098
-
6099
6501
  // src/core/sidecar/jsonSource.ts
6100
6502
  var JsonReader = class {
6101
6503
  constructor(text) {
@@ -6359,8 +6761,13 @@ function resolvePackageContracts(loaded) {
6359
6761
  const collided = /* @__PURE__ */ new Set();
6360
6762
  const firstSource = /* @__PURE__ */ new Map();
6361
6763
  const filterOwner = /* @__PURE__ */ new Map();
6764
+ const stateOwner = /* @__PURE__ */ new Map();
6765
+ const stateWinners = /* @__PURE__ */ new Map();
6766
+ const collidedStates = /* @__PURE__ */ new Set();
6767
+ let hasApplicationArtifact = false;
6362
6768
  for (const lm of loaded) {
6363
6769
  if (lm.manifest === null) continue;
6770
+ if (lm.manifest.kind === "application") hasApplicationArtifact = true;
6364
6771
  const types = lm.manifest.manifestExtensions?.["wcstack.types"];
6365
6772
  if (lm.manifest.kind === "package" && types !== void 0) {
6366
6773
  for (const [tag, component] of Object.entries(types.components ?? {})) {
@@ -6412,13 +6819,102 @@ function resolvePackageContracts(loaded) {
6412
6819
  );
6413
6820
  }
6414
6821
  }
6822
+ if (lm.manifest.kind === "application" && application?.states !== void 0) {
6823
+ for (const [name, entry] of Object.entries(application.states)) {
6824
+ const schema = entry?.stateSchema;
6825
+ if (schema === null || typeof schema !== "object" || Array.isArray(schema)) continue;
6826
+ const priorSource = stateOwner.get(name);
6827
+ if (priorSource === void 0) {
6828
+ stateOwner.set(name, lm.artifact.source);
6829
+ stateWinners.set(name, schema);
6830
+ continue;
6831
+ }
6832
+ collidedStates.add(name);
6833
+ stateWinners.delete(name);
6834
+ ctxFor(lm).add(
6835
+ WcsDiagnosticCode.ManifestStateCollision,
6836
+ pointer("manifestExtensions", "wcstack.application", "states", name),
6837
+ `State "${name}" declares a stateSchema in multiple application artifacts (also in "${priorSource}"); neither is used.`,
6838
+ "error",
6839
+ { statePath: name },
6840
+ true
6841
+ );
6842
+ }
6843
+ }
6415
6844
  }
6416
6845
  const diagnosticsBySource = /* @__PURE__ */ new Map();
6417
6846
  for (const [source, diags] of perSource) {
6418
6847
  const kept = diags.filter((d) => !(d.code === WcsDiagnosticCode.ManifestOverride && d.tag !== void 0 && collided.has(d.tag)));
6419
6848
  if (kept.length > 0) diagnosticsBySource.set(source, kept);
6420
6849
  }
6421
- return { tags: winners, diagnosticsBySource };
6850
+ return { tags: winners, applicationStates: stateWinners, hasApplicationArtifact, diagnosticsBySource };
6851
+ }
6852
+
6853
+ // src/core/sidecar/discover.ts
6854
+ var APPLICATION_MANIFEST_FILENAME = "wcstack.manifest.json";
6855
+ var MAX_ASCEND = 16;
6856
+ function discoverApplicationManifest(fileReader) {
6857
+ for (let up = 0; up <= MAX_ASCEND; up++) {
6858
+ const relativePath = `${"../".repeat(up)}${APPLICATION_MANIFEST_FILENAME}`;
6859
+ const text = fileReader(relativePath);
6860
+ if (text === void 0) continue;
6861
+ const loaded = loadManifest({ text, source: relativePath });
6862
+ return { relativePath, text, loaded, states: applicationStatesOf(loaded) };
6863
+ }
6864
+ return void 0;
6865
+ }
6866
+ function applicationStatesOf(loaded) {
6867
+ const states = /* @__PURE__ */ new Map();
6868
+ const manifest = loaded.manifest;
6869
+ if (manifest === null || manifest.kind !== "application") return states;
6870
+ const application = manifest.manifestExtensions?.["wcstack.application"];
6871
+ for (const [name, entry] of Object.entries(application?.states ?? {})) {
6872
+ const schema = entry?.stateSchema;
6873
+ if (schema !== null && typeof schema === "object" && !Array.isArray(schema)) {
6874
+ states.set(name, schema);
6875
+ }
6876
+ }
6877
+ return states;
6878
+ }
6879
+ function joinRelativeSource(htmlSource, relativePath) {
6880
+ const sepIndex = Math.max(htmlSource.lastIndexOf("/"), htmlSource.lastIndexOf("\\"));
6881
+ const dirSegments = sepIndex === -1 ? [] : htmlSource.slice(0, sepIndex).split(/[\\/]/);
6882
+ for (const segment of relativePath.split("/")) {
6883
+ if (segment === "" || segment === ".") continue;
6884
+ if (segment === "..") {
6885
+ if (dirSegments.length > 0 && dirSegments[dirSegments.length - 1] !== "..") dirSegments.pop();
6886
+ else dirSegments.push("..");
6887
+ continue;
6888
+ }
6889
+ dirSegments.push(segment);
6890
+ }
6891
+ return dirSegments.join("/");
6892
+ }
6893
+
6894
+ // src/core/validateDocument.ts
6895
+ function validateDocument(text, options = {}) {
6896
+ const bindAttribute = options.bindAttribute ?? "data-wcs";
6897
+ const stateTagName = options.stateTagName ?? "wcs-state";
6898
+ const locale3 = options.locale;
6899
+ const fileReader = options.fileReader;
6900
+ const applicationStates = options.applicationStates ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.states : void 0);
6901
+ const out = [];
6902
+ out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader, applicationStates));
6903
+ out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader, applicationStates));
6904
+ out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
6905
+ out.push(...validateAriaAttributes(text, bindAttribute, locale3));
6906
+ out.push(...validateDocumentEnv(text, locale3));
6907
+ out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
6908
+ out.push(...validateArrayMutations(text, stateTagName, locale3));
6909
+ out.push(...validateWatchDeclarations(text, stateTagName, locale3));
6910
+ out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
6911
+ for (const d of validateStateTypes(text, stateTagName, locale3)) {
6912
+ out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
6913
+ }
6914
+ for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
6915
+ out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
6916
+ }
6917
+ return sortDiagnostics(out);
6422
6918
  }
6423
6919
 
6424
6920
  // src/core/sidecar/drift.ts
@@ -6477,13 +6973,24 @@ function checkDrift(tag, component, live, ctx) {
6477
6973
  }
6478
6974
 
6479
6975
  // src/core/sidecar/validate.ts
6976
+ function validateManifestArtifact(artifact) {
6977
+ const loaded = loadManifest(artifact);
6978
+ validateLoadedSchemas(loaded);
6979
+ return sortDiagnostics(loaded.ctx.diagnostics);
6980
+ }
6480
6981
  function validateLoadedSchemas(loaded) {
6481
6982
  if (loaded.manifest === null) return;
6482
6983
  const types = loaded.manifest.manifestExtensions?.["wcstack.types"];
6483
- if (types === void 0) return;
6484
- for (const [tag, component] of Object.entries(types.components ?? {})) {
6984
+ for (const [tag, component] of Object.entries(types?.components ?? {})) {
6485
6985
  validateComponentSchemas(tag, component, loaded.ctx);
6486
6986
  }
6987
+ const application = loaded.manifest.manifestExtensions?.["wcstack.application"];
6988
+ for (const [name, entry] of Object.entries(application?.states ?? {})) {
6989
+ const schema = entry?.stateSchema;
6990
+ if (schema === null || typeof schema !== "object" || Array.isArray(schema)) continue;
6991
+ const ptr = `${pointer("manifestExtensions", "wcstack.application", "states", name)}/stateSchema`;
6992
+ validateSchemaSubset(schema, ptr, loaded.ctx, schema.$defs ?? {});
6993
+ }
6487
6994
  }
6488
6995
  function validateComponentSchemas(tag, component, ctx) {
6489
6996
  const base = pointer("manifestExtensions", "wcstack.types", "components", tag);
@@ -6533,7 +7040,9 @@ function validateManifestSet(input) {
6533
7040
  return {
6534
7041
  diagnostics: sortDiagnostics(all),
6535
7042
  byArtifact: sortedByArtifact,
6536
- resolvedTags
7043
+ resolvedTags,
7044
+ resolvedStates: resolved.applicationStates,
7045
+ hasApplicationArtifact: resolved.hasApplicationArtifact
6537
7046
  };
6538
7047
  }
6539
7048
  function escapePtr(key) {
@@ -6544,13 +7053,9 @@ function escapePtr(key) {
6544
7053
  var severityLabel = { error: "error", warning: "warning", info: "info" };
6545
7054
  function runValidation(inputs, options = {}) {
6546
7055
  const diagnosticsBySource = /* @__PURE__ */ new Map();
6547
- for (const input of inputs) {
6548
- if (input.kind === "html") {
6549
- const docOptions = input.fileReader !== void 0 ? { ...options, fileReader: input.fileReader } : options;
6550
- diagnosticsBySource.set(input.source, validateDocument(input.text, docOptions));
6551
- }
6552
- }
7056
+ const textBySource = new Map(inputs.map((i) => [i.source, i.text]));
6553
7057
  const manifestInputs = inputs.filter((i) => i.kind === "manifest");
7058
+ let explicitStates;
6554
7059
  if (manifestInputs.length > 0) {
6555
7060
  const result = validateManifestSet({
6556
7061
  artifacts: manifestInputs.map((m) => ({ text: m.text, source: m.source })),
@@ -6559,8 +7064,29 @@ function runValidation(inputs, options = {}) {
6559
7064
  for (const input of manifestInputs) {
6560
7065
  diagnosticsBySource.set(input.source, result.byArtifact.get(input.source) ?? []);
6561
7066
  }
7067
+ if (result.hasApplicationArtifact) explicitStates = result.resolvedStates;
7068
+ }
7069
+ for (const input of inputs) {
7070
+ if (input.kind !== "html") continue;
7071
+ let applicationStates = explicitStates;
7072
+ if (applicationStates === void 0 && input.fileReader !== void 0) {
7073
+ const discovered = discoverApplicationManifest(input.fileReader);
7074
+ applicationStates = discovered?.states ?? /* @__PURE__ */ new Map();
7075
+ if (discovered !== void 0) {
7076
+ const source = joinRelativeSource(input.source, discovered.relativePath);
7077
+ if (!diagnosticsBySource.has(source)) {
7078
+ textBySource.set(source, discovered.text);
7079
+ diagnosticsBySource.set(source, validateManifestArtifact({ text: discovered.text, source }));
7080
+ }
7081
+ }
7082
+ }
7083
+ const docOptions = {
7084
+ ...options,
7085
+ ...input.fileReader !== void 0 ? { fileReader: input.fileReader } : {},
7086
+ ...applicationStates !== void 0 ? { applicationStates } : {}
7087
+ };
7088
+ diagnosticsBySource.set(input.source, validateDocument(input.text, docOptions));
6562
7089
  }
6563
- const textBySource = new Map(inputs.map((i) => [i.source, i.text]));
6564
7090
  const lines = [];
6565
7091
  let errorCount = 0;
6566
7092
  let warningCount = 0;
@@ -6582,7 +7108,7 @@ function runValidation(inputs, options = {}) {
6582
7108
  errorCount,
6583
7109
  warningCount,
6584
7110
  infoCount,
6585
- exitCode: errorCount > 0 ? 1 : 0,
7111
+ exitCode: errorCount > 0 || options.strict === true && warningCount > 0 ? 1 : 0,
6586
7112
  diagnosticsBySource
6587
7113
  };
6588
7114
  }
@@ -6599,6 +7125,7 @@ function parseArgs(argv) {
6599
7125
  else if (arg.startsWith("--state-tag=")) options.stateTagName = arg.slice("--state-tag=".length);
6600
7126
  else if (arg.startsWith("--lang=")) options.locale = arg.slice("--lang=".length);
6601
7127
  else if (arg === "--errors-only" || arg === "--quiet") options.errorsOnly = true;
7128
+ else if (arg === "--strict") options.strict = true;
6602
7129
  else if (!arg.startsWith("-")) files.push(arg);
6603
7130
  }
6604
7131
  return { options, files };
@@ -6617,7 +7144,7 @@ function main(argv) {
6617
7144
  const { options, files } = parseArgs(argv);
6618
7145
  const locale3 = resolveCliLocale(options.locale);
6619
7146
  if (files.length === 0) {
6620
- process.stderr.write("usage: wcs-validate [--attr=data-wcs] [--state-tag=wcs-state] [--lang=ja|en] <file> [<file> ...]\n");
7147
+ process.stderr.write("usage: wcs-validate [--attr=data-wcs] [--state-tag=wcs-state] [--lang=ja|en] [--errors-only] [--strict] <file> [<file> ...]\n");
6621
7148
  return 2;
6622
7149
  }
6623
7150
  const inputs = [];
@@ -6639,7 +7166,7 @@ function main(argv) {
6639
7166
  }
6640
7167
  process.stdout.write(
6641
7168
  `
6642
- ${result.errorCount} error(s), ${result.warningCount} warning(s), ${result.infoCount} info
7169
+ ${result.errorCount} error(s), ${result.warningCount} warning(s), ${result.infoCount} info${options.strict ? " (strict)" : ""}
6643
7170
  `
6644
7171
  );
6645
7172
  return result.exitCode;