@wcstack/lint 1.33.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -60,20 +60,16 @@ app.manifest.json:1:3 error wcs/manifest-broken Broken manifest JSON: ...
60
60
 
61
61
  ```json
62
62
  {
63
- "schemaVersion": 1,
63
+ "schemaVersion": 2,
64
64
  "kind": "application",
65
65
  "manifestExtensions": {
66
66
  "wcstack.application": {
67
- "version": 1,
68
- "states": {
69
- "default": {
70
- "stateSchema": {
71
- "type": "object",
72
- "properties": {
73
- "count": { "type": "number" },
74
- "users": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" } } } }
75
- }
76
- }
67
+ "version": 2,
68
+ "stateSchema": {
69
+ "type": "object",
70
+ "properties": {
71
+ "count": { "type": "number" },
72
+ "users": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" } } } }
77
73
  }
78
74
  }
79
75
  }
@@ -82,7 +78,7 @@ app.manifest.json:1:3 error wcs/manifest-broken Broken manifest JSON: ...
82
78
  ```
83
79
 
84
80
  - **発見**: HTML ファイルから上へ辿って最も近い `wcstack.manifest.json` が自動で使われます — コマンドラインに渡す必要はありません。`application` artifact を含む `*.manifest.json` を引数に渡した場合は、その実行全体で発見を置き換えます。VS Code 拡張も同じファイルを発見するので IDE と CLI の結果は一致します。
85
- - **効果**: `stateSchema` を持つ state では、schema 上に確定的に存在しないバインドパスは `wcs/path-nonexistent`(**error**・exit `1`)、`for:` に非配列は `wcs/path-type-mismatch`(**error**)になります。schema が開いたままの箇所(素の `{}`)は沈黙し、インラインスクリプトのメソッド / getter / `$listKeys` は引き続き存在扱いです。schema の無い state は従来どおりです。
81
+ - **効果**: `stateSchema` を宣言すると、schema 上に確定的に存在しないバインドパスは `wcs/path-nonexistent`(**error**・exit `1`)、`for:` に非配列は `wcs/path-type-mismatch`(**error**)になります。schema が開いたままの箇所(素の `{}`)は沈黙し、インラインスクリプトのメソッド / getter / `$listKeys` は引き続き存在扱いです。`stateSchema` が無ければ従来どおりです。
86
82
  - **schema の出どころ**: 手で書く(受け付けるのは JSON-Schema サブセット `type / properties / required / items / enum / const / anyOf / $defs / $ref` のみ)か、TypeScript の state ファイルから `wcs-schema`(`@wcstack/typescript`)で生成します。manifest は派生物なので、CI では `wcs-schema check` で同期を保ってください。
87
83
 
88
84
  ## 生成 → 検証 → 修正ループでの利用
package/README.md CHANGED
@@ -60,20 +60,16 @@ Without a contract, a path the validator cannot resolve is only a **warning** (`
60
60
 
61
61
  ```json
62
62
  {
63
- "schemaVersion": 1,
63
+ "schemaVersion": 2,
64
64
  "kind": "application",
65
65
  "manifestExtensions": {
66
66
  "wcstack.application": {
67
- "version": 1,
68
- "states": {
69
- "default": {
70
- "stateSchema": {
71
- "type": "object",
72
- "properties": {
73
- "count": { "type": "number" },
74
- "users": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" } } } }
75
- }
76
- }
67
+ "version": 2,
68
+ "stateSchema": {
69
+ "type": "object",
70
+ "properties": {
71
+ "count": { "type": "number" },
72
+ "users": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" } } } }
77
73
  }
78
74
  }
79
75
  }
@@ -82,7 +78,7 @@ Without a contract, a path the validator cannot resolve is only a **warning** (`
82
78
  ```
83
79
 
84
80
  - **Discovery**: the nearest `wcstack.manifest.json` walking up from the HTML file is used automatically — nothing to pass on the command line. Passing `*.manifest.json` arguments that contain an `application` artifact replaces discovery for the whole run. The VS Code extension discovers the same file, so IDE and CLI agree.
85
- - **Effect**: for a state that has a `stateSchema`, a bound path that the schema definitely lacks is `wcs/path-nonexistent` (**error**, exit `1`); `for:` on a non-array is `wcs/path-type-mismatch` (**error**). Paths the schema leaves open (a bare `{}`) stay silent, and methods / getters / `$listKeys` from the inline script still count as existing. States without a schema are unchanged.
81
+ - **Effect**: when a `stateSchema` is declared, a bound path that the schema definitely lacks is `wcs/path-nonexistent` (**error**, exit `1`); `for:` on a non-array is `wcs/path-type-mismatch` (**error**). Paths the schema leaves open (a bare `{}`) stay silent, and methods / getters / `$listKeys` from the inline script still count as existing. Without a `stateSchema`, behavior is unchanged.
86
82
  - **Where the schema comes from**: write it by hand (only the JSON-Schema subset `type / properties / required / items / enum / const / anyOf / $defs / $ref` is accepted), or generate it from a TypeScript state file with `wcs-schema` (`@wcstack/typescript`). The manifest is a derived artifact — keep it in sync with `wcs-schema check` in CI.
87
83
 
88
84
  ## Use in generate–validate–fix loops
package/dist/cli.cjs CHANGED
@@ -175,7 +175,11 @@ var WcsDiagnosticCode = {
175
175
  // --- deprecations ---
176
176
  // 名前付き State(`<wcs-state name>` / `path@name`)。v2 でマウント(`mount=` と接頭辞付きパス)に
177
177
  // 置き換わる(docs/state-mount-design.md D16)。1.x では warning、v2 では parse error と同時に error。
178
- NamedStateDeprecated: "wcs/named-state-deprecated"
178
+ NamedStateDeprecated: "wcs/named-state-deprecated",
179
+ // --- volume mount ---
180
+ // `<wcs-state mount="...">` の値が runtime の validateVolumeMountPath で raise する形
181
+ // (空・空セグメント・ワイルドカード・予約文字 $ # @)。runtime と同条件・同文言(v2)。
182
+ MountPathInvalid: "wcs/mount-path-invalid"
179
183
  };
180
184
  function sortDiagnostics(diagnostics) {
181
185
  const severityRank = { error: 0, warning: 1, info: 2 };
@@ -791,7 +795,6 @@ var MAX_WILDCARD_DEPTH = 128;
791
795
  var BINDING_SEPARATOR = ";";
792
796
  var PROP_VALUE_SEPARATOR = ":";
793
797
  var MODIFIER_SEPARATOR = "#";
794
- var STATE_NAME_SEPARATOR = "@";
795
798
  var FILTER_SEPARATOR = "|";
796
799
  var MODIFIER_PREVENT = "prevent";
797
800
  var MODIFIER_STOP = "stop";
@@ -849,7 +852,6 @@ function getWcsManifest() {
849
852
  binding: BINDING_SEPARATOR,
850
853
  propValue: PROP_VALUE_SEPARATOR,
851
854
  modifier: MODIFIER_SEPARATOR,
852
- stateName: STATE_NAME_SEPARATOR,
853
855
  filter: FILTER_SEPARATOR
854
856
  },
855
857
  // 正本 STRUCTURAL_BINDING_TYPE_SET から導出(手書きの二重定義を排除)。
@@ -922,7 +924,7 @@ var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
922
924
  var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
923
925
  var RESERVED_LIST_KEYS_KEY = "$listKeys";
924
926
  var RESERVED_WATCH_KEY = "$watch";
925
- function analyzeStatePaths(scriptContent, stateName = "default") {
927
+ function analyzeStatePaths(scriptContent) {
926
928
  const objectContent = extractDefaultExportObject(scriptContent);
927
929
  if (!objectContent) return [];
928
930
  const paths = [];
@@ -931,27 +933,27 @@ function analyzeStatePaths(scriptContent, stateName = "default") {
931
933
  const pendingListKeys = [];
932
934
  for (const prop of topLevelProps) {
933
935
  if (prop.name.startsWith("$")) {
934
- collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys, stateName);
936
+ collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
935
937
  continue;
936
938
  }
937
939
  if (prop.kind === "method") {
938
- paths.push({ path: prop.name, kind: "method", stateName });
940
+ paths.push({ path: prop.name, kind: "method" });
939
941
  continue;
940
942
  }
941
943
  if (prop.kind === "getter") {
942
- if (!paths.some((p) => p.stateName === stateName && p.path === prop.name)) {
943
- paths.push({ path: prop.name, kind: "computed", stateName });
944
+ if (!paths.some((p) => p.path === prop.name)) {
945
+ paths.push({ path: prop.name, kind: "computed" });
944
946
  }
945
947
  continue;
946
948
  }
947
- pushDataPropertyPaths(prop, paths, stateName);
949
+ pushDataPropertyPaths(prop, paths);
948
950
  }
949
951
  for (const streamValue of pendingStreamValues) {
950
- if (paths.some((p) => p.stateName === stateName && p.path === streamValue.name)) continue;
951
- pushDataPropertyPaths(streamValue, paths, stateName);
952
+ if (paths.some((p) => p.path === streamValue.name)) continue;
953
+ pushDataPropertyPaths(streamValue, paths);
952
954
  }
953
955
  for (const listKeyEntry of pendingListKeys) {
954
- pushListKeyPaths(listKeyEntry, paths, stateName);
956
+ pushListKeyPaths(listKeyEntry, paths);
955
957
  }
956
958
  return paths;
957
959
  }
@@ -1038,7 +1040,7 @@ function findNonObjectWatch(scriptContent) {
1038
1040
  if (!isArrowFunction && !isWholeLiteral) return null;
1039
1041
  return span;
1040
1042
  }
1041
- function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys, stateName) {
1043
+ function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys) {
1042
1044
  if (prop.name === RESERVED_STREAMS_KEY && prop.kind === "data" && prop.value && isObjectLiteral(prop.value)) {
1043
1045
  const entries = parseTopLevelProperties(extractObjectContent(prop.value));
1044
1046
  for (const entry of entries) {
@@ -1050,20 +1052,20 @@ function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKe
1050
1052
  value: initial?.value,
1051
1053
  typeHint: initial?.typeHint
1052
1054
  });
1053
- paths.push({ path: `$streamStatus.${entry.name}`, kind: "data", typeHint: "string", stateName });
1054
- paths.push({ path: `$streamError.${entry.name}`, kind: "data", stateName });
1055
+ paths.push({ path: `$streamStatus.${entry.name}`, kind: "data", typeHint: "string" });
1056
+ paths.push({ path: `$streamError.${entry.name}`, kind: "data" });
1055
1057
  }
1056
1058
  return;
1057
1059
  }
1058
1060
  if (prop.name === RESERVED_COMMAND_TOKENS_KEY && prop.value) {
1059
1061
  for (const name of extractStringArrayItems(prop.value)) {
1060
- paths.push({ path: `$command.${name}`, kind: "command", stateName });
1062
+ paths.push({ path: `$command.${name}`, kind: "command" });
1061
1063
  }
1062
1064
  return;
1063
1065
  }
1064
1066
  if (prop.name === RESERVED_EVENT_TOKENS_KEY && prop.value) {
1065
1067
  for (const name of extractStringArrayItems(prop.value)) {
1066
- paths.push({ path: name, kind: "eventToken", stateName });
1068
+ paths.push({ path: name, kind: "eventToken" });
1067
1069
  }
1068
1070
  return;
1069
1071
  }
@@ -1075,22 +1077,22 @@ function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKe
1075
1077
  return;
1076
1078
  }
1077
1079
  }
1078
- function pushListKeyPaths(entry, paths, stateName) {
1080
+ function pushListKeyPaths(entry, paths) {
1079
1081
  const listPath = entry.name;
1080
1082
  const segments = listPath.split(".");
1081
1083
  if (listPath.length === 0 || segments.some((s) => s.length === 0) || segments[segments.length - 1] === "*") {
1082
1084
  return;
1083
1085
  }
1084
- const has = (path) => paths.some((p) => p.stateName === stateName && p.path === path);
1085
- if (!has(listPath)) paths.push({ path: listPath, kind: "data", typeHint: "array", stateName });
1086
- if (!has(`${listPath}.*`)) paths.push({ path: `${listPath}.*`, kind: "list", stateName });
1086
+ const has = (path) => paths.some((p) => p.path === path);
1087
+ if (!has(listPath)) paths.push({ path: listPath, kind: "data", typeHint: "array" });
1088
+ if (!has(`${listPath}.*`)) paths.push({ path: `${listPath}.*`, kind: "list" });
1087
1089
  if (!has(`${listPath}.length`)) {
1088
- paths.push({ path: `${listPath}.length`, kind: "data", typeHint: "number", stateName });
1090
+ paths.push({ path: `${listPath}.length`, kind: "data", typeHint: "number" });
1089
1091
  }
1090
1092
  const keyField = extractStringLiteralValue(entry.value);
1091
1093
  if (keyField === null || keyField.includes(".") || keyField.includes("*")) return;
1092
1094
  if (!has(`${listPath}.*.${keyField}`)) {
1093
- paths.push({ path: `${listPath}.*.${keyField}`, kind: "data", stateName });
1095
+ paths.push({ path: `${listPath}.*.${keyField}`, kind: "data" });
1094
1096
  }
1095
1097
  }
1096
1098
  function extractStringLiteralValue(value) {
@@ -1113,17 +1115,17 @@ function extractStringArrayItems(value) {
1113
1115
  return items;
1114
1116
  }
1115
1117
  var MAX_OBJECT_NEST_DEPTH = 5;
1116
- function pushDataPropertyPaths(prop, paths, stateName) {
1117
- pushDataPropertyPathsAt(prop.name, prop, paths, stateName, 0);
1118
+ function pushDataPropertyPaths(prop, paths) {
1119
+ pushDataPropertyPathsAt(prop.name, prop, paths, 0);
1118
1120
  }
1119
- function pushDataPropertyPathsAt(path, prop, paths, stateName, depth) {
1120
- paths.push({ path, kind: "data", typeHint: prop.typeHint, rawInitial: prop.value?.trim(), stateName });
1121
+ function pushDataPropertyPathsAt(path, prop, paths, depth) {
1122
+ paths.push({ path, kind: "data", typeHint: prop.typeHint, rawInitial: prop.value?.trim() });
1121
1123
  if (prop.value && isArrayLiteral(prop.value)) {
1122
- paths.push({ path: `${path}.*`, kind: "list", stateName });
1123
- paths.push({ path: `${path}.length`, kind: "data", typeHint: "number", stateName });
1124
+ paths.push({ path: `${path}.*`, kind: "list" });
1125
+ paths.push({ path: `${path}.length`, kind: "data", typeHint: "number" });
1124
1126
  if (depth >= MAX_OBJECT_NEST_DEPTH) return;
1125
1127
  for (const childProp of extractArrayElementDataProperties(prop.value)) {
1126
- pushDataPropertyPathsAt(`${path}.*.${childProp.name}`, childProp, paths, stateName, depth + 1);
1128
+ pushDataPropertyPathsAt(`${path}.*.${childProp.name}`, childProp, paths, depth + 1);
1127
1129
  }
1128
1130
  return;
1129
1131
  }
@@ -1132,11 +1134,11 @@ function pushDataPropertyPathsAt(path, prop, paths, stateName, depth) {
1132
1134
  const childProps = parseTopLevelProperties(extractObjectContent(prop.value));
1133
1135
  for (const childProp of childProps) {
1134
1136
  if (childProp.kind !== "data") continue;
1135
- pushDataPropertyPathsAt(`${path}.${childProp.name}`, childProp, paths, stateName, depth + 1);
1137
+ pushDataPropertyPathsAt(`${path}.${childProp.name}`, childProp, paths, depth + 1);
1136
1138
  }
1137
1139
  }
1138
1140
  }
1139
- function analyzeJsonPaths(jsonString, stateName = "default") {
1141
+ function analyzeJsonPaths(jsonString) {
1140
1142
  let data;
1141
1143
  try {
1142
1144
  data = JSON.parse(jsonString);
@@ -1145,31 +1147,31 @@ function analyzeJsonPaths(jsonString, stateName = "default") {
1145
1147
  }
1146
1148
  if (typeof data !== "object" || data === null || Array.isArray(data)) return [];
1147
1149
  const paths = [];
1148
- collectJsonPaths(data, "", paths, stateName, 0);
1150
+ collectJsonPaths(data, "", paths, 0);
1149
1151
  return paths;
1150
1152
  }
1151
- function collectJsonPaths(obj, prefix, paths, stateName, depth) {
1153
+ function collectJsonPaths(obj, prefix, paths, depth) {
1152
1154
  if (depth >= MAX_OBJECT_NEST_DEPTH) return;
1153
1155
  for (const [key, value] of Object.entries(obj)) {
1154
1156
  if (prefix === "" && key.startsWith("$")) continue;
1155
1157
  const path = prefix ? `${prefix}.${key}` : key;
1156
- pushJsonValuePaths(path, value, paths, stateName, depth);
1158
+ pushJsonValuePaths(path, value, paths, depth);
1157
1159
  }
1158
1160
  }
1159
- function pushJsonValuePaths(path, value, paths, stateName, depth) {
1160
- paths.push({ path, kind: "data", typeHint: inferJsonTypeHint(value), stateName });
1161
+ function pushJsonValuePaths(path, value, paths, depth) {
1162
+ paths.push({ path, kind: "data", typeHint: inferJsonTypeHint(value) });
1161
1163
  if (Array.isArray(value)) {
1162
- paths.push({ path: `${path}.*`, kind: "list", stateName });
1163
- paths.push({ path: `${path}.length`, kind: "data", typeHint: "number", stateName });
1164
+ paths.push({ path: `${path}.*`, kind: "list" });
1165
+ paths.push({ path: `${path}.length`, kind: "data", typeHint: "number" });
1164
1166
  if (depth >= MAX_OBJECT_NEST_DEPTH) return;
1165
1167
  if (value.length > 0 && typeof value[0] === "object" && value[0] !== null && !Array.isArray(value[0])) {
1166
1168
  const firstElement = value[0];
1167
1169
  for (const [childKey, childValue] of Object.entries(firstElement)) {
1168
- pushJsonValuePaths(`${path}.*.${childKey}`, childValue, paths, stateName, depth + 1);
1170
+ pushJsonValuePaths(`${path}.*.${childKey}`, childValue, paths, depth + 1);
1169
1171
  }
1170
1172
  }
1171
1173
  } else if (typeof value === "object" && value !== null) {
1172
- collectJsonPaths(value, path, paths, stateName, depth + 1);
1174
+ collectJsonPaths(value, path, paths, depth + 1);
1173
1175
  }
1174
1176
  }
1175
1177
  function inferJsonTypeHint(value) {
@@ -1409,23 +1411,21 @@ function inferTypeHint(valueStart) {
1409
1411
  if (v.startsWith("{")) return "object";
1410
1412
  return void 0;
1411
1413
  }
1412
- function analyzeSchemaPaths(schema, stateName = "default") {
1414
+ function analyzeSchemaPaths(schema) {
1413
1415
  const paths = [];
1414
1416
  const defs = schema.$defs ?? {};
1415
- collectSchemaObjectPaths(schema, "", paths, stateName, defs, 0);
1417
+ collectSchemaObjectPaths(schema, "", paths, defs, 0);
1416
1418
  return paths;
1417
1419
  }
1418
- function mergeSchemaCandidates(candidates, applicationStates) {
1419
- if (applicationStates === void 0 || applicationStates.size === 0) return candidates;
1420
+ function mergeSchemaCandidates(candidates, applicationSchema) {
1421
+ if (applicationSchema === void 0) return candidates;
1420
1422
  const schemaCandidates = [];
1421
1423
  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
- }
1424
+ for (const p of analyzeSchemaPaths(applicationSchema)) {
1425
+ schemaCandidates.push(p);
1426
+ schemaKeys.add(p.path);
1427
1427
  }
1428
- const kept = candidates.filter((p) => !schemaKeys.has(`${p.stateName} ${p.path}`));
1428
+ const kept = candidates.filter((p) => !schemaKeys.has(p.path));
1429
1429
  return [...kept, ...schemaCandidates];
1430
1430
  }
1431
1431
  function derefSchemaNodes(node, defs) {
@@ -1477,7 +1477,7 @@ function schemaTypeHint(nodes) {
1477
1477
  }
1478
1478
  return hints.size === 0 ? void 0 : [...hints].join("|");
1479
1479
  }
1480
- function collectSchemaObjectPaths(node, prefix, paths, stateName, defs, depth) {
1480
+ function collectSchemaObjectPaths(node, prefix, paths, defs, depth) {
1481
1481
  if (depth >= MAX_OBJECT_NEST_DEPTH) return;
1482
1482
  const seen = /* @__PURE__ */ new Set();
1483
1483
  for (const n of derefSchemaNodes(node, defs)) {
@@ -1486,33 +1486,33 @@ function collectSchemaObjectPaths(node, prefix, paths, stateName, defs, depth) {
1486
1486
  if (seen.has(key)) continue;
1487
1487
  seen.add(key);
1488
1488
  const path = prefix ? `${prefix}.${key}` : key;
1489
- pushSchemaValuePaths(path, child, paths, stateName, defs, depth);
1489
+ pushSchemaValuePaths(path, child, paths, defs, depth);
1490
1490
  }
1491
1491
  }
1492
1492
  }
1493
- function pushSchemaValuePaths(path, node, paths, stateName, defs, depth) {
1493
+ function pushSchemaValuePaths(path, node, paths, defs, depth) {
1494
1494
  const nodes = derefSchemaNodes(node, defs);
1495
1495
  const typeHint = schemaTypeHint(nodes);
1496
- paths.push(withHint({ path, kind: "data", stateName, fromSchema: true }, typeHint));
1496
+ paths.push(withHint({ path, kind: "data", fromSchema: true }, typeHint));
1497
1497
  const items = nodes.map((n) => n.items).find((i) => i !== void 0 && i !== null && typeof i === "object");
1498
1498
  const isArray = items !== void 0 || (typeHint?.split("|").includes("array") ?? false);
1499
1499
  if (isArray) {
1500
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 });
1501
+ paths.push(withHint({ path: `${path}.*`, kind: "list", fromSchema: true }, schemaTypeHint(itemNodes)));
1502
+ paths.push({ path: `${path}.length`, kind: "data", typeHint: "number", fromSchema: true });
1503
1503
  if (depth >= MAX_OBJECT_NEST_DEPTH) return;
1504
1504
  const seen = /* @__PURE__ */ new Set();
1505
1505
  for (const n of itemNodes) {
1506
1506
  for (const [childKey, childNode] of Object.entries(n.properties ?? {})) {
1507
1507
  if (seen.has(childKey)) continue;
1508
1508
  seen.add(childKey);
1509
- pushSchemaValuePaths(`${path}.*.${childKey}`, childNode, paths, stateName, defs, depth + 1);
1509
+ pushSchemaValuePaths(`${path}.*.${childKey}`, childNode, paths, defs, depth + 1);
1510
1510
  }
1511
1511
  }
1512
1512
  return;
1513
1513
  }
1514
1514
  if (nodes.some((n) => n.properties !== void 0)) {
1515
- collectSchemaObjectPaths(node, path, paths, stateName, defs, depth + 1);
1515
+ collectSchemaObjectPaths(node, path, paths, defs, depth + 1);
1516
1516
  }
1517
1517
  }
1518
1518
  function withHint(candidate, typeHint) {
@@ -1536,7 +1536,7 @@ function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
1536
1536
  pos++;
1537
1537
  continue;
1538
1538
  }
1539
- const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
1539
+ const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
1540
1540
  pos = wcsMatch.end;
1541
1541
  const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
1542
1542
  const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
@@ -1568,7 +1568,7 @@ function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
1568
1568
  contentStart,
1569
1569
  contentEnd,
1570
1570
  content: html.slice(contentStart, contentEnd),
1571
- stateName
1571
+ mountPath
1572
1572
  });
1573
1573
  pos = html.indexOf(">", scriptCloseIdx) + 1;
1574
1574
  if (pos === 0) break;
@@ -1597,7 +1597,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
1597
1597
  pos++;
1598
1598
  continue;
1599
1599
  }
1600
- const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
1600
+ const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
1601
1601
  const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
1602
1602
  const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
1603
1603
  const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
@@ -1634,12 +1634,12 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
1634
1634
  contentStart,
1635
1635
  contentEnd: scriptCloseIdx,
1636
1636
  content: html.slice(contentStart, scriptCloseIdx),
1637
- stateName
1637
+ mountPath
1638
1638
  });
1639
1639
  pos = html.indexOf(">", scriptCloseIdx) + 1;
1640
1640
  if (pos === 0) break;
1641
1641
  }
1642
- elements.push({ stateName, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
1642
+ elements.push({ mountPath, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
1643
1643
  pos = wcsEnd;
1644
1644
  if (wcsCloseIdx !== -1) {
1645
1645
  const closeEnd = html.indexOf(">", wcsCloseIdx);
@@ -1750,33 +1750,45 @@ function getStatePathsFromHtml(html, stateTagName = "wcs-state", fileReader) {
1750
1750
  return allPaths;
1751
1751
  }
1752
1752
  function resolveElementPaths(element, html, fileReader) {
1753
+ const raw = resolveElementPathsRaw(element, html, fileReader);
1754
+ if (element.mountPath === null) return raw;
1755
+ const prefix = element.mountPath + ".";
1756
+ const out = [];
1757
+ for (const p of raw) {
1758
+ if (p.path.startsWith("$")) continue;
1759
+ if (p.kind === "method" || p.kind === "eventToken") continue;
1760
+ out.push({ ...p, path: prefix + p.path });
1761
+ }
1762
+ return out;
1763
+ }
1764
+ function resolveElementPathsRaw(element, html, fileReader) {
1753
1765
  if (element.stateAttr) {
1754
1766
  const jsonContent = findScriptJsonById(html, element.stateAttr);
1755
1767
  if (jsonContent) {
1756
- const paths = analyzeJsonPaths(jsonContent, element.stateName);
1768
+ const paths = analyzeJsonPaths(jsonContent);
1757
1769
  if (paths.length > 0) return paths;
1758
1770
  }
1759
1771
  }
1760
1772
  if (element.srcAttr && fileReader) {
1761
- const paths = resolveSrcAttribute(element.srcAttr, element.stateName, fileReader);
1773
+ const paths = resolveSrcAttribute(element.srcAttr, fileReader);
1762
1774
  if (paths.length > 0) return paths;
1763
1775
  }
1764
1776
  if (element.jsonAttr) {
1765
- const paths = analyzeJsonPaths(element.jsonAttr, element.stateName);
1777
+ const paths = analyzeJsonPaths(element.jsonAttr);
1766
1778
  if (paths.length > 0) return paths;
1767
1779
  }
1768
1780
  if (element.scriptBlocks.length > 0) {
1769
1781
  return element.scriptBlocks.flatMap(
1770
- (block) => analyzeStatePaths(block.content, block.stateName)
1782
+ (block) => analyzeStatePaths(block.content)
1771
1783
  );
1772
1784
  }
1773
1785
  return [];
1774
1786
  }
1775
- function resolveSrcAttribute(srcPath, stateName, fileReader) {
1787
+ function resolveSrcAttribute(srcPath, fileReader) {
1776
1788
  if (srcPath.endsWith(".json")) {
1777
1789
  const content = fileReader(srcPath);
1778
1790
  if (content) {
1779
- return analyzeJsonPaths(content, stateName);
1791
+ return analyzeJsonPaths(content);
1780
1792
  }
1781
1793
  return [];
1782
1794
  }
@@ -1784,18 +1796,18 @@ function resolveSrcAttribute(srcPath, stateName, fileReader) {
1784
1796
  const tsPath = srcPath.replace(/\.js$/, ".ts");
1785
1797
  const tsContent = fileReader(tsPath);
1786
1798
  if (tsContent) {
1787
- return analyzeStatePaths(tsContent, stateName);
1799
+ return analyzeStatePaths(tsContent);
1788
1800
  }
1789
1801
  const jsContent = fileReader(srcPath);
1790
1802
  if (jsContent) {
1791
- return analyzeStatePaths(jsContent, stateName);
1803
+ return analyzeStatePaths(jsContent);
1792
1804
  }
1793
1805
  return [];
1794
1806
  }
1795
1807
  if (srcPath.endsWith(".ts")) {
1796
1808
  const content = fileReader(srcPath);
1797
1809
  if (content) {
1798
- return analyzeStatePaths(content, stateName);
1810
+ return analyzeStatePaths(content);
1799
1811
  }
1800
1812
  return [];
1801
1813
  }
@@ -1970,8 +1982,20 @@ var ja = {
1970
1982
  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`,
1971
1983
  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`,
1972
1984
  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`
1985
+ namedStateAttrDeprecated: (name) => `name \u5C5E\u6027\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\uFF081 root 1 \u30C4\u30EA\u30FC\uFF09\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`,
1986
+ namedStatePathDeprecated: (name) => name === "default" ? `"@default" \u30BB\u30EC\u30AF\u30BF\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\u3002"@default" \u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09` : `"@name" \u30BB\u30EC\u30AF\u30BF\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\uFF081 root 1 \u30C4\u30EA\u30FC\uFF09\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`,
1987
+ mountPathInvalid: (problem, mountPath) => {
1988
+ switch (problem) {
1989
+ case "empty":
1990
+ return `"mount" \u306B\u306F\u7A7A\u3067\u306A\u3044\u30C4\u30EA\u30FC\u30D1\u30B9\u304C\u5FC5\u8981\u3067\u3059\uFF08runtime: "mount" requires a non-empty tree path.\uFF09`;
1991
+ case "emptySegment":
1992
+ return `"mount" \u30D1\u30B9 "${mountPath}" \u306B\u7A7A\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059\uFF08runtime: has an empty segment.\uFF09`;
1993
+ case "wildcard":
1994
+ return `"mount" \u30D1\u30B9 "${mountPath}" \u306F\u9759\u7684\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093 \u2014 \u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08runtime: must be static.\uFF09`;
1995
+ default:
1996
+ return `"mount" \u30D1\u30B9 "${mountPath}" \u306B\u4E88\u7D04\u6587\u5B57\uFF08$, #, @\uFF09\u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08runtime: must not use reserved characters.\uFF09`;
1997
+ }
1998
+ }
1975
1999
  };
1976
2000
  var EN_EXPECTED_LABEL = {
1977
2001
  array: "an array-typed path",
@@ -2029,8 +2053,20 @@ var en = {
2029
2053
  devtoolsAfterState: () => `Load @wcstack/devtools/auto BEFORE @wcstack/state/auto (otherwise the wiring ledger is not captured live)`,
2030
2054
  baseHrefMissing: () => `An SPA using @wcstack/router needs <base href="/"> in <head> (without it, deep links misderive the basename)`,
2031
2055
  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)`
2056
+ namedStateAttrDeprecated: (name) => `The "name" attribute was removed in v2 \u2014 there is a single state tree per root. Mount this state onto the tree instead: <wcs-state mount="${name}"> and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
2057
+ namedStatePathDeprecated: (name) => name === "default" ? `The "@default" selector was removed in v2 \u2014 drop it (docs/state-mount-design.md \xA79)` : `The "@name" selector was removed in v2 \u2014 there is a single state tree. Mount the named state onto the tree (<wcs-state mount="...">) and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
2058
+ mountPathInvalid: (problem, mountPath) => {
2059
+ switch (problem) {
2060
+ case "empty":
2061
+ return `"mount" requires a non-empty tree path.`;
2062
+ case "emptySegment":
2063
+ return `"mount" path "${mountPath}" has an empty segment.`;
2064
+ case "wildcard":
2065
+ return `"mount" path "${mountPath}" must be static (wildcards are not allowed).`;
2066
+ default:
2067
+ return `"mount" path "${mountPath}" must not use reserved characters ($, #, @).`;
2068
+ }
2069
+ }
2034
2070
  };
2035
2071
  var CATALOGS = { ja, en };
2036
2072
  function getMessages(locale3) {
@@ -2231,16 +2267,10 @@ function escape(key) {
2231
2267
 
2232
2268
  // src/service/bindingValidator.ts
2233
2269
  var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
2234
- function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader, applicationStates) {
2270
+ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader, applicationSchema) {
2235
2271
  const diagnostics = [];
2236
2272
  const msgs = getMessages(locale3);
2237
- const statePaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationStates);
2238
- const pathsByState = /* @__PURE__ */ new Map();
2239
- for (const p of statePaths) {
2240
- const list = pathsByState.get(p.stateName) ?? [];
2241
- list.push(p);
2242
- pathsByState.set(p.stateName, list);
2243
- }
2273
+ const statePaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
2244
2274
  const attrs = findAllBindAttributes(html, attrName);
2245
2275
  let structuralTemplates = null;
2246
2276
  const getStructuralTemplates = () => {
@@ -2273,7 +2303,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
2273
2303
  for (const binding of bindings) {
2274
2304
  const bindingStart = attr.valueStart + pos;
2275
2305
  const parsed = parseBindingExpression(binding);
2276
- const scopedPaths = pathsByState.get(parsed.targetState) ?? [];
2306
+ const scopedPaths = statePaths;
2277
2307
  const scopedPathSet = new Set(scopedPaths.map((p) => p.path));
2278
2308
  const propNoMod = parsed.property.replace(/#.*$/, "").trim();
2279
2309
  if (propNoMod === "...") {
@@ -2357,7 +2387,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
2357
2387
  }
2358
2388
  }
2359
2389
  if (checkPath) {
2360
- const schema = applicationStates?.get(parsed.targetState);
2390
+ const schema = applicationSchema;
2361
2391
  const verdict = schema !== void 0 ? validateSchemaPathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, schema, msgs) : toMissingVerdict(validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs));
2362
2392
  if (verdict) {
2363
2393
  const pathOffset = binding.indexOf(parsed.path);
@@ -2486,7 +2516,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
2486
2516
  if (typeReq && resultType !== typeReq.expected) {
2487
2517
  const pathOffset = binding.indexOf(parsed.path);
2488
2518
  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);
2519
+ const schemaDefinite = typeReq.expected === "array" && parsed.filters.length === 0 && applicationSchema !== void 0 && scopedPaths.some((p) => p.path === pathTrimmed && p.fromSchema === true);
2490
2520
  diagnostics.push({
2491
2521
  code: schemaDefinite ? WcsDiagnosticCode.PathTypeMismatch : WcsDiagnosticCode.BindingTypeExpectation,
2492
2522
  start: pathStart,
@@ -2540,7 +2570,7 @@ function splitBindingExpressions(value) {
2540
2570
  function parseBindingExpression(expr) {
2541
2571
  const colonIndex = expr.indexOf(":");
2542
2572
  if (colonIndex === -1) {
2543
- return { property: expr.trim(), path: null, targetState: "default", filters: [], inputFilters: [] };
2573
+ return { property: expr.trim(), path: null, filters: [], inputFilters: [] };
2544
2574
  }
2545
2575
  const rawProp = expr.slice(0, colonIndex);
2546
2576
  const propSegments = splitByPipe(rawProp);
@@ -2552,9 +2582,8 @@ function parseBindingExpression(expr) {
2552
2582
  const filterSegments = segments.slice(1);
2553
2583
  const atIndex = pathSegment.indexOf("@");
2554
2584
  const path = atIndex !== -1 ? pathSegment.slice(0, atIndex) : pathSegment;
2555
- const targetState = atIndex !== -1 ? pathSegment.slice(atIndex + 1).trim() || "default" : "default";
2556
2585
  const filters = parseFilterSegments(expr, filterSegments, colonIndex + 1 + pathSegment.length + 1);
2557
- return { property, path: path.trim() || null, targetState, filters, inputFilters };
2586
+ return { property, path: path.trim() || null, filters, inputFilters };
2558
2587
  }
2559
2588
  function parseFilterSegments(expr, segments, searchStart) {
2560
2589
  const filters = [];
@@ -3078,11 +3107,11 @@ function isInsideTag(html, offset, tagName) {
3078
3107
  }
3079
3108
 
3080
3109
  // src/service/templateSyntaxValidator.ts
3081
- function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader, applicationStates) {
3110
+ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader, applicationSchema) {
3082
3111
  const diagnostics = [];
3083
3112
  const msgs = getMessages(locale3);
3084
- const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationStates);
3085
- const defaultSchema = applicationStates?.get("default");
3113
+ const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
3114
+ const defaultSchema = applicationSchema;
3086
3115
  const missingVerdict = (path, displayPath, pathSet2, scoped) => {
3087
3116
  if (isValidTemplatePath(path, pathSet2, scoped)) return null;
3088
3117
  if (defaultSchema !== void 0 && !path.startsWith("$")) {
@@ -3092,7 +3121,7 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
3092
3121
  return { code: WcsDiagnosticCode.BindingPathMissing, severity: "warning", message: msgs.pathMissing(displayPath) };
3093
3122
  };
3094
3123
  if (allPaths.length === 0) return diagnostics;
3095
- const defaultPaths = allPaths.filter((p) => p.stateName === "default");
3124
+ const defaultPaths = allPaths;
3096
3125
  const pathSet = new Set(defaultPaths.map((p) => p.path));
3097
3126
  const filterNameSet = new Set(BUILTIN_FILTERS.map((f) => f.name));
3098
3127
  const mustaches = findAllMustacheSyntax(html);
@@ -3118,9 +3147,8 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
3118
3147
  }
3119
3148
  if (!item.expression) continue;
3120
3149
  const parts = item.expression.split("|");
3121
- let pathPart = (parts[0] || "").trim();
3122
- const atIdx = pathPart.indexOf("@");
3123
- if (atIdx !== -1) pathPart = pathPart.slice(0, atIdx).trim();
3150
+ const pathPart = (parts[0] || "").trim();
3151
+ if (pathPart.includes("@")) continue;
3124
3152
  const insideFor = item.insideTemplate && isInsideForTemplate(html, item.matchStart, bindAttrName);
3125
3153
  if (pathPart && !/^-?\d|^["'`]|^true$|^false$|^null$/.test(pathPart)) {
3126
3154
  if (!insideFor && pathPart.includes("*")) {
@@ -3141,7 +3169,7 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
3141
3169
  severity: "warning"
3142
3170
  });
3143
3171
  }
3144
- if (insideFor && !pathPart.startsWith(".") && !pathPart.includes("@")) {
3172
+ if (insideFor && !pathPart.startsWith(".")) {
3145
3173
  const indexMatch = /^\$(\d+)$/.exec(pathPart);
3146
3174
  const needed = indexMatch !== null ? Number(indexMatch[1]) : pathPart.includes("*") ? countWildcardSegments(pathPart) : 0;
3147
3175
  if (needed > 0) {
@@ -4603,7 +4631,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
4603
4631
  return;
4604
4632
  }
4605
4633
  if (property === "trigger" && "trigger" in contract.inputs && parsed.path) {
4606
- const cand = findDataSlot(getPaths(), parsed.path, parsed.targetState);
4634
+ const cand = findDataSlot(getPaths(), parsed.path);
4607
4635
  if (cand?.rawInitial === "true") {
4608
4636
  diagnostics.push({
4609
4637
  code: WcsDiagnosticCode.TriggerSeededTruthy,
@@ -4617,7 +4645,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
4617
4645
  }
4618
4646
  }
4619
4647
  if (tagName === "wcs-storage" && property === "value" && !hasManual && parsed.path && !/(?:^|,)init=(?:element|auto)\b/.test(modifiers)) {
4620
- const cand = findDataSlot(getPaths(), parsed.path, parsed.targetState);
4648
+ const cand = findDataSlot(getPaths(), parsed.path);
4621
4649
  if (cand?.rawInitial !== void 0 && EMPTYISH_SEEDS.has(normalizeSeed(cand.rawInitial))) {
4622
4650
  diagnostics.push({
4623
4651
  code: WcsDiagnosticCode.StorageSeedClobber,
@@ -4631,8 +4659,8 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
4631
4659
  }
4632
4660
  }
4633
4661
  }
4634
- function findDataSlot(paths, path, stateName) {
4635
- return paths.find((c) => c.kind === "data" && c.path === path && c.stateName === stateName);
4662
+ function findDataSlot(paths, path) {
4663
+ return paths.find((c) => c.kind === "data" && c.path === path);
4636
4664
  }
4637
4665
  function normalizeSeed(raw) {
4638
4666
  const compact = raw.replace(/\s+/g, "");
@@ -4907,7 +4935,7 @@ function blankJsComments(code) {
4907
4935
  }
4908
4936
 
4909
4937
  // src/service/watchDeclarationValidator.ts
4910
- var STATE_NAME_SEPARATOR2 = "@";
4938
+ var STATE_NAME_SEPARATOR = "@";
4911
4939
  function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
4912
4940
  const msgs = getMessages(locale3);
4913
4941
  const out = [];
@@ -4924,7 +4952,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
4924
4952
  }
4925
4953
  const entries = analyzeWatchEntries(block.content);
4926
4954
  if (entries.length === 0) continue;
4927
- const paths = analyzeStatePaths(block.content, block.stateName);
4955
+ const paths = analyzeStatePaths(block.content);
4928
4956
  const pathSet = new Set(paths.map((p) => p.path));
4929
4957
  for (const entry of entries) {
4930
4958
  const diagnostic = validateEntry(entry, pathSet, msgs);
@@ -4943,7 +4971,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
4943
4971
  function validateEntry(entry, pathSet, msgs) {
4944
4972
  const { key } = entry;
4945
4973
  const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
4946
- if (key.includes(STATE_NAME_SEPARATOR2)) {
4974
+ if (key.includes(STATE_NAME_SEPARATOR)) {
4947
4975
  return invalid(msgs.watchKeyCrossState(key));
4948
4976
  }
4949
4977
  if (key.startsWith("$")) {
@@ -4992,7 +5020,6 @@ function validateNamedState(html, attrName, stateTagName = "wcs-state", locale3)
4992
5020
  const diagnostics = [];
4993
5021
  for (const element of parseWcsStateElements(html, stateTagName)) {
4994
5022
  const tagText = html.slice(element.tagStart, element.tagEnd);
4995
- if (/\sbind-component(?=[\s=>/])/i.test(tagText)) continue;
4996
5023
  const match = /(?:^|\s)name\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tagText);
4997
5024
  if (match === null) continue;
4998
5025
  const value = match[1] ?? match[2] ?? match[3] ?? "";
@@ -5003,7 +5030,7 @@ function validateNamedState(html, attrName, stateTagName = "wcs-state", locale3)
5003
5030
  start: valueEnd - value.length,
5004
5031
  end: valueEnd,
5005
5032
  message: msgs.namedStateAttrDeprecated(value),
5006
- severity: "warning"
5033
+ severity: "error"
5007
5034
  });
5008
5035
  }
5009
5036
  for (const attr of findAllBindAttributes(html, attrName)) {
@@ -5016,27 +5043,62 @@ function validateNamedState(html, attrName, stateTagName = "wcs-state", locale3)
5016
5043
  start: attr.valueStart + pos + selector.start,
5017
5044
  end: attr.valueStart + pos + selector.end,
5018
5045
  message: msgs.namedStatePathDeprecated(selector.name),
5019
- severity: "warning"
5046
+ severity: "error"
5020
5047
  });
5021
5048
  }
5022
5049
  pos += expr.length + 1;
5023
5050
  }
5024
5051
  }
5025
- for (const mustache of findAllMustacheSyntax(html)) {
5026
- const selector = findStateSelector(mustache.expression, true);
5052
+ for (const item of [...findAllMustacheSyntax(html), ...findAllCommentBindings(html)]) {
5053
+ const selector = findStateSelector(item.expression, true);
5027
5054
  if (selector !== null) {
5028
5055
  diagnostics.push({
5029
5056
  code: WcsDiagnosticCode.NamedStateDeprecated,
5030
- start: mustache.exprStart + selector.start,
5031
- end: mustache.exprStart + selector.end,
5057
+ start: item.exprStart + selector.start,
5058
+ end: item.exprStart + selector.end,
5032
5059
  message: msgs.namedStatePathDeprecated(selector.name),
5033
- severity: "warning"
5060
+ severity: "error"
5034
5061
  });
5035
5062
  }
5036
5063
  }
5037
5064
  return diagnostics;
5038
5065
  }
5039
5066
 
5067
+ // src/service/mountAttrValidator.ts
5068
+ function findMountPathProblem(mountPath) {
5069
+ if (mountPath.length === 0) return "empty";
5070
+ for (const segment of mountPath.split(".")) {
5071
+ if (segment.length === 0) return "emptySegment";
5072
+ if (segment === "*") return "wildcard";
5073
+ if (segment.includes("$") || segment.includes("#") || segment.includes("@")) return "reserved";
5074
+ }
5075
+ return null;
5076
+ }
5077
+ function validateMountAttributes(html, stateTagName = "wcs-state", locale3) {
5078
+ const msgs = getMessages(locale3);
5079
+ const diagnostics = [];
5080
+ for (const element of parseWcsStateElements(html, stateTagName)) {
5081
+ if (element.mountPath === null) continue;
5082
+ const problem = findMountPathProblem(element.mountPath);
5083
+ if (problem === null) continue;
5084
+ const tagText = html.slice(element.tagStart, element.tagEnd);
5085
+ const match = /(?:^|\s)mount\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tagText);
5086
+ if (match === null) continue;
5087
+ const value = match[1] ?? match[2] ?? match[3] ?? "";
5088
+ const quoted = match[1] !== void 0 || match[2] !== void 0;
5089
+ const valueEnd = element.tagStart + match.index + match[0].length - (quoted ? 1 : 0);
5090
+ const attrStart = element.tagStart + match.index + (/^\s/.test(match[0]) ? 1 : 0);
5091
+ diagnostics.push({
5092
+ code: WcsDiagnosticCode.MountPathInvalid,
5093
+ start: value.length === 0 ? attrStart : valueEnd - value.length,
5094
+ end: valueEnd + (quoted && value.length === 0 ? 1 : 0),
5095
+ message: msgs.mountPathInvalid(problem, element.mountPath),
5096
+ severity: "error"
5097
+ });
5098
+ }
5099
+ return diagnostics;
5100
+ }
5101
+
5040
5102
  // ../state/dist/parser.esm.js
5041
5103
  var DELIMITER2 = ".";
5042
5104
  var WILDCARD2 = "*";
@@ -5044,7 +5106,6 @@ var MAX_WILDCARD_DEPTH2 = 128;
5044
5106
  var BINDING_SEPARATOR2 = ";";
5045
5107
  var PROP_VALUE_SEPARATOR2 = ":";
5046
5108
  var MODIFIER_SEPARATOR2 = "#";
5047
- var STATE_NAME_SEPARATOR3 = "@";
5048
5109
  var FILTER_SEPARATOR2 = "|";
5049
5110
  var ELSE_KEYWORD2 = "else";
5050
5111
  var SPREAD_PROP2 = "...";
@@ -5900,11 +5961,12 @@ function parseStatePart(statePart) {
5900
5961
  } else {
5901
5962
  stateAndPath = statePart.trim();
5902
5963
  }
5903
- if (stateAndPath.indexOf(STATE_NAME_SEPARATOR3) !== -1) ;
5904
- const [statePathName, stateName = "default"] = stateAndPath.split(STATE_NAME_SEPARATOR3).map(trimFn);
5964
+ if (stateAndPath.indexOf("@") !== -1) {
5965
+ raiseError2(`"${stateAndPath}": the "@name" selector was removed in v2 \u2014 there is a single state tree. Mount the named state onto the tree (<wcs-state mount="...">) and read it by its path prefix instead.`);
5966
+ }
5967
+ const statePathName = stateAndPath;
5905
5968
  const pathInfo = getPathInfo(statePathName);
5906
5969
  return {
5907
- stateName,
5908
5970
  statePathName,
5909
5971
  statePathInfo: pathInfo,
5910
5972
  outFilters: filters
@@ -5927,7 +5989,6 @@ function parseBindTextsForElement(bindText) {
5927
5989
  propModifiers: [],
5928
5990
  statePathName: "#else",
5929
5991
  statePathInfo: pathInfo,
5930
- stateName: "",
5931
5992
  inFilters: [],
5932
5993
  outFilters: [],
5933
5994
  bindingType: "else"
@@ -6027,24 +6088,18 @@ function parseEmbeddedTextWithPositions(expression) {
6027
6088
  error = e.message;
6028
6089
  }
6029
6090
  if (parsed === null) {
6030
- return { exprRange, exprText: expression, parsed, error, propRange: null, pathRange: null, stateNameRange: null };
6091
+ return { exprRange, exprText: expression, parsed, error, propRange: null, pathRange: null };
6031
6092
  }
6032
6093
  const firstPipe = expression.indexOf(delimiters.filter);
6033
6094
  const pathScopeEnd = firstPipe === -1 ? expression.length : firstPipe;
6034
6095
  const pathLocal = locate(expression, parsed.statePathName, 0, pathScopeEnd);
6035
- let stateNameLocal = null;
6036
- const at = expression.indexOf(delimiters.stateName);
6037
- if (at !== -1 && at < pathScopeEnd) {
6038
- stateNameLocal = locate(expression, parsed.stateName, at + 1, pathScopeEnd);
6039
- }
6040
6096
  return {
6041
6097
  exprRange,
6042
6098
  exprText: expression,
6043
6099
  parsed,
6044
6100
  error,
6045
6101
  propRange: null,
6046
- pathRange: pathLocal,
6047
- stateNameRange: stateNameLocal
6102
+ pathRange: pathLocal
6048
6103
  };
6049
6104
  }
6050
6105
  function parseBindTextWithPositions(bindText) {
@@ -6066,23 +6121,18 @@ function parseBindTextWithPositions(bindText) {
6066
6121
  error = e.message;
6067
6122
  }
6068
6123
  if (parsed === null) {
6069
- results.push({ exprRange, exprText: expr, parsed, error, propRange: null, pathRange: null, stateNameRange: null });
6124
+ results.push({ exprRange, exprText: expr, parsed, error, propRange: null, pathRange: null });
6070
6125
  continue;
6071
6126
  }
6072
6127
  const colon = expr.indexOf(delimiters.propValue);
6073
6128
  const propEndLimit = colon === -1 ? expr.length : colon;
6074
6129
  const propLocal = locate(expr, parsed.propName, 0, propEndLimit);
6075
6130
  let pathLocal = null;
6076
- let stateNameLocal = null;
6077
6131
  if (colon !== -1) {
6078
6132
  const stateBase = colon + 1;
6079
6133
  const firstPipe = expr.indexOf(delimiters.filter, stateBase);
6080
6134
  const pathScopeEnd = firstPipe === -1 ? expr.length : firstPipe;
6081
6135
  pathLocal = locate(expr, parsed.statePathName, stateBase, pathScopeEnd);
6082
- const at = expr.indexOf(delimiters.stateName, stateBase);
6083
- if (at !== -1 && at < pathScopeEnd) {
6084
- stateNameLocal = locate(expr, parsed.stateName, at + 1, pathScopeEnd);
6085
- }
6086
6136
  }
6087
6137
  const lift = (range) => range === null ? null : { start: exprStart + range.start, end: exprStart + range.end };
6088
6138
  results.push({
@@ -6091,17 +6141,13 @@ function parseBindTextWithPositions(bindText) {
6091
6141
  parsed,
6092
6142
  error,
6093
6143
  propRange: lift(propLocal),
6094
- pathRange: lift(pathLocal),
6095
- stateNameRange: lift(stateNameLocal)
6144
+ pathRange: lift(pathLocal)
6096
6145
  });
6097
6146
  }
6098
6147
  return results;
6099
6148
  }
6100
6149
 
6101
6150
  // src/core/index/referenceIndex.ts
6102
- function keyOf(stateName, path) {
6103
- return `${stateName}\0${path}`;
6104
- }
6105
6151
  function buildReferenceIndex(html, options = {}) {
6106
6152
  clearParserCaches();
6107
6153
  const bindAttribute = options.bindAttribute ?? "data-wcs";
@@ -6119,13 +6165,11 @@ function buildReferenceIndex(html, options = {}) {
6119
6165
  occurrences.push({
6120
6166
  source: "attribute",
6121
6167
  kind: binding.parsed.propSegments[0] === "eventToken" ? "eventToken" : "path",
6122
- stateName: binding.parsed.stateName,
6123
6168
  path: binding.parsed.statePathName,
6124
6169
  pathRange: lift(binding.pathRange),
6125
6170
  exprRange: lift(binding.exprRange),
6126
6171
  propName: binding.parsed.propName,
6127
6172
  propRange: binding.propRange === null ? null : lift(binding.propRange),
6128
- stateNameRange: binding.stateNameRange === null ? null : lift(binding.stateNameRange),
6129
6173
  bindingType: binding.parsed.bindingType
6130
6174
  });
6131
6175
  }
@@ -6148,22 +6192,21 @@ function buildReferenceIndex(html, options = {}) {
6148
6192
  occurrences.push({
6149
6193
  source: match.kind,
6150
6194
  kind: "path",
6151
- stateName: binding.parsed.stateName,
6152
6195
  path: binding.parsed.statePathName,
6153
6196
  pathRange: shift(binding.pathRange),
6154
6197
  exprRange: { start: match.exprStart, end: match.exprEnd },
6155
6198
  propName: null,
6156
6199
  propRange: null,
6157
- stateNameRange: binding.stateNameRange === null ? null : shift(binding.stateNameRange),
6158
6200
  bindingType: "text"
6159
6201
  });
6160
6202
  }
6161
6203
  const declarations = [];
6162
6204
  for (const block of parseWcsScriptBlocks(html, stateTagName)) {
6205
+ const prefix = block.mountPath === null ? "" : block.mountPath + ".";
6163
6206
  for (const span of analyzeDeclarationSpans(block.content)) {
6207
+ if (prefix !== "" && span.name.startsWith("$")) continue;
6164
6208
  declarations.push({
6165
- stateName: block.stateName,
6166
- name: span.name,
6209
+ name: prefix + span.name,
6167
6210
  kind: span.kind,
6168
6211
  range: { start: block.contentStart + span.start, end: block.contentStart + span.end }
6169
6212
  });
@@ -6172,7 +6215,7 @@ function buildReferenceIndex(html, options = {}) {
6172
6215
  const byPath = /* @__PURE__ */ new Map();
6173
6216
  for (const occurrence of occurrences) {
6174
6217
  if (occurrence.kind !== "path") continue;
6175
- const key = keyOf(occurrence.stateName, occurrence.path);
6218
+ const key = occurrence.path;
6176
6219
  const list = byPath.get(key);
6177
6220
  if (list === void 0) {
6178
6221
  byPath.set(key, [occurrence]);
@@ -6182,22 +6225,22 @@ function buildReferenceIndex(html, options = {}) {
6182
6225
  }
6183
6226
  const declarationByName = /* @__PURE__ */ new Map();
6184
6227
  for (const declaration of declarations) {
6185
- const key = keyOf(declaration.stateName, declaration.name);
6228
+ const key = declaration.name;
6186
6229
  if (!declarationByName.has(key)) declarationByName.set(key, declaration);
6187
6230
  }
6188
6231
  return {
6189
6232
  occurrences,
6190
6233
  declarations,
6191
6234
  problems,
6192
- referencesOf(stateName, path) {
6193
- return (byPath.get(keyOf(stateName, path)) ?? []).slice();
6235
+ referencesOf(path) {
6236
+ return (byPath.get(path) ?? []).slice();
6194
6237
  },
6195
- declarationOf(stateName, path) {
6196
- const exact = declarationByName.get(keyOf(stateName, path));
6238
+ declarationOf(path) {
6239
+ const exact = declarationByName.get(path);
6197
6240
  if (exact !== void 0) return exact;
6198
6241
  const firstSegment = path.split(".")[0];
6199
6242
  if (firstSegment === path) return null;
6200
- return declarationByName.get(keyOf(stateName, firstSegment)) ?? null;
6243
+ return declarationByName.get(firstSegment) ?? null;
6201
6244
  },
6202
6245
  occurrenceAt(offset) {
6203
6246
  for (const occurrence of occurrences) {
@@ -6442,8 +6485,9 @@ function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3
6442
6485
  for (const block of blocks) {
6443
6486
  const callback = analyzeCallableBodies(block.content).find((entry) => entry.name === STATE_UPDATED_CALLBACK && entry.kind === "method");
6444
6487
  if (callback === void 0) continue;
6445
- const declared = new Set(analyzeStatePaths(block.content, block.stateName).map((p) => p.path));
6446
- const bound = boundPaths.get(block.stateName) ?? /* @__PURE__ */ new Set();
6488
+ if (block.mountPath !== null) continue;
6489
+ const declared = new Set(analyzeStatePaths(block.content).map((p) => p.path));
6490
+ const bound = boundPaths;
6447
6491
  const body = blankComments(callback.body);
6448
6492
  PATH_TEST_LITERAL.lastIndex = 0;
6449
6493
  let match;
@@ -6466,27 +6510,18 @@ function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3
6466
6510
  return out;
6467
6511
  }
6468
6512
  function collectBoundPaths(html, stateTagName, bindAttrName) {
6469
- const byState = /* @__PURE__ */ new Map();
6470
- const add = (stateName, path) => {
6471
- let set = byState.get(stateName);
6472
- if (set === void 0) {
6473
- set = /* @__PURE__ */ new Set();
6474
- byState.set(stateName, set);
6475
- }
6476
- set.add(path);
6477
- };
6513
+ const bound = /* @__PURE__ */ new Set();
6478
6514
  const index = buildReferenceIndex(html, { bindAttribute: bindAttrName, stateTagName });
6479
6515
  for (const occurrence of index.occurrences) {
6480
- add(occurrence.stateName, occurrence.path);
6516
+ bound.add(occurrence.path);
6481
6517
  if (!occurrence.path.startsWith(".")) continue;
6482
6518
  const forPath = getInnermostForPath(html, occurrence.pathRange.start, bindAttrName);
6483
6519
  if (forPath === null || forPath.startsWith(".")) continue;
6484
- add(
6485
- occurrence.stateName,
6520
+ bound.add(
6486
6521
  occurrence.path === "." ? `${forPath}.*` : `${forPath}.*.${occurrence.path.slice(1)}`
6487
6522
  );
6488
6523
  }
6489
- return byState;
6524
+ return bound;
6490
6525
  }
6491
6526
  function validateSemantics(html, stateTagName = "wcs-state", locale3, bindAttrName = "data-wcs") {
6492
6527
  const out = [];
@@ -6671,8 +6706,8 @@ function parseJsonWithSpans(text) {
6671
6706
  }
6672
6707
 
6673
6708
  // src/core/sidecar/types.ts
6674
- var SUPPORTED_SCHEMA_VERSION = 1;
6675
- var SUPPORTED_NAMESPACE_VERSION = 1;
6709
+ var SUPPORTED_SCHEMA_VERSION = 2;
6710
+ var SUPPORTED_NAMESPACE_VERSION = 2;
6676
6711
 
6677
6712
  // src/core/sidecar/loader.ts
6678
6713
  var NAMESPACE_KEYS = ["wcstack.types", "wcstack.async", "wcstack.platformCapabilities", "wcstack.application"];
@@ -6709,10 +6744,11 @@ function loadManifest(artifact) {
6709
6744
  return { artifact, manifest: null, ctx, spans: parsed.spans };
6710
6745
  }
6711
6746
  if (obj.schemaVersion !== SUPPORTED_SCHEMA_VERSION) {
6747
+ const migration = obj.schemaVersion === 1 ? ` schemaVersion 1 (states[name]) predates v2's single state tree \u2014 regenerate with \`wcs-schema emit\`.` : "";
6712
6748
  ctx.add(
6713
6749
  WcsDiagnosticCode.ManifestSchemaVersion,
6714
6750
  pointer("schemaVersion"),
6715
- `Unsupported schemaVersion ${obj.schemaVersion}; this reader supports ${SUPPORTED_SCHEMA_VERSION}.`,
6751
+ `Unsupported schemaVersion ${obj.schemaVersion}; this reader supports ${SUPPORTED_SCHEMA_VERSION}.${migration}`,
6716
6752
  "error"
6717
6753
  );
6718
6754
  return { artifact, manifest: null, ctx, spans: parsed.spans };
@@ -6761,9 +6797,8 @@ function resolvePackageContracts(loaded) {
6761
6797
  const collided = /* @__PURE__ */ new Set();
6762
6798
  const firstSource = /* @__PURE__ */ new Map();
6763
6799
  const filterOwner = /* @__PURE__ */ new Map();
6764
- const stateOwner = /* @__PURE__ */ new Map();
6765
- const stateWinners = /* @__PURE__ */ new Map();
6766
- const collidedStates = /* @__PURE__ */ new Set();
6800
+ let schemaOwner;
6801
+ let schemaWinner;
6767
6802
  let hasApplicationArtifact = false;
6768
6803
  for (const lm of loaded) {
6769
6804
  if (lm.manifest === null) continue;
@@ -6819,26 +6854,23 @@ function resolvePackageContracts(loaded) {
6819
6854
  );
6820
6855
  }
6821
6856
  }
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;
6857
+ if (lm.manifest.kind === "application" && application?.stateSchema !== void 0) {
6858
+ const schema = application.stateSchema;
6859
+ if (schema !== null && typeof schema === "object" && !Array.isArray(schema)) {
6860
+ if (schemaOwner === void 0) {
6861
+ schemaOwner = lm.artifact.source;
6862
+ schemaWinner = schema;
6863
+ } else {
6864
+ schemaWinner = void 0;
6865
+ ctxFor(lm).add(
6866
+ WcsDiagnosticCode.ManifestStateCollision,
6867
+ pointer("manifestExtensions", "wcstack.application", "stateSchema"),
6868
+ `Multiple application artifacts declare a stateSchema (also in "${schemaOwner}"); neither is used.`,
6869
+ "error",
6870
+ void 0,
6871
+ true
6872
+ );
6831
6873
  }
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
6874
  }
6843
6875
  }
6844
6876
  }
@@ -6847,7 +6879,7 @@ function resolvePackageContracts(loaded) {
6847
6879
  const kept = diags.filter((d) => !(d.code === WcsDiagnosticCode.ManifestOverride && d.tag !== void 0 && collided.has(d.tag)));
6848
6880
  if (kept.length > 0) diagnosticsBySource.set(source, kept);
6849
6881
  }
6850
- return { tags: winners, applicationStates: stateWinners, hasApplicationArtifact, diagnosticsBySource };
6882
+ return { tags: winners, applicationSchema: schemaWinner, hasApplicationArtifact, diagnosticsBySource };
6851
6883
  }
6852
6884
 
6853
6885
  // src/core/sidecar/discover.ts
@@ -6859,22 +6891,19 @@ function discoverApplicationManifest(fileReader) {
6859
6891
  const text = fileReader(relativePath);
6860
6892
  if (text === void 0) continue;
6861
6893
  const loaded = loadManifest({ text, source: relativePath });
6862
- return { relativePath, text, loaded, states: applicationStatesOf(loaded) };
6894
+ return { relativePath, text, loaded, schema: applicationSchemaOf(loaded) };
6863
6895
  }
6864
6896
  return void 0;
6865
6897
  }
6866
- function applicationStatesOf(loaded) {
6867
- const states = /* @__PURE__ */ new Map();
6898
+ function applicationSchemaOf(loaded) {
6868
6899
  const manifest = loaded.manifest;
6869
- if (manifest === null || manifest.kind !== "application") return states;
6900
+ if (manifest === null || manifest.kind !== "application") return void 0;
6870
6901
  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
- }
6902
+ const schema = application?.stateSchema;
6903
+ if (schema !== null && typeof schema === "object" && !Array.isArray(schema)) {
6904
+ return schema;
6876
6905
  }
6877
- return states;
6906
+ return void 0;
6878
6907
  }
6879
6908
  function joinRelativeSource(htmlSource, relativePath) {
6880
6909
  const sepIndex = Math.max(htmlSource.lastIndexOf("/"), htmlSource.lastIndexOf("\\"));
@@ -6897,10 +6926,10 @@ function validateDocument(text, options = {}) {
6897
6926
  const stateTagName = options.stateTagName ?? "wcs-state";
6898
6927
  const locale3 = options.locale;
6899
6928
  const fileReader = options.fileReader;
6900
- const applicationStates = options.applicationStates ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.states : void 0);
6929
+ const applicationSchema = options.applicationSchema ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.schema : void 0);
6901
6930
  const out = [];
6902
- out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader, applicationStates));
6903
- out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader, applicationStates));
6931
+ out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader, applicationSchema));
6932
+ out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader, applicationSchema));
6904
6933
  out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
6905
6934
  out.push(...validateAriaAttributes(text, bindAttribute, locale3));
6906
6935
  out.push(...validateDocumentEnv(text, locale3));
@@ -6908,6 +6937,7 @@ function validateDocument(text, options = {}) {
6908
6937
  out.push(...validateArrayMutations(text, stateTagName, locale3));
6909
6938
  out.push(...validateWatchDeclarations(text, stateTagName, locale3));
6910
6939
  out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
6940
+ out.push(...validateMountAttributes(text, stateTagName, locale3));
6911
6941
  for (const d of validateStateTypes(text, stateTagName, locale3)) {
6912
6942
  out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
6913
6943
  }
@@ -6985,11 +7015,10 @@ function validateLoadedSchemas(loaded) {
6985
7015
  validateComponentSchemas(tag, component, loaded.ctx);
6986
7016
  }
6987
7017
  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 ?? {});
7018
+ const stateSchema = application?.stateSchema;
7019
+ if (stateSchema !== void 0 && stateSchema !== null && typeof stateSchema === "object" && !Array.isArray(stateSchema)) {
7020
+ const ptr = pointer("manifestExtensions", "wcstack.application", "stateSchema");
7021
+ validateSchemaSubset(stateSchema, ptr, loaded.ctx, stateSchema.$defs ?? {});
6993
7022
  }
6994
7023
  }
6995
7024
  function validateComponentSchemas(tag, component, ctx) {
@@ -7041,7 +7070,7 @@ function validateManifestSet(input) {
7041
7070
  diagnostics: sortDiagnostics(all),
7042
7071
  byArtifact: sortedByArtifact,
7043
7072
  resolvedTags,
7044
- resolvedStates: resolved.applicationStates,
7073
+ resolvedSchema: resolved.applicationSchema,
7045
7074
  hasApplicationArtifact: resolved.hasApplicationArtifact
7046
7075
  };
7047
7076
  }
@@ -7055,7 +7084,8 @@ function runValidation(inputs, options = {}) {
7055
7084
  const diagnosticsBySource = /* @__PURE__ */ new Map();
7056
7085
  const textBySource = new Map(inputs.map((i) => [i.source, i.text]));
7057
7086
  const manifestInputs = inputs.filter((i) => i.kind === "manifest");
7058
- let explicitStates;
7087
+ let explicitSchema;
7088
+ let haveExplicitApplication = false;
7059
7089
  if (manifestInputs.length > 0) {
7060
7090
  const result = validateManifestSet({
7061
7091
  artifacts: manifestInputs.map((m) => ({ text: m.text, source: m.source })),
@@ -7064,14 +7094,17 @@ function runValidation(inputs, options = {}) {
7064
7094
  for (const input of manifestInputs) {
7065
7095
  diagnosticsBySource.set(input.source, result.byArtifact.get(input.source) ?? []);
7066
7096
  }
7067
- if (result.hasApplicationArtifact) explicitStates = result.resolvedStates;
7097
+ if (result.hasApplicationArtifact) {
7098
+ haveExplicitApplication = true;
7099
+ explicitSchema = result.resolvedSchema;
7100
+ }
7068
7101
  }
7069
7102
  for (const input of inputs) {
7070
7103
  if (input.kind !== "html") continue;
7071
- let applicationStates = explicitStates;
7072
- if (applicationStates === void 0 && input.fileReader !== void 0) {
7104
+ let applicationSchema = explicitSchema;
7105
+ if (!haveExplicitApplication && input.fileReader !== void 0) {
7073
7106
  const discovered = discoverApplicationManifest(input.fileReader);
7074
- applicationStates = discovered?.states ?? /* @__PURE__ */ new Map();
7107
+ applicationSchema = discovered?.schema;
7075
7108
  if (discovered !== void 0) {
7076
7109
  const source = joinRelativeSource(input.source, discovered.relativePath);
7077
7110
  if (!diagnosticsBySource.has(source)) {
@@ -7083,7 +7116,7 @@ function runValidation(inputs, options = {}) {
7083
7116
  const docOptions = {
7084
7117
  ...options,
7085
7118
  ...input.fileReader !== void 0 ? { fileReader: input.fileReader } : {},
7086
- ...applicationStates !== void 0 ? { applicationStates } : {}
7119
+ ...applicationSchema !== void 0 ? { applicationSchema } : {}
7087
7120
  };
7088
7121
  diagnosticsBySource.set(input.source, validateDocument(input.text, docOptions));
7089
7122
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/lint",
3
- "version": "1.33.0",
3
+ "version": "2.0.0",
4
4
  "description": "Static-contract validator CLI (wcs-validate) for wcstack data-wcs bindings and wcstack.manifest.json sidecars. Thin npm wrapper around the wcstack-intellisense validator core.",
5
5
  "type": "module",
6
6
  "bin": {