@wcstack/typescript 4.3.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,8 +53,12 @@ var WcsDiagnosticCode = {
53
53
  // --- path / type resolution against a stateSchema ---
54
54
  PathNonexistent: "wcs/path-nonexistent",
55
55
  PathTypeMismatch: "wcs/path-type-mismatch",
56
+ // 書き戻す束縛(`value:` / `checked:` / `radio:` / `checkbox:`、`#ro` なし)が、setter の無い getter を指す。ランタイムは
57
+ // 入力のたびに `"<path>" is a getter without a setter` を投げる(code なし — 7GUIs の評価の Temperature)。error。
56
58
  PathReadonly: "wcs/path-readonly",
57
59
  PathReservedName: "wcs/path-reserved-name",
60
+ // lint の限界の告知(作者の誤りではない): `export default { … }` のトップレベルの spread が持ち込むキーは読めないので、
61
+ // stateSchema の無い state のパスは検査しない — それを spread の上で 1 回だけ言う。info。
58
62
  PathDynamicUnknown: "wcs/path-dynamic-unknown",
59
63
  // --- existing binding-expression validators (retrofitted) ---
60
64
  FilterUnknown: "wcs/filter-unknown",
@@ -65,6 +69,17 @@ var WcsDiagnosticCode = {
65
69
  BindingTypeExpectation: "wcs/binding-type-expectation",
66
70
  TokenUndeclared: "wcs/token-undeclared",
67
71
  TokenMisconfigured: "wcs/token-misconfigured",
72
+ // 宣言したコマンドトークンを emit する(スクリプトの `.emit()`・マークアップの `on*: $command.x`)のに、どの
73
+ // `command.<method>:` 束縛にも結ばれていない。emit は誰にも届かず、ランタイムは何も言わない(7GUIs の評価)。warning
74
+ // (ページの外 — 別ファイルのルートのテンプレート — で束ねる形は見えない)。
75
+ CommandTokenUnbound: "wcs/command-token-unbound",
76
+ // state が宣言していないトップレベルのキーへの書き込み(`this.selectedID = …` と `selectedId`)。新しいキーができる
77
+ // だけで、宣言したキーの束縛は変わらない。ランタイムは何も言わない。近い名前(打ち間違い)があるときだけ、warning。
78
+ WritePathMissing: "wcs/write-path-missing",
79
+ // getter・メソッドの中で読む state のパス(`this["items.*.amout"]`・`$getAll("items.*.amout")`・`this.totl`)が宣言に無い。
80
+ // 入れ子のパスの読みは undefined になり、そこから計算した金額が黙って違う(確定申告の下書きツールの評価)。トップレベルの
81
+ // キーは読んだ時点で throw する。近い名前(打ち間違い)があるときだけ、warning。ランタイムの diagnostics も同じ code。
82
+ ReadPathMissing: "wcs/read-path-missing",
68
83
  NestedAssign: "wcs/nested-assign",
69
84
  // --- 意味論(構文・存在検査では捕まらない取り違え。service/semanticValidator.ts) ---
70
85
  // `$getAll` / `$setAll` / `$resolve` の添字の本数がパスの `*` の本数と噛み合わない。
@@ -163,6 +178,11 @@ var WcsDiagnosticCode = {
163
178
  // 未知メンバーへのバインド(プロパティ / command. / eventToken. キー)。4.0 のランタイムは #38 / #1203 /
164
179
  // #1204 で拒み、root では初期化が、行では束縛が失敗する(3.x の「黙って無視」ではない)。error。
165
180
  TagMemberUnknown: "wcs/tag-member-unknown",
181
+ // `semantics: "handle"` の出力(`<wcs-camera>` の `streamReady` = 生の MediaStream)を、値としてパスに
182
+ // 束ねた。プロトコルは「値の置き場ではなく ref / callback で受ける」と定める(protocol/wc-bindable.ts)。
183
+ // ランタイムは書くので動くが、スナップショット・DevTools・SSR に載らないオブジェクトが state に入る
184
+ // (docs/seam-apps.md、state-field-report)。warning。5.0 でランタイムが拒むかは誤りの目録の議題。
185
+ HandleBoundAsValue: "wcs/handle-bound-as-value",
166
186
  // タグのメンバー名が "on" で始まる(`once` 等)のに先頭ドット無しで束縛した: ランタイムはイベント束縛にして
167
187
  // "ce" イベントを待ち、値は届かない。明示のプロパティ形 `.once:` を提案する(@wcstack/state 3.1・要件 B5 / 3.x 計画 D36)
168
188
  OnPrefixedMember: "wcs/on-prefixed-member",
@@ -448,8 +468,8 @@ function derefUnion(node, rootDefs) {
448
468
  }
449
469
  return { kind: "ok", nodes: out };
450
470
  }
451
- function firstDefined(nodes, pick) {
452
- for (const n of nodes) {
471
+ function firstDefined(nodes2, pick) {
472
+ for (const n of nodes2) {
453
473
  const v = pick(n);
454
474
  if (v !== void 0) return v;
455
475
  }
@@ -1012,8 +1032,8 @@ function checkPathLikeGetPathInfo(path) {
1012
1032
  function recursionUnsupported(path) {
1013
1033
  raise(1101, [path]);
1014
1034
  }
1015
- function parseStatePart(statePart) {
1016
- const [stateAndPath, filters] = splitFilters(statePart, "output", cacheFilterInfos);
1035
+ function parseStatePart(statePart2) {
1036
+ const [stateAndPath, filters] = splitFilters(statePart2, "output", cacheFilterInfos);
1017
1037
  if (stateAndPath.includes("@")) {
1018
1038
  raise(32, [stateAndPath]);
1019
1039
  }
@@ -1022,7 +1042,7 @@ function parseStatePart(statePart) {
1022
1042
  const body = isLoopRelative ? statePathName.slice(DELIMITER.length) : statePathName;
1023
1043
  const hasEmptySegment = body.length > 0 && body.split(DELIMITER).some((segment) => segment.length === 0);
1024
1044
  if (hasEmptySegment || !isLoopRelative && body.length === 0) {
1025
- raise(119, [statePart]);
1045
+ raise(119, [statePart2]);
1026
1046
  }
1027
1047
  checkPathLikeGetPathInfo(statePathName);
1028
1048
  return {
@@ -1584,7 +1604,7 @@ function parseBindTextsForElement(bindText) {
1584
1604
  raise(101, [bindText2]);
1585
1605
  }
1586
1606
  const propPart = bindText2.slice(0, separatorIndex).trim();
1587
- const statePart = bindText2.slice(separatorIndex + 1).trim();
1607
+ const statePart2 = bindText2.slice(separatorIndex + 1).trim();
1588
1608
  const keyword = propPart.split(MODIFIER_SEPARATOR)[0].split(FILTER_SEPARATOR)[0].trim();
1589
1609
  if (KEYWORDS_WITHOUT_MODIFIERS.has(keyword)) {
1590
1610
  if (keyword !== propPart) {
@@ -1592,20 +1612,20 @@ function parseBindTextsForElement(bindText) {
1592
1612
  }
1593
1613
  let stateResult2;
1594
1614
  if (keyword === ELSE_KEYWORD) {
1595
- if (statePart.length > 0) {
1615
+ if (statePart2.length > 0) {
1596
1616
  raise(103, [bindText2]);
1597
1617
  }
1598
1618
  stateResult2 = { statePathName: "#else", outFilters: [] };
1599
1619
  } else if (keyword === SPREAD_PROP) {
1600
- if (statePart.length === 0) {
1620
+ if (statePart2.length === 0) {
1601
1621
  raise(104, [bindText2]);
1602
1622
  }
1603
- stateResult2 = parseStatePart(statePart);
1623
+ stateResult2 = parseStatePart(statePart2);
1604
1624
  if (stateResult2.outFilters.length > 0) {
1605
1625
  raise(105, [bindText2]);
1606
1626
  }
1607
1627
  } else {
1608
- stateResult2 = parseStatePart(statePart);
1628
+ stateResult2 = parseStatePart(statePart2);
1609
1629
  if (keyword === "for" && stateResult2.outFilters.length > 0) {
1610
1630
  raise(121, [bindText2]);
1611
1631
  }
@@ -1622,7 +1642,7 @@ function parseBindTextsForElement(bindText) {
1622
1642
  if (keyword === CLASS_NAMESPACE || keyword === ATTR_NAMESPACE) {
1623
1643
  raise(122, [bindText2, keyword]);
1624
1644
  }
1625
- const stateResult = parseStatePart(statePart);
1645
+ const stateResult = parseStatePart(statePart2);
1626
1646
  const propResult = parsePropPart(propPart);
1627
1647
  const [head] = propResult.propSegments;
1628
1648
  if (head === "" && propResult.propSegments.length > 1) {
@@ -1868,6 +1888,9 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
1868
1888
  const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
1869
1889
  const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
1870
1890
  const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
1891
+ const contentAt = pos + 1 + stateTagName.length;
1892
+ const srcSpan = extractAttributeSpan(wcsMatch.tagContent, "src", contentAt);
1893
+ const jsonSpan = extractAttributeSpan(wcsMatch.tagContent, "json", contentAt);
1871
1894
  const tagStart = pos;
1872
1895
  const tagEnd = wcsMatch.end;
1873
1896
  pos = wcsMatch.end;
@@ -1911,7 +1934,18 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
1911
1934
  wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
1912
1935
  }
1913
1936
  }
1914
- elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
1937
+ elements.push({
1938
+ mountPath,
1939
+ bindComponent,
1940
+ jsonAttr,
1941
+ stateAttr,
1942
+ srcAttr,
1943
+ scriptBlocks,
1944
+ tagStart,
1945
+ tagEnd,
1946
+ ...srcSpan !== null ? { srcSpan } : {},
1947
+ ...jsonSpan !== null ? { jsonSpan } : {}
1948
+ });
1915
1949
  pos = wcsEnd;
1916
1950
  if (wcsCloseIdx !== -1) pos = afterCloseTag(html, wcsCloseIdx);
1917
1951
  }
@@ -2046,13 +2080,13 @@ function findCloseTag(html, startPos, tagName, lower2 = asciiLowerCase(html)) {
2046
2080
  return -1;
2047
2081
  }
2048
2082
  function parseAttributeNames(tagContent) {
2049
- const names = /* @__PURE__ */ new Set();
2083
+ const names2 = /* @__PURE__ */ new Set();
2050
2084
  const attribute = /([^\s"'<>/=]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'<>`=]+))?/g;
2051
2085
  let match;
2052
2086
  while ((match = attribute.exec(tagContent)) !== null) {
2053
- names.add(match[1].toLowerCase());
2087
+ names2.add(match[1].toLowerCase());
2054
2088
  }
2055
- return names;
2089
+ return names2;
2056
2090
  }
2057
2091
  function extractAttribute(tagContent, attrName) {
2058
2092
  const regex = new RegExp(
@@ -2063,6 +2097,16 @@ function extractAttribute(tagContent, attrName) {
2063
2097
  if (!match) return null;
2064
2098
  return match[1] ?? match[2] ?? match[3] ?? null;
2065
2099
  }
2100
+ function extractAttributeSpan(tagContent, attrName, at2) {
2101
+ const regex = new RegExp(`(^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`, "i");
2102
+ const match = regex.exec(tagContent);
2103
+ if (match === null) return null;
2104
+ const start = at2 + match.index + match[1].length;
2105
+ const end = at2 + match.index + match[0].length;
2106
+ const value = match[2] ?? match[3] ?? match[4] ?? "";
2107
+ const valueEnd = match[4] === void 0 ? end - 1 : end;
2108
+ return { start, end, valueStart: valueEnd - value.length, valueEnd };
2109
+ }
2066
2110
  var IGNORE_VOID_ELEMENTS = /* @__PURE__ */ new Set([
2067
2111
  "area",
2068
2112
  "base",
@@ -3417,17 +3461,25 @@ function hasDefaultExportObject(scriptContent) {
3417
3461
  return locateDefaultExportObject(scriptContent) !== null;
3418
3462
  }
3419
3463
  function hasTopLevelSpread(scriptContent) {
3464
+ return findTopLevelSpread(scriptContent) !== null;
3465
+ }
3466
+ function findTopLevelSpread(scriptContent) {
3420
3467
  const root = locateDefaultExportObject(scriptContent);
3421
- if (!root) return false;
3468
+ if (!root) return null;
3422
3469
  const scan = maskCommentsAndStrings(root.content);
3423
3470
  let depth = 0;
3471
+ let start = -1;
3424
3472
  for (let i = 0; i < scan.length; i++) {
3425
3473
  const ch = scan[i];
3426
3474
  if (ch === "(" || ch === "[" || ch === "{") depth++;
3427
3475
  else if (ch === ")" || ch === "]" || ch === "}") depth--;
3428
- else if (depth === 0 && ch === "." && scan.startsWith("...", i)) return true;
3476
+ else if (depth === 0 && start < 0 && ch === "." && scan.startsWith("...", i)) start = i;
3477
+ else if (depth === 0 && start >= 0 && ch === ",") return spreadRange(root.start, scan, start, i);
3429
3478
  }
3430
- return false;
3479
+ return start < 0 ? null : spreadRange(root.start, scan, start, scan.length);
3480
+ }
3481
+ function spreadRange(base, scan, start, stop) {
3482
+ return { start: base + start, end: base + start + scan.slice(start, stop).trimEnd().length };
3431
3483
  }
3432
3484
  function analyzeObjectEntries(scriptContent, key, allowEmptyKeys = false) {
3433
3485
  const root = locateDefaultExportObject(scriptContent);
@@ -3800,7 +3852,7 @@ function readBase(expr, scan, aliases, paths) {
3800
3852
  if (alias === null) return null;
3801
3853
  if (alias[1] !== "this") {
3802
3854
  const shape = aliases.get(alias[1]);
3803
- return shape === void 0 ? null : { shape, rest: alias[1].length };
3855
+ return { shape: shape ?? OPAQUE, rest: alias[1].length };
3804
3856
  }
3805
3857
  const segments = [];
3806
3858
  let i = 4;
@@ -3825,6 +3877,7 @@ function readBase(expr, scan, aliases, paths) {
3825
3877
  break;
3826
3878
  }
3827
3879
  const path = segments.join(".");
3880
+ if (path === "" && /^\s*\??\.\s*\$[$\w]*\s*\(/.test(scan.slice(4))) return { shape: OPAQUE, rest: 4 };
3828
3881
  if (path === "" || path.startsWith("$")) return null;
3829
3882
  return {
3830
3883
  shape: { isList: hasPath(paths, `${path}.*`), mirrors: [{ from: path, to: "" }], fields: [] },
@@ -3835,12 +3888,25 @@ function applyChain(expr, scan, start, initial) {
3835
3888
  let shape = initial;
3836
3889
  let i = start;
3837
3890
  while (scan.slice(i).trim() !== "") {
3891
+ if (shape.opaque && /^\s*\(/.test(scan.slice(i))) {
3892
+ const open2 = scan.indexOf("(", i);
3893
+ i = open2 + extractDelimitedContent(expr, scan, open2, "(", ")").length + 2;
3894
+ continue;
3895
+ }
3838
3896
  const call = /^\s*\??\.\s*([$\w]+)\s*\(/.exec(scan.slice(i));
3839
3897
  if (call === null) return null;
3840
3898
  const open = i + call[0].length - 1;
3841
3899
  const args = extractDelimitedContent(expr, scan, open, "(", ")");
3842
3900
  i = open + args.length + 2;
3843
3901
  const method = call[1];
3902
+ if (shape.opaque) {
3903
+ if (method === "map") {
3904
+ const mapped = mapRows(args, shape);
3905
+ if (mapped === null) return null;
3906
+ shape = mapped;
3907
+ }
3908
+ continue;
3909
+ }
3844
3910
  if (ROW_PRESERVING_METHODS.has(method)) {
3845
3911
  if (!shape.isList) return null;
3846
3912
  } else if (ROW_PICKING_METHODS.has(method)) {
@@ -3855,8 +3921,9 @@ function applyChain(expr, scan, start, initial) {
3855
3921
  return null;
3856
3922
  }
3857
3923
  }
3858
- return shape;
3924
+ return shape.opaque ? null : shape;
3859
3925
  }
3926
+ var OPAQUE = { isList: true, mirrors: [], fields: [], opaque: true };
3860
3927
  function pickRow(shape) {
3861
3928
  return {
3862
3929
  isList: false,
@@ -3867,7 +3934,7 @@ function pickRow(shape) {
3867
3934
  function mapRows(args, shape) {
3868
3935
  const [callback] = splitTopLevelElements(args);
3869
3936
  if (callback === void 0) return null;
3870
- const head = /^(?:async\s+)?(?:function\b[^(]*\(\s*([$\w]+)[^)]*\)|\(\s*([$\w]+)[^)]*\)|([$\w]+))\s*(=>)?\s*/.exec(callback);
3937
+ const head = /^(?:async\s+)?(?:function\b[^(]*\(\s*([$\w]+)?[^)]*\)|\(\s*([$\w]+)?[^)]*\)|([$\w]+))\s*(=>)?\s*/.exec(callback);
3871
3938
  if (head === null) return null;
3872
3939
  const param = head[1] ?? head[2] ?? head[3];
3873
3940
  const body = callback.slice(head[0].length);
@@ -4381,9 +4448,9 @@ function derefSchemaNodes(node, defs) {
4381
4448
  }
4382
4449
  return out;
4383
4450
  }
4384
- function schemaTypeHint(nodes) {
4451
+ function schemaTypeHint(nodes2) {
4385
4452
  const hints = /* @__PURE__ */ new Set();
4386
- for (const n of nodes) {
4453
+ for (const n of nodes2) {
4387
4454
  const types2 = typeof n.type === "string" ? [n.type] : Array.isArray(n.type) ? n.type : [];
4388
4455
  if (types2.length > 0) {
4389
4456
  for (const t of types2) {
@@ -4422,10 +4489,10 @@ function collectSchemaObjectPaths(node, prefix, paths, defs, depth) {
4422
4489
  }
4423
4490
  }
4424
4491
  function pushSchemaValuePaths(path, node, paths, defs, depth) {
4425
- const nodes = derefSchemaNodes(node, defs);
4426
- const typeHint = schemaTypeHint(nodes);
4492
+ const nodes2 = derefSchemaNodes(node, defs);
4493
+ const typeHint = schemaTypeHint(nodes2);
4427
4494
  paths.push(withHint({ path, kind: "data", fromSchema: true }, typeHint));
4428
- const items = nodes.map((n) => n.items).find((i) => i !== void 0 && i !== null && typeof i === "object");
4495
+ const items = nodes2.map((n) => n.items).find((i) => i !== void 0 && i !== null && typeof i === "object");
4429
4496
  const isArray2 = items !== void 0 || (typeHint?.split("|").includes("array") ?? false);
4430
4497
  if (isArray2) {
4431
4498
  const itemNodes = items !== void 0 ? derefSchemaNodes(items, defs) : [];
@@ -4442,7 +4509,7 @@ function pushSchemaValuePaths(path, node, paths, defs, depth) {
4442
4509
  }
4443
4510
  return;
4444
4511
  }
4445
- if (nodes.some((n) => n.properties !== void 0)) {
4512
+ if (nodes2.some((n) => n.properties !== void 0)) {
4446
4513
  collectSchemaObjectPaths(node, path, paths, defs, depth + 1);
4447
4514
  }
4448
4515
  }
@@ -4541,9 +4608,12 @@ function fromJson(text3) {
4541
4608
  const paths = analyzeJsonPaths(text3);
4542
4609
  return { paths, readable: paths.length > 0 || isJsonObject(text3) };
4543
4610
  }
4611
+ function scriptReadable(script, paths) {
4612
+ return !hasTopLevelSpread(script) && (paths.length > 0 || hasDefaultExportObject(script));
4613
+ }
4544
4614
  function fromScript(script) {
4545
4615
  const paths = analyzeStatePaths(script);
4546
- return { paths, readable: paths.length > 0 || isReadableStateScript(script) };
4616
+ return { paths, readable: scriptReadable(script, paths) };
4547
4617
  }
4548
4618
  function resolveElementPathsRaw(element2, html, fileReader) {
4549
4619
  let readable = false;
@@ -4566,19 +4636,23 @@ function resolveElementPathsRaw(element2, html, fileReader) {
4566
4636
  readable ||= resolved.readable;
4567
4637
  }
4568
4638
  if (element2.scriptBlocks.length > 0) {
4569
- const paths = element2.scriptBlocks.flatMap((block) => analyzeStatePaths(block.content));
4639
+ const blocks = element2.scriptBlocks.map((block) => ({ script: block.content, paths: analyzeStatePaths(block.content) }));
4570
4640
  return {
4571
- paths,
4572
- readable: paths.length > 0 || readable || element2.scriptBlocks.some((block) => isReadableStateScript(block.content))
4641
+ paths: blocks.flatMap((block) => block.paths),
4642
+ readable: readable || blocks.some((block) => scriptReadable(block.script, block.paths))
4573
4643
  };
4574
4644
  }
4575
4645
  return readable ? { paths: [], readable } : UNREADABLE;
4576
4646
  }
4577
4647
  function readStateScript(srcPath, fileReader) {
4578
- if (srcPath.endsWith(".js")) {
4579
- return fileReader(srcPath.replace(/\.js$/, ".ts")) || fileReader(srcPath) || void 0;
4648
+ return readStateScriptFile(srcPath, fileReader)?.text;
4649
+ }
4650
+ function readStateScriptFile(srcPath, fileReader) {
4651
+ const paths = srcPath.endsWith(".js") ? [srcPath.replace(/\.js$/, ".ts"), srcPath] : srcPath.endsWith(".ts") ? [srcPath] : [];
4652
+ for (const path of paths) {
4653
+ const text3 = fileReader(path);
4654
+ if (text3) return { path, text: text3 };
4580
4655
  }
4581
- if (srcPath.endsWith(".ts")) return fileReader(srcPath) || void 0;
4582
4656
  return void 0;
4583
4657
  }
4584
4658
  function resolveSrcAttribute(srcPath, fileReader) {
@@ -4611,10 +4685,6 @@ function isInsideForTemplate(html, offset2, bindAttrName = "data-wcs") {
4611
4685
  }
4612
4686
  return false;
4613
4687
  }
4614
- function getInnermostForPath(html, offset2, bindAttrName = "data-wcs") {
4615
- const chain = getEnclosingForPaths(html, offset2, bindAttrName);
4616
- return chain.length === 0 ? null : chain[chain.length - 1];
4617
- }
4618
4688
  var BINDING_SYNTAX_MARKER = `[${WcsDiagnosticCode.BindingSyntax}]`;
4619
4689
  function isForBindingRefused(bindText) {
4620
4690
  try {
@@ -4860,10 +4930,10 @@ function analyzeElementContexts(html, offsets, bindAttrName = "data-wcs", stateT
4860
4930
  }
4861
4931
  const match3 = bindAttr.exec(attrs);
4862
4932
  const value = match3 === null ? null : match3[1] ?? match3[2] ?? match3[3];
4863
- const names = value === null ? [] : propertyNamesOf(value);
4864
- const structural = name === "template" && names.length === 1 && STRUCTURAL_DIRECTIVES2.has(names[0]) && !value.slice(0, Math.max(0, indexOfOutsideQuotes2(value, ":"))).includes("#");
4933
+ const names2 = value === null ? [] : propertyNamesOf(value);
4934
+ const structural = name === "template" && names2.length === 1 && STRUCTURAL_DIRECTIVES2.has(names2[0]) && !value.slice(0, Math.max(0, indexOfOutsideQuotes2(value, ":"))).includes("#");
4865
4935
  const plainTemplate = name === "template" && !structural;
4866
- const blocks = plainTemplate && stack.some((e) => e.name === "template") || name !== "template" && names.some((n) => CONTENT_PROPERTIES.has(n));
4936
+ const blocks = plainTemplate && stack.some((e) => e.name === "template") || name !== "template" && names2.some((n) => CONTENT_PROPERTIES.has(n));
4867
4937
  const scope = plainTemplate ? { hasOwnState: false, id: extractAttribute(attrs, "id"), underRouter: stack.some((e) => e.name === ROUTER_TAG) } : null;
4868
4938
  stack.push({ name, structural, scope, blocks });
4869
4939
  }
@@ -6320,7 +6390,7 @@ pp$8.parseClassElement = function(constructorAllowsSuper) {
6320
6390
  }
6321
6391
  var ecmaVersion = this.options.ecmaVersion;
6322
6392
  var node = this.startNode();
6323
- var keyName = "";
6393
+ var keyName2 = "";
6324
6394
  var isGenerator = false;
6325
6395
  var isAsync = false;
6326
6396
  var kind = "method";
@@ -6333,34 +6403,34 @@ pp$8.parseClassElement = function(constructorAllowsSuper) {
6333
6403
  if (this.isClassElementNameStart() || this.type === types$1.star) {
6334
6404
  isStatic = true;
6335
6405
  } else {
6336
- keyName = "static";
6406
+ keyName2 = "static";
6337
6407
  }
6338
6408
  }
6339
6409
  node.static = isStatic;
6340
- if (!keyName && ecmaVersion >= 8 && this.eatContextual("async")) {
6410
+ if (!keyName2 && ecmaVersion >= 8 && this.eatContextual("async")) {
6341
6411
  if ((this.isClassElementNameStart() || this.type === types$1.star) && !this.canInsertSemicolon()) {
6342
6412
  isAsync = true;
6343
6413
  } else {
6344
- keyName = "async";
6414
+ keyName2 = "async";
6345
6415
  }
6346
6416
  }
6347
- if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {
6417
+ if (!keyName2 && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {
6348
6418
  isGenerator = true;
6349
6419
  }
6350
- if (!keyName && !isAsync && !isGenerator) {
6420
+ if (!keyName2 && !isAsync && !isGenerator) {
6351
6421
  var lastValue = this.value;
6352
6422
  if (this.eatContextual("get") || this.eatContextual("set")) {
6353
6423
  if (this.isClassElementNameStart()) {
6354
6424
  kind = lastValue;
6355
6425
  } else {
6356
- keyName = lastValue;
6426
+ keyName2 = lastValue;
6357
6427
  }
6358
6428
  }
6359
6429
  }
6360
- if (keyName) {
6430
+ if (keyName2) {
6361
6431
  node.computed = false;
6362
6432
  node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);
6363
- node.key.name = keyName;
6433
+ node.key.name = keyName2;
6364
6434
  this.finishNode(node.key, "Identifier");
6365
6435
  } else {
6366
6436
  this.parseClassElementName(node);
@@ -6661,7 +6731,7 @@ pp$8.parseExportSpecifier = function(exports$1) {
6661
6731
  return this.finishNode(node, "ExportSpecifier");
6662
6732
  };
6663
6733
  pp$8.parseExportSpecifiers = function(exports$1) {
6664
- var nodes = [], first = true;
6734
+ var nodes2 = [], first = true;
6665
6735
  this.expect(types$1.braceL);
6666
6736
  while (!this.eat(types$1.braceR)) {
6667
6737
  if (!first) {
@@ -6672,9 +6742,9 @@ pp$8.parseExportSpecifiers = function(exports$1) {
6672
6742
  } else {
6673
6743
  first = false;
6674
6744
  }
6675
- nodes.push(this.parseExportSpecifier(exports$1));
6745
+ nodes2.push(this.parseExportSpecifier(exports$1));
6676
6746
  }
6677
- return nodes;
6747
+ return nodes2;
6678
6748
  };
6679
6749
  pp$8.parseImport = function(node) {
6680
6750
  this.next();
@@ -6719,16 +6789,16 @@ pp$8.parseImportNamespaceSpecifier = function() {
6719
6789
  return this.finishNode(node, "ImportNamespaceSpecifier");
6720
6790
  };
6721
6791
  pp$8.parseImportSpecifiers = function() {
6722
- var nodes = [], first = true;
6792
+ var nodes2 = [], first = true;
6723
6793
  if (this.type === types$1.name) {
6724
- nodes.push(this.parseImportDefaultSpecifier());
6794
+ nodes2.push(this.parseImportDefaultSpecifier());
6725
6795
  if (!this.eat(types$1.comma)) {
6726
- return nodes;
6796
+ return nodes2;
6727
6797
  }
6728
6798
  }
6729
6799
  if (this.type === types$1.star) {
6730
- nodes.push(this.parseImportNamespaceSpecifier());
6731
- return nodes;
6800
+ nodes2.push(this.parseImportNamespaceSpecifier());
6801
+ return nodes2;
6732
6802
  }
6733
6803
  this.expect(types$1.braceL);
6734
6804
  while (!this.eat(types$1.braceR)) {
@@ -6740,14 +6810,14 @@ pp$8.parseImportSpecifiers = function() {
6740
6810
  } else {
6741
6811
  first = false;
6742
6812
  }
6743
- nodes.push(this.parseImportSpecifier());
6813
+ nodes2.push(this.parseImportSpecifier());
6744
6814
  }
6745
- return nodes;
6815
+ return nodes2;
6746
6816
  };
6747
6817
  pp$8.parseWithClause = function() {
6748
- var nodes = [];
6818
+ var nodes2 = [];
6749
6819
  if (!this.eat(types$1._with)) {
6750
- return nodes;
6820
+ return nodes2;
6751
6821
  }
6752
6822
  this.expect(types$1.braceL);
6753
6823
  var attributeKeys = {};
@@ -6762,14 +6832,14 @@ pp$8.parseWithClause = function() {
6762
6832
  first = false;
6763
6833
  }
6764
6834
  var attr = this.parseImportAttribute();
6765
- var keyName = attr.key.type === "Identifier" ? attr.key.name : attr.key.value;
6766
- if (hasOwn(attributeKeys, keyName)) {
6767
- this.raiseRecoverable(attr.key.start, "Duplicate attribute key '" + keyName + "'");
6835
+ var keyName2 = attr.key.type === "Identifier" ? attr.key.name : attr.key.value;
6836
+ if (hasOwn(attributeKeys, keyName2)) {
6837
+ this.raiseRecoverable(attr.key.start, "Duplicate attribute key '" + keyName2 + "'");
6768
6838
  }
6769
- attributeKeys[keyName] = true;
6770
- nodes.push(attr);
6839
+ attributeKeys[keyName2] = true;
6840
+ nodes2.push(attr);
6771
6841
  }
6772
- return nodes;
6842
+ return nodes2;
6773
6843
  };
6774
6844
  pp$8.parseImportAttribute = function() {
6775
6845
  var node = this.startNode();
@@ -10918,7 +10988,7 @@ function visitDestructure(pattern, prefix, scope, out) {
10918
10988
  emit(out, segments, { form: "destructure", callee: false, written: false }, property.start, property.end);
10919
10989
  }
10920
10990
  }
10921
- function collectThisMemberRefs(body, names) {
10991
+ function collectThisMemberRefs(body, names2) {
10922
10992
  let program;
10923
10993
  try {
10924
10994
  program = parse3(PREFIX + body + SUFFIX, { ecmaVersion: "latest", sourceType: "module" });
@@ -10927,11 +10997,11 @@ function collectThisMemberRefs(body, names) {
10927
10997
  }
10928
10998
  const fn = unwrapWrapper(program);
10929
10999
  if (fn === null) return null;
10930
- return walkThisMembers(fn.body, names, PREFIX.length);
11000
+ return walkThisMembers(fn.body, names2, PREFIX.length);
10931
11001
  }
10932
11002
  var VALUE_PREFIX = "(async function* () {\n(";
10933
11003
  var VALUE_SUFFIX = ")\n})";
10934
- function collectThisMemberRefsInValue(value, names) {
11004
+ function collectThisMemberRefsInValue(value, names2) {
10935
11005
  let program;
10936
11006
  try {
10937
11007
  program = parse3(VALUE_PREFIX + value + VALUE_SUFFIX, { ecmaVersion: "latest", sourceType: "module" });
@@ -10944,9 +11014,9 @@ function collectThisMemberRefsInValue(value, names) {
10944
11014
  if (statement.type !== "ExpressionStatement") return null;
10945
11015
  const expression = statement.expression;
10946
11016
  if (expression.type !== "FunctionExpression") return [];
10947
- return walkThisMembers(expression.body, names, VALUE_PREFIX.length);
11017
+ return walkThisMembers(expression.body, names2, VALUE_PREFIX.length);
10948
11018
  }
10949
- function walkThisMembers(root, names, offsetShift) {
11019
+ function walkThisMembers(root, names2, offsetShift) {
10950
11020
  const out = [];
10951
11021
  const walk = (node, thisIsState) => {
10952
11022
  if (isClassNode(node)) return;
@@ -10957,7 +11027,7 @@ function walkThisMembers(root, names, offsetShift) {
10957
11027
  }
10958
11028
  if (thisIsState && node.type === "MemberExpression" && node.object.type === "ThisExpression") {
10959
11029
  const hit = thisMemberName(node);
10960
- if (hit !== null && names.has(hit.name)) {
11030
+ if (hit !== null && names2.has(hit.name)) {
10961
11031
  out.push({ name: hit.name, start: hit.start - offsetShift, end: hit.end - offsetShift });
10962
11032
  }
10963
11033
  }
@@ -11124,13 +11194,13 @@ function findInPlaceChanges(text3, kind, root) {
11124
11194
  const collect2 = (node) => {
11125
11195
  if (isFunctionNode(node)) {
11126
11196
  if (node.type === "FunctionDeclaration" && node.id !== null) bind(node.id.name);
11127
- const names = /* @__PURE__ */ new Set();
11128
- for (const param of node.params) bindingNames(param, names);
11129
- names.forEach(bind);
11197
+ const names2 = /* @__PURE__ */ new Set();
11198
+ for (const param of node.params) bindingNames(param, names2);
11199
+ names2.forEach(bind);
11130
11200
  } else if (node.type === "VariableDeclarator") {
11131
- const names = /* @__PURE__ */ new Set();
11132
- bindingNames(node.id, names);
11133
- names.forEach(bind);
11201
+ const names2 = /* @__PURE__ */ new Set();
11202
+ bindingNames(node.id, names2);
11203
+ names2.forEach(bind);
11134
11204
  const init = node.init ? unwrapChain(node.init) : null;
11135
11205
  if (init !== null && node.id.type === "Identifier" && init.type === "MemberExpression") {
11136
11206
  const chain = memberChain(init, source);
@@ -11148,9 +11218,9 @@ function findInPlaceChanges(text3, kind, root) {
11148
11218
  }
11149
11219
  }
11150
11220
  } else if (node.type === "CatchClause" && node.param) {
11151
- const names = /* @__PURE__ */ new Set();
11152
- bindingNames(node.param, names);
11153
- names.forEach(bind);
11221
+ const names2 = /* @__PURE__ */ new Set();
11222
+ bindingNames(node.param, names2);
11223
+ names2.forEach(bind);
11154
11224
  } else if (node.type === "ClassDeclaration" && node.id !== null) {
11155
11225
  bind(node.id.name);
11156
11226
  } else if (node.type === "AssignmentExpression" && node.left.type === "Identifier") {
@@ -11212,14 +11282,14 @@ function findLocationRead(body) {
11212
11282
  }
11213
11283
  const wrapper = unwrapWrapper(program);
11214
11284
  if (wrapper === null) return null;
11215
- const names = /* @__PURE__ */ new Set();
11285
+ const names2 = /* @__PURE__ */ new Set();
11216
11286
  const scan = (node) => {
11217
- if (isFunctionNode(node)) for (const param of node.params) bindingNames(param, names);
11218
- if (node.type === "VariableDeclarator") bindingNames(node.id, names);
11287
+ if (isFunctionNode(node)) for (const param of node.params) bindingNames(param, names2);
11288
+ if (node.type === "VariableDeclarator") bindingNames(node.id, names2);
11219
11289
  forEachChild(node, scan);
11220
11290
  };
11221
11291
  scan(wrapper.body);
11222
- if (names.has("location")) return null;
11292
+ if (names2.has("location")) return null;
11223
11293
  const isLocation = (node) => {
11224
11294
  const n = unwrapChain(node);
11225
11295
  if (n.type === "Identifier") return n.name === "location";
@@ -11296,6 +11366,13 @@ var ja = {
11296
11366
  updatedCallbackUnbound: (p) => `$renderedCallback \u306F binding \u99C6\u52D5\u3067\u3059\u3002"${p}" \u306F\u3053\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u3069\u306E\u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u306B\u3082\u73FE\u308C\u306A\u3044\u305F\u3081\u3001\u3053\u306E\u5206\u5C90\u306F\u4E00\u5EA6\u3082\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093\u3002\u63CF\u753B\u306B\u4F9D\u5B58\u305B\u305A\u53CD\u5FDC\u3059\u308B\u306A\u3089 $watch \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044`,
11297
11367
  onHandlerThis: (name) => `$on \u306E\u30CF\u30F3\u30C9\u30E9 "${name}" \u304C this \u3092\u4F7F\u3063\u3066\u3044\u307E\u3059\u3002$on \u306E\u30CF\u30F3\u30C9\u30E9\u306F state \u3092\u7B2C 1 \u5F15\u6570\u3067\u53D7\u3051\u53D6\u308A\uFF08\`${name}(state, event) { state.x = \u2026 }\`\uFF09\u3001this \u306F undefined \u306A\u306E\u3067\u3001this.\u2026 \u306F\u547C\u3070\u308C\u305F\u3068\u304D\u306B\u4F8B\u5916\u306B\u306A\u308A\u307E\u3059\u3002$watch \u3084\u30E1\u30BD\u30C3\u30C9\u3068\u9055\u3046\u70B9\u306B\u6CE8\u610F`,
11298
11368
  getterReadsLocation: (read) => `\u3053\u306E getter \u306F ${read} \u3092\u8AAD\u3093\u3067\u3044\u307E\u3059\u304C\u3001location \u306F state \u3067\u306F\u306A\u3044\u306E\u3067\u3001\u6700\u521D\u306B\u8AAD\u3093\u3060 URL \u3092\u6301\u3061\u7D9A\u3051\u307E\u3059\uFF08\u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3\u3067\u518D\u8A55\u4FA1\u3055\u308C\u307E\u305B\u3093\uFF09\u3002URL \u306F state \u3092\u901A\u3057\u3066\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044 \u2014 <wcs-router> \u306E path / searchParams \u3092\u675F\u306D\u308B`,
11369
+ getterReadsClock: (read) => `\u3053\u306E getter \u306F ${read} \u3092\u8AAD\u3093\u3067\u3044\u307E\u3059\u304C\u3001\u6642\u523B\u306F state \u3067\u306F\u306A\u3044\u306E\u3067\u4F9D\u5B58\u3068\u3057\u3066\u767B\u9332\u3055\u308C\u305A\u3001getter \u306F\u30AD\u30E3\u30C3\u30B7\u30E5\u3057\u305F\u6700\u521D\u306E\u5024\u3092\u8FD4\u3057\u7D9A\u3051\u307E\u3059\uFF08\u30A4\u30D9\u30F3\u30C8\u30C8\u30FC\u30AF\u30F3\u3067\u6BCE\u30D5\u30EC\u30FC\u30E0\u300C\u63CF\u304D\u76F4\u3057\u300D\u3066\u3082\u5909\u308F\u308A\u307E\u305B\u3093\uFF09\u3002\u6642\u523B\u3092 state \u306B\u7F6E\u3044\u3066\u304F\u3060\u3055\u3044 \u2014 <wcs-raf> \u3084 <wcs-timer> \u304C\u66F8\u304F\u5024\u3092\u675F\u306D\u308B`,
11370
+ updatedCallbackUnknownPath: (p) => `"${p}" \u306F state \u306E\u30D1\u30B9\u3067\u306F\u306A\u3044\u306E\u3067 $renderedCallback \u306E paths \u306B\u73FE\u308C\u305A\u3001\u3053\u306E\u5206\u5C90\u306F\u4E00\u5EA6\u3082\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093`,
11371
+ commandTokenUnbound: (t) => `\u30B3\u30DE\u30F3\u30C9\u30C8\u30FC\u30AF\u30F3 "${t}" \u3092 emit \u3057\u3066\u3044\u307E\u3059\u304C\u3001\u3053\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306B\u305D\u308C\u3092\u53D7\u3051\u308B command.<\u30E1\u30BD\u30C3\u30C9>: ${t} \u306E\u675F\u7E1B\u304C\u3042\u308A\u307E\u305B\u3093\u3002emit \u306F\u8AB0\u306B\u3082\u5C4A\u304D\u307E\u305B\u3093\u3002\u30E1\u30BD\u30C3\u30C9\u3092\u547C\u3076\u8981\u7D20\u306B\u675F\u306D\u3066\u304F\u3060\u3055\u3044\uFF08command.focus: ${t}\uFF09`,
11372
+ writePathMissing: (k) => `"${k}" \u306F state \u304C\u5BA3\u8A00\u3057\u3066\u3044\u306A\u3044\u30AD\u30FC\u3067\u3059\u3002\u66F8\u304D\u8FBC\u307F\u306F\u65B0\u3057\u3044\u30AD\u30FC\u3092\u4F5C\u308B\u3060\u3051\u3067\u3001\u5BA3\u8A00\u3057\u305F\u30AD\u30FC\u306E\u675F\u7E1B\u306B\u306F\u5C4A\u304D\u307E\u305B\u3093`,
11373
+ readPathMissing: (p, top) => top ? `"${p}" \u306F state \u304C\u5BA3\u8A00\u3057\u3066\u3044\u306A\u3044\u30AD\u30FC\u3067\u3059\u3002\u8AAD\u3093\u3060\u6642\u70B9\u3067\u4F8B\u5916\u306B\u306A\u308A\u307E\u3059\uFF08#301\uFF09` : `\u30D1\u30B9 "${p}" \u306F\u72B6\u614B\u5B9A\u7FA9\u306B\u5B58\u5728\u3057\u307E\u305B\u3093\u3002\u8AAD\u307F\u306F undefined \u306B\u306A\u308A\u3001\u305D\u3053\u304B\u3089\u8A08\u7B97\u3057\u305F\u5024\u306F\u9ED9\u3063\u3066\u9055\u3044\u307E\u3059`,
11374
+ pathDynamicUnknownSpread: (sp) => `${sp} \u304C\u6301\u3061\u8FBC\u3080\u30AD\u30FC\u306F\u9759\u7684\u306B\u8AAD\u3081\u306A\u3044\u306E\u3067\u3001\u3053\u306E state \u306E\u30D1\u30B9\u306F\u691C\u67FB\u3057\u3066\u3044\u307E\u305B\u3093\uFF08\u675F\u7E1B\u30FBgetter \u306E\u4E2D\u306E\u8AAD\u307F\u30FB$watch \u306E\u30AD\u30FC\uFF09\u3002\u30AD\u30FC\u3092 wcstack.manifest.json \u306E stateSchema \u306B\u5BA3\u8A00\u3059\u308B\u3068\u691C\u67FB\u3055\u308C\u307E\u3059\u3002npx wcs-schema\uFF08@wcstack/typescript\uFF09\u304C state \u306E\u578B\uFF08JSDoc\u30FBTypeScript\uFF09\u304B\u3089\u4F5C\u308C\u307E\u3059`,
11375
+ pathReadonlyTwoWay: (p, prop) => `"${p}" \u306F setter \u306E\u7121\u3044 getter \u3067\u3059\u304C\u3001${prop}: \u306F\u8AAD\u8005\u306E\u5165\u529B\u3092\u66F8\u304D\u623B\u3059\u306E\u3067\u3001\u5165\u529B\u306E\u305F\u3073\u306B\u66F8\u304D\u8FBC\u307F\u304C\u5931\u6557\u3057\u307E\u3059\uFF08"${p}" is a getter without a setter\uFF09\u3002setter \u3092\u8DB3\u3059\uFF08set "${p}"(v) { \u2026 }\uFF09\u304B\u3001#ro \u3067\u4E00\u65B9\u5411\u306B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08${prop}#ro: ${p}\uFF09`,
11299
11376
  submitWithoutPrevent: (h) => `onsubmit \u306B #prevent \u304C\u7121\u304F\u3001"${h}" \u3082 preventDefault \u3092\u547C\u3073\u307E\u305B\u3093\u3002\u30D6\u30E9\u30A6\u30B6\u306F\u30D5\u30A9\u30FC\u30E0\u3082\u9001\u308B\u306E\u3067\u3001\u30DA\u30FC\u30B8\u304C\u8AAD\u307F\u76F4\u3055\u308C\u308B\u304B\u3001router \u304C\u3042\u308C\u3070 GET \u304C\u6B04\u306E\u5024\uFF08\u30D1\u30B9\u30EF\u30FC\u30C9\u3082\uFF09\u3092 URL \u3068\u5C65\u6B74\u306B\u8F09\u305B\u307E\u3059\u3002onsubmit#prevent: ${h} \u3068\u66F8\u304F\u304B\u3001\u30CF\u30F3\u30C9\u30E9\u3067 event.preventDefault() \u3092\u547C\u3093\u3067\u304F\u3060\u3055\u3044`,
11300
11377
  getterUntrackedRead: (root, sp) => `\u3053\u3053\u3067\u8FFD\u8DE1\u3055\u308C\u308B\u306E\u306F "${root}" \u3060\u3051\u3067\u3059\u3002"${sp}" \u304C\u5909\u308F\u3063\u3066\u3082\u3053\u306E getter \u306F\u518D\u8A55\u4FA1\u3055\u308C\u307E\u305B\u3093\uFF08\u30D1\u30B9\u8AAD\u307F\u53D6\u308A\u306E\u5148\u306E\u7D20\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u30A2\u30AF\u30BB\u30B9\u306F\u8FFD\u8DE1\u3055\u308C\u306A\u3044\uFF09\u3002this["${sp}"] \u3067\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044`,
11301
11378
  handlerFilterNotAllowed: (prop) => `\u30A4\u30D9\u30F3\u30C8\u30CF\u30F3\u30C9\u30E9 "${prop}" \u306B\u30D5\u30A3\u30EB\u30BF\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
@@ -11332,7 +11409,7 @@ var ja = {
11332
11409
  behaviorValueType: (key, type) => `$behavior \u306E "${key}" \u306F ${type === "boolean" ? "true \u304B false" : type} \u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09`,
11333
11410
  configInVolume: (key, mountPath) => `${key} \u306F\u30DC\u30EA\u30E5\u30FC\u30E0\uFF08mount="${mountPath}"\uFF09\u3067\u306F\u5BA3\u8A00\u3067\u304D\u307E\u305B\u3093\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u63A5\u304E\u6728\u3092\u62D2\u307F\u3001console.error \u3067\u5831\u544A\u3057\u307E\u3059 \u2014 \u3053\u306E state \u306F\u6728\u306B\u8F09\u308A\u307E\u305B\u3093\uFF09\u3002root \u306E state \u306B\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 \u30DC\u30EA\u30E5\u30FC\u30E0\u306F root \u306E\u30A8\u30F3\u30B8\u30F3\u306B\u63A5\u304E\u6728\u3055\u308C\u307E\u3059`,
11334
11411
  featuresNotArray: () => `$features \u306F\u5F8C\u4ED8\u3051\u306E\u540D\u524D\u306E\u914D\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u4F8B: ["temporal", "formats"]\u3002\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09`,
11335
- featureUnknown: (name, names, suggestion2) => `"${name}" \u306F\u5F8C\u4ED8\u3051\u306E\u540D\u524D\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08${names.join("\u30FB")}\uFF09${suggestion2}`,
11412
+ featureUnknown: (name, names2, suggestion2) => `"${name}" \u306F\u5F8C\u4ED8\u3051\u306E\u540D\u524D\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08${names2.join("\u30FB")}\uFF09${suggestion2}`,
11336
11413
  volumeDeclarationRejected: (key, mountPath) => `${key} \u306F\u30DC\u30EA\u30E5\u30FC\u30E0\uFF08mount="${mountPath}"\uFF09\u3067\u306F\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093\u3002\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u63A5\u304E\u6728\u3092\u62D2\u3093\u3067 console.error \u3067\u5831\u544A\u3057\u307E\u3059\uFF08\u3053\u306E state \u306F\u6728\u306B\u8F09\u308A\u307E\u305B\u3093\uFF09\u3002root \u306E state \u306B\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044`,
11337
11414
  volumeDeclarationNotRun: (key, mountPath) => `${key} \u306F\u30DC\u30EA\u30E5\u30FC\u30E0\uFF08mount="${mountPath}"\uFF09\u3067\u306F\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F console.warn \u3067\u77E5\u3089\u305B\u3001\u3053\u306E\u5BA3\u8A00\u3092\u7121\u8996\u3057\u307E\u3059 \u2014 root \u306E state \u306E\u3082\u306E\u3067\u3059\uFF09\u3002root \u306E state \u306B\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044`,
11338
11415
  secondRoot: () => '\u540C\u3058 root \u306B 2 \u3064\u76EE\u306E <wcs-state> \u304C\u3042\u308A\u307E\u3059\u3002state \u306E\u6728\u306F root \u3054\u3068\u306B 1 \u3064\u3067\u3059\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u5F8C\u304B\u3089\u8AAD\u307F\u8FBC\u3093\u3060\u65B9\u3092\u62D2\u3093\u3067 console.error \u3067\u5831\u544A\u3057\u307E\u3059\uFF09\u3002\u90E8\u5206\u6728\u306F <wcs-state mount="path"> \u3067\u63A5\u304E\u6728\u3057\u3066\u304F\u3060\u3055\u3044',
@@ -11345,6 +11422,7 @@ var ja = {
11345
11422
  nativeCommandUnknown: (method, tag, allowed) => `"${method}" \u306F\u30CD\u30A4\u30C6\u30A3\u30D6\u306E <${tag}> \u306B command \u3067\u547C\u3079\u308B\u30E1\u30BD\u30C3\u30C9\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u547C\u3079\u308B\u306E\u306F ${allowed}\u3002\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u521D\u671F\u5316\u3067 wcs/token-misconfigured \u3092\u6295\u3052\u307E\u3059\uFF09`,
11346
11423
  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`,
11347
11424
  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`,
11425
+ handleBoundAsValue: (name, tag) => `<${tag}> \u306E "${name}" \u306F\u751F\u304D\u305F\u30EA\u30BD\u30FC\u30B9\uFF08\u30CF\u30F3\u30C9\u30EB\uFF09\u3067\u3001\u5024\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002state \u306E\u30D1\u30B9\u306B\u675F\u306D\u308B\u3068\u3001\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3084 SSR \u306B\u8F09\u3089\u306A\u3044\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u304C state \u306B\u5165\u308A\u307E\u3059 \u2014 eventToken.${name}: \u3067\u53D7\u3051\u3001\u30B3\u30DE\u30F3\u30C9\u306E\u5F15\u6570\u3068\u3057\u3066\u6B21\u306E\u8981\u7D20\u3078\u76F4\u63A5\u6E21\u3057\u3066\u304F\u3060\u3055\u3044`,
11348
11426
  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`,
11349
11427
  didYouMean: (c) => `\u3002\u3082\u3057\u304B\u3057\u3066: "${c}"`,
11350
11428
  none: () => `\u306A\u3057`,
@@ -11497,6 +11575,13 @@ var en = {
11497
11575
  updatedCallbackUnbound: (p) => `$renderedCallback is binding-driven. "${p}" is not bound anywhere in this document, so this branch never runs. Use $watch to react without depending on what is rendered`,
11498
11576
  onHandlerThis: (name) => `The $on handler "${name}" uses this. A $on handler receives the state as its first argument (\`${name}(state, event) { state.x = \u2026 }\`) and this is undefined, so this.\u2026 throws when it is called \u2014 unlike $watch handlers and methods`,
11499
11577
  getterReadsLocation: (read) => `This getter reads ${read}, which is not state: it keeps the URL it first read (a navigation does not re-evaluate it). Read the URL through state \u2014 bind <wcs-router>'s path / searchParams`,
11578
+ getterReadsClock: (read) => `This getter reads ${read}. The time is not state, so it is not a dependency: the getter is cached and keeps returning its first value (an event token "redrawing" every frame does not change it). Keep the time in state \u2014 bind the value <wcs-raf> or <wcs-timer> writes`,
11579
+ updatedCallbackUnknownPath: (p) => `"${p}" is not a path of this state, so it is never in $renderedCallback's paths and this branch never runs`,
11580
+ commandTokenUnbound: (t) => `The command token "${t}" is emitted, but no command.<method>: ${t} binding in this document receives it, so the emit reaches nothing. Bind it on the element whose method it should call (command.focus: ${t})`,
11581
+ writePathMissing: (k) => `"${k}" is not a key the state declares. The write creates a new key and reaches no binding of a declared one`,
11582
+ readPathMissing: (p, top) => top ? `"${p}" is not a key the state declares. Reading it throws (#301)` : `Path "${p}" does not exist in the state definition. The read gives undefined, and what is computed from it is wrong without a word`,
11583
+ pathDynamicUnknownSpread: (sp) => `The keys ${sp} brings in cannot be read statically, so this state's paths are not checked (bindings, reads in getters, $watch keys). Declare the keys in the stateSchema of wcstack.manifest.json to have them checked; npx wcs-schema (@wcstack/typescript) derives it from the state's types (JSDoc or TypeScript)`,
11584
+ pathReadonlyTwoWay: (p, prop) => `"${p}" is a getter without a setter, and ${prop}: writes back what the reader enters, so every edit fails ("${p}" is a getter without a setter). Add a setter (set "${p}"(v) { \u2026 }), or bind it one way with #ro (${prop}#ro: ${p})`,
11500
11585
  submitWithoutPrevent: (h) => `onsubmit has no #prevent and "${h}" does not call preventDefault, so the browser submits the form too: the page reloads, or with the router a GET puts the fields (a password too) in the URL and the history. Write onsubmit#prevent: ${h}, or call event.preventDefault() in the handler`,
11501
11586
  getterUntrackedRead: (root, sp) => `Only "${root}" is tracked here: the getter is not re-evaluated when "${sp}" changes (plain property access after a path read is not tracked). Read this["${sp}"] instead`,
11502
11587
  handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
@@ -11533,7 +11618,7 @@ var en = {
11533
11618
  behaviorValueType: (key, type) => `$behavior "${key}" must be ${type === "boolean" ? "true or false" : type} (the runtime throws at load time)`,
11534
11619
  configInVolume: (key, mountPath) => `${key} cannot be declared in a volume (mount="${mountPath}"): the runtime refuses to graft the volume and reports that with console.error (this state is not on the tree). Declare it on the root state \u2014 a volume is grafted onto the root's engine`,
11535
11620
  featuresNotArray: () => `$features must be an array of add-on names (for example ["temporal", "formats"]; the runtime throws at load time)`,
11536
- featureUnknown: (name, names, suggestion2) => `"${name}" is not an add-on (${names.join(", ")})${suggestion2}`,
11621
+ featureUnknown: (name, names2, suggestion2) => `"${name}" is not an add-on (${names2.join(", ")})${suggestion2}`,
11537
11622
  volumeDeclarationRejected: (key, mountPath) => `${key} is not run in a volume (mount="${mountPath}"): the runtime refuses to graft the volume and reports that with console.error (this state is not on the tree). Declare it on the root state`,
11538
11623
  volumeDeclarationNotRun: (key, mountPath) => `${key} is not run in a volume (mount="${mountPath}"): the runtime says so with console.warn and ignores this declaration \u2014 it belongs to the root state. Declare it there`,
11539
11624
  secondRoot: () => 'A second <wcs-state> on the same root: there is one state tree per root (the runtime refuses the one that loads second and reports it with console.error). Graft a subtree with <wcs-state mount="path">',
@@ -11546,6 +11631,7 @@ var en = {
11546
11631
  nativeCommandUnknown: (method, tag, allowed) => `"${method}" is not a command of a native <${tag}> (its commands: ${allowed}; the runtime throws wcs/token-misconfigured at initialization)`,
11547
11632
  spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
11548
11633
  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})`,
11634
+ handleBoundAsValue: (name, tag) => `"${name}" of <${tag}> is a live resource (a handle), not a value. Bound to a state path, it puts an object no snapshot or SSR can carry into the state \u2014 receive it with eventToken.${name}: and hand it to the next element as a command argument`,
11549
11635
  ariaAttrUnknown: (name) => `"${name}" is not a WAI-ARIA attribute. setAttribute writes it anyway, and assistive technology silently ignores it`,
11550
11636
  didYouMean: (c) => `. Did you mean "${c}"?`,
11551
11637
  none: () => `none`,
@@ -11785,11 +11871,11 @@ var DELEGATED_EVENTS = /* @__PURE__ */ new Set([
11785
11871
  ]);
11786
11872
  var CUSTOM_ELEMENT_OWN_EVENTS = /* @__PURE__ */ new Set(["input", "change", "submit"]);
11787
11873
  function collectCurrentTargetHandlers(elements, html, fileReader) {
11788
- const names = /* @__PURE__ */ new Set();
11874
+ const names2 = /* @__PURE__ */ new Set();
11789
11875
  forEachStateMethod(elements, html, fileReader, "currentTarget", (name, params, body) => {
11790
- if (readsEventCurrentTarget(params, body)) names.add(name);
11876
+ if (readsEventCurrentTarget(params, body)) names2.add(name);
11791
11877
  });
11792
- return names;
11878
+ return names2;
11793
11879
  }
11794
11880
  function collectPreventDefaultHandlers(elements, html, fileReader) {
11795
11881
  const handlers = /* @__PURE__ */ new Map();
@@ -12036,6 +12122,18 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
12036
12122
  message: `${verdict.message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
12037
12123
  severity: verdict.severity
12038
12124
  });
12125
+ } else if (writesBack(attr, propNoMod, parsed.property)) {
12126
+ const target = scopedPaths.find((p) => p.path === checkPath);
12127
+ if (target !== void 0 && target.kind === "computed" && target.settable !== true) {
12128
+ const pathStart = bindingStart + binding.indexOf(parsed.path);
12129
+ diagnostics.push({
12130
+ code: WcsDiagnosticCode.PathReadonly,
12131
+ start: pathStart,
12132
+ end: pathStart + pathTrimmed.length,
12133
+ message: msgs.pathReadonlyTwoWay(checkPath, propNoMod),
12134
+ severity: "error"
12135
+ });
12136
+ }
12039
12137
  }
12040
12138
  }
12041
12139
  }
@@ -12222,6 +12320,19 @@ function openTagBefore(html, valueStart) {
12222
12320
  const lt2 = html.lastIndexOf("<", valueStart);
12223
12321
  return lt2 === -1 ? "" : html.slice(lt2, valueStart);
12224
12322
  }
12323
+ function writesBack(attr, prop, written) {
12324
+ if (written.split("#").slice(1).some((modifier) => modifier.trim() === "ro")) return false;
12325
+ if (prop === "radio" || prop === "checkbox") return true;
12326
+ const tag = attr.tagName;
12327
+ if (tag === void 0 || tag.includes("-")) return false;
12328
+ if (tag === "input") {
12329
+ const type = (attr.inputType || "text").toLowerCase();
12330
+ if (type === "button") return false;
12331
+ if ((type === "radio" || type === "checkbox") && prop === "checked") return true;
12332
+ return prop === "value" || prop === "valueAsNumber" || prop === "valueAsDate";
12333
+ }
12334
+ return (tag === "select" || tag === "textarea") && prop === "value";
12335
+ }
12225
12336
  function findAllBindAttributes(html, attrName) {
12226
12337
  const attrs = [];
12227
12338
  const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -12235,7 +12346,8 @@ function findAllBindAttributes(html, attrName) {
12235
12346
  const valueStart = tag.start + match.index + match[0].length;
12236
12347
  const valueEnd = html.indexOf(quote, valueStart);
12237
12348
  if (valueEnd === -1 || valueEnd > tag.end) continue;
12238
- attrs.push({ value: html.slice(valueStart, valueEnd), valueStart, tagName: tag.tagName });
12349
+ const inputType = tag.tagName === "input" ? extractAttribute(attrsText, "type") ?? void 0 : void 0;
12350
+ attrs.push({ value: html.slice(valueStart, valueEnd), valueStart, tagName: tag.tagName, ...inputType !== void 0 ? { inputType } : {} });
12239
12351
  regex.lastIndex = valueEnd - tag.start;
12240
12352
  }
12241
12353
  }
@@ -13444,6 +13556,9 @@ var BUILTIN_TAGS = {
13444
13556
  "start",
13445
13557
  "stop",
13446
13558
  "switchCamera"
13559
+ ],
13560
+ "handles": [
13561
+ "streamReady"
13447
13562
  ]
13448
13563
  },
13449
13564
  "wcs-recorder": {
@@ -14387,6 +14502,162 @@ var BUILTIN_TAGS = {
14387
14502
  }
14388
14503
  };
14389
14504
 
14505
+ // src/service/pageElements.ts
14506
+ var MAX_FILES = 32;
14507
+ var JS_TYPES = /* @__PURE__ */ new Set(["", "module", "text/javascript", "application/javascript", "text/ecmascript"]);
14508
+ function pageScripts(html) {
14509
+ const inline = [];
14510
+ const srcs = [];
14511
+ const re = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/script\s*>/gi;
14512
+ const text3 = html.replace(/<!--[\s\S]*?-->/g, (c) => " ".repeat(c.length));
14513
+ let m;
14514
+ while ((m = re.exec(text3)) !== null) {
14515
+ const attrs = m[1];
14516
+ const type = (extractAttribute(attrs, "type") ?? "").trim().toLowerCase();
14517
+ if (!JS_TYPES.has(type)) continue;
14518
+ const src = extractAttribute(attrs, "src");
14519
+ if (src !== null) srcs.push(src);
14520
+ else if (m[2].trim() !== "") inline.push(m[2]);
14521
+ }
14522
+ return { inline, srcs };
14523
+ }
14524
+ function joinRelative(from, spec) {
14525
+ if (!spec.startsWith("./") && !spec.startsWith("../")) return null;
14526
+ const dir = from.includes("/") ? from.slice(0, from.lastIndexOf("/") + 1) : "";
14527
+ const out = [];
14528
+ for (const seg of `${dir}${spec}`.split("/")) {
14529
+ if (seg === "" || seg === ".") continue;
14530
+ if (seg === ".." && out.length > 0 && out[out.length - 1] !== "..") out.pop();
14531
+ else out.push(seg);
14532
+ }
14533
+ return `./${out.join("/")}`;
14534
+ }
14535
+ function parseScript(source) {
14536
+ for (const sourceType of ["module", "script"]) {
14537
+ try {
14538
+ return parse3(source, { ecmaVersion: "latest", sourceType, allowHashBang: true });
14539
+ } catch {
14540
+ }
14541
+ }
14542
+ return null;
14543
+ }
14544
+ function* nodes(node) {
14545
+ yield node;
14546
+ for (const value of Object.values(node)) {
14547
+ if (Array.isArray(value)) {
14548
+ for (const v of value) if (v !== null && typeof v === "object" && typeof v.type === "string") yield* nodes(v);
14549
+ } else if (value !== null && typeof value === "object" && typeof value.type === "string") {
14550
+ yield* nodes(value);
14551
+ }
14552
+ }
14553
+ }
14554
+ var keyName = (key, computed) => computed ? null : key.type === "Identifier" ? key.name : key.type === "Literal" && typeof key.value === "string" ? key.value : null;
14555
+ function names(obj, key) {
14556
+ const prop = obj.properties.find((p) => p.type === "Property" && keyName(p.key, p.computed) === key);
14557
+ if (prop === void 0 || prop.type !== "Property") return void 0;
14558
+ if (prop.value.type !== "ArrayExpression") return null;
14559
+ const out = [];
14560
+ for (const el of prop.value.elements) {
14561
+ if (el === null || el.type !== "ObjectExpression" || el.properties.some((p) => p.type !== "Property")) return null;
14562
+ const name = el.properties.find((p) => p.type === "Property" && keyName(p.key, p.computed) === "name");
14563
+ if (name === void 0 || name.type !== "Property" || name.value.type !== "Literal" || typeof name.value.value !== "string") return null;
14564
+ out.push(name.value.value);
14565
+ }
14566
+ return out;
14567
+ }
14568
+ function contractOf(value) {
14569
+ if (value.type !== "ObjectExpression" || value.properties.some((p) => p.type !== "Property")) return "unknown";
14570
+ const properties = names(value, "properties");
14571
+ const inputs = names(value, "inputs");
14572
+ const commands = names(value, "commands");
14573
+ if (properties === null || inputs === null || commands === null) return "unknown";
14574
+ return {
14575
+ properties: properties ?? [],
14576
+ inputs: Object.fromEntries((inputs ?? []).map((n) => [n, null])),
14577
+ commands: commands ?? []
14578
+ };
14579
+ }
14580
+ function declaredBy(cls) {
14581
+ let declared = "none";
14582
+ for (const member of cls.body.body) {
14583
+ if (member.type !== "PropertyDefinition" && member.type !== "MethodDefinition" || !member.static) continue;
14584
+ if (keyName(member.key, member.computed) !== "wcBindable") continue;
14585
+ if (member.type === "PropertyDefinition") {
14586
+ declared = member.value === null || member.value === void 0 ? "unknown" : contractOf(member.value);
14587
+ } else {
14588
+ const body = member.kind === "get" ? member.value.body.body : [];
14589
+ const ret = body.length === 1 && body[0].type === "ReturnStatement" ? body[0].argument : null;
14590
+ declared = ret === null || ret === void 0 ? "unknown" : contractOf(ret);
14591
+ }
14592
+ }
14593
+ const sup = cls.superClass;
14594
+ return { declared, superName: sup !== null && sup !== void 0 && sup.type === "Identifier" ? sup.name : null };
14595
+ }
14596
+ function isDefine(callee) {
14597
+ if (callee.type !== "MemberExpression" || keyName(callee.property, callee.computed) !== "define") return false;
14598
+ const o = callee.object;
14599
+ return o.type === "Identifier" && o.name === "customElements" || o.type === "MemberExpression" && keyName(o.property, o.computed) === "customElements";
14600
+ }
14601
+ function collectPageElementContracts(html, fileReader) {
14602
+ const { inline, srcs } = pageScripts(html);
14603
+ const programs = [];
14604
+ const read = /* @__PURE__ */ new Set();
14605
+ const queue = srcs.map((spec) => ({ from: "", spec: spec.startsWith(".") ? spec : `./${spec}` }));
14606
+ const visit2 = (program, from) => {
14607
+ programs.push(program);
14608
+ for (const n of nodes(program)) {
14609
+ const source = n.type === "ImportDeclaration" || n.type === "ExportAllDeclaration" || n.type === "ExportNamedDeclaration" ? n.source : n.type === "ImportExpression" ? n.source : null;
14610
+ if (source !== null && source !== void 0 && source.type === "Literal" && typeof source.value === "string") queue.push({ from, spec: source.value });
14611
+ }
14612
+ };
14613
+ for (const source of inline) {
14614
+ const program = parseScript(source);
14615
+ if (program !== null) visit2(program, "");
14616
+ }
14617
+ while (queue.length > 0 && fileReader !== void 0 && read.size < MAX_FILES) {
14618
+ const { from, spec } = queue.shift();
14619
+ const path = joinRelative(from, spec);
14620
+ if (path === null || read.has(path) || !/\.m?js$/.test(path)) continue;
14621
+ read.add(path);
14622
+ const text3 = fileReader(path);
14623
+ const program = text3 === void 0 ? null : parseScript(text3);
14624
+ if (program !== null) visit2(program, path);
14625
+ }
14626
+ const classes = /* @__PURE__ */ new Map();
14627
+ const defines = [];
14628
+ for (const program of programs) {
14629
+ for (const n of nodes(program)) {
14630
+ if (n.type === "ClassDeclaration" && n.id !== null && !classes.has(n.id.name)) classes.set(n.id.name, declaredBy(n));
14631
+ else if (n.type === "VariableDeclarator" && n.id.type === "Identifier" && n.init?.type === "ClassExpression" && !classes.has(n.id.name)) {
14632
+ classes.set(n.id.name, declaredBy(n.init));
14633
+ } else if (n.type === "CallExpression" && isDefine(n.callee) && n.arguments.length >= 2) {
14634
+ const [tag, cls] = n.arguments;
14635
+ if (tag.type !== "Literal" || typeof tag.value !== "string") continue;
14636
+ if (cls.type === "ClassExpression") defines.push({ tag: tag.value, info: declaredBy(cls) });
14637
+ else if (cls.type === "Identifier") defines.push({ tag: tag.value, info: cls.name });
14638
+ }
14639
+ }
14640
+ }
14641
+ const resolve = (info, depth) => {
14642
+ if (info.declared === "unknown") return null;
14643
+ if (info.declared !== "none") return info.declared;
14644
+ const sup = info.superName === null ? void 0 : classes.get(info.superName);
14645
+ return sup === void 0 || depth > 16 ? null : resolve(sup, depth + 1);
14646
+ };
14647
+ const out = /* @__PURE__ */ new Map();
14648
+ for (const { tag, info } of defines) {
14649
+ const name = tag.toLowerCase();
14650
+ if (name.startsWith("wcs-") || out.has(name)) continue;
14651
+ const cls = typeof info === "string" ? classes.get(info) : info;
14652
+ const contract = cls === void 0 ? null : resolve(cls, 0);
14653
+ if (contract !== null) out.set(name, contract);
14654
+ }
14655
+ return out;
14656
+ }
14657
+ function mayDefineElements(html) {
14658
+ return /customElements|<script\b[^>]*\bsrc\s*=/i.test(html);
14659
+ }
14660
+
14390
14661
  // src/service/ioNodeValidator.ts
14391
14662
  var HTML_SINK_PROPERTIES = /* @__PURE__ */ new Set(["html", "innerHTML", "outerHTML", "srcdoc"]);
14392
14663
  var STRUCTURAL_DIRECTIVES3 = /* @__PURE__ */ new Set(["for", "if", "elseif", "else"]);
@@ -14401,7 +14672,6 @@ function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-s
14401
14672
  const diagnostics = [];
14402
14673
  const msgs = getMessages(locale2);
14403
14674
  const occurrences = findBuiltinTagOccurrences(html);
14404
- if (occurrences.length === 0) return diagnostics;
14405
14675
  let statePaths = null;
14406
14676
  const getPaths = () => statePaths ??= getStatePathsFromHtml(html, stateTagName, fileReader);
14407
14677
  for (const occ of occurrences) {
@@ -14429,32 +14699,35 @@ function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-s
14429
14699
  }
14430
14700
  if (contract.properties.length === 0 && contract.commands.length === 0 && Object.keys(contract.inputs).length === 0) continue;
14431
14701
  const hasManual = hasBooleanAttribute(occ.attrsText, "manual");
14432
- let exprOffset = 0;
14433
- for (const expr of splitBindingExpressions(bindAttr.value)) {
14434
- const exprStart = valueStart + exprOffset;
14435
- exprOffset += expr.length + 1;
14436
- const parsed = parseBindingExpression(expr);
14437
- const property = parsed.property;
14438
- if (!property) continue;
14439
- const propIndex = expr.indexOf(property);
14440
- const start = propIndex === -1 ? exprStart : exprStart + propIndex;
14441
- const end = propIndex === -1 ? exprStart + expr.length : start + property.length;
14442
- validateBindingAgainstContract(
14443
- occ.tagName,
14444
- contract,
14445
- parsed,
14446
- property,
14447
- start,
14448
- end,
14449
- hasManual,
14450
- getPaths,
14451
- diagnostics,
14452
- msgs
14453
- );
14702
+ checkBindings(occ.tagName, contract, bindAttr.value, valueStart, hasManual, getPaths, diagnostics, msgs);
14703
+ }
14704
+ if (mayDefineElements(html)) {
14705
+ const own2 = findPageTagOccurrences(html, bindAttribute);
14706
+ const contracts = own2.length > 0 ? collectPageElementContracts(html, fileReader) : null;
14707
+ for (const occ of own2) {
14708
+ const contract = contracts.get(occ.tagName);
14709
+ if (contract === void 0) continue;
14710
+ if (contract.properties.length === 0 && contract.commands.length === 0 && Object.keys(contract.inputs).length === 0) continue;
14711
+ const bindAttr = extractAttributeValue(occ.attrsText, bindAttribute);
14712
+ checkBindings(occ.tagName, contract, bindAttr.value, occ.attrsStart + bindAttr.valueOffsetInAttrs, false, getPaths, diagnostics, msgs);
14454
14713
  }
14455
14714
  }
14456
14715
  return diagnostics;
14457
14716
  }
14717
+ function checkBindings(tagName, contract, value, valueStart, hasManual, getPaths, diagnostics, msgs) {
14718
+ let exprOffset = 0;
14719
+ for (const expr of splitBindingExpressions(value)) {
14720
+ const exprStart = valueStart + exprOffset;
14721
+ exprOffset += expr.length + 1;
14722
+ const parsed = parseBindingExpression(expr);
14723
+ const property = parsed.property;
14724
+ if (!property) continue;
14725
+ const propIndex = expr.indexOf(property);
14726
+ const start = propIndex === -1 ? exprStart : exprStart + propIndex;
14727
+ const end = propIndex === -1 ? exprStart + expr.length : start + property.length;
14728
+ validateBindingAgainstContract(tagName, contract, parsed, property, start, end, hasManual, getPaths, diagnostics, msgs);
14729
+ }
14730
+ }
14458
14731
  function validateBindingAgainstContract(tagName, contract, parsed, property, start, end, hasManual, getPaths, diagnostics, msgs) {
14459
14732
  const hashIndex = property.indexOf("#");
14460
14733
  const modifiers = hashIndex === -1 ? "" : property.slice(hashIndex + 1);
@@ -14513,6 +14786,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
14513
14786
  }
14514
14787
  return;
14515
14788
  }
14789
+ if (!tagName.startsWith("wcs-") && property.includes(".")) return;
14516
14790
  if (!contract.properties.includes(property) && !(property in contract.inputs) && !HTML_SINK_PROPERTIES.has(property)) {
14517
14791
  const members = [...contract.properties, ...inputNames];
14518
14792
  diagnostics.push({
@@ -14526,7 +14800,19 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
14526
14800
  });
14527
14801
  return;
14528
14802
  }
14529
- if (property === "trigger" && "trigger" in contract.inputs && parsed.path) {
14803
+ if (contract.handles?.includes(property)) {
14804
+ diagnostics.push({
14805
+ code: WcsDiagnosticCode.HandleBoundAsValue,
14806
+ start,
14807
+ end,
14808
+ severity: "warning",
14809
+ tag: tagName,
14810
+ member: property,
14811
+ message: msgs.handleBoundAsValue(property, tagName)
14812
+ });
14813
+ return;
14814
+ }
14815
+ if (tagName.startsWith("wcs-") && property === "trigger" && "trigger" in contract.inputs && parsed.path) {
14530
14816
  const cand = findDataSlot(getPaths(), parsed.path);
14531
14817
  if (cand?.rawInitial === "true") {
14532
14818
  diagnostics.push({
@@ -14603,6 +14889,17 @@ function findBuiltinTagOccurrences(html) {
14603
14889
  }
14604
14890
  return out;
14605
14891
  }
14892
+ function findPageTagOccurrences(html, bindAttribute) {
14893
+ const out = [];
14894
+ for (const region of findStartTagRegions(html)) {
14895
+ const tagName = region.tagName;
14896
+ if (!tagName.includes("-") || tagName.startsWith("wcs-")) continue;
14897
+ const attrsText = html.slice(region.start, region.end);
14898
+ if (extractAttributeValue(attrsText, bindAttribute) === null) continue;
14899
+ out.push({ tagName, tagStart: region.start - tagName.length - 1, attrsText, attrsStart: region.start });
14900
+ }
14901
+ return out;
14902
+ }
14606
14903
  function extractAttributeValue(attrsText, attrName) {
14607
14904
  const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14608
14905
  const regex = new RegExp(`(?:^|\\s)${escaped}\\s*=\\s*(["'])`, "i");
@@ -15048,7 +15345,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale2, fi
15048
15345
  const scopes = {
15049
15346
  mounts: volumes?.scopes.mounts ?? [],
15050
15347
  unresolvedMounts: volumes?.scopes.unresolvedMounts ?? NO_MOUNTS,
15051
- rootUnresolved: own2.length === 0 && !isReadableStateScript(block.content)
15348
+ rootUnresolved: !scriptReadable(block.content, own2)
15052
15349
  };
15053
15350
  for (const entry of entries) {
15054
15351
  const diagnostic = validateEntry(entry, pathSet, paths, scopes, msgs);
@@ -15069,10 +15366,10 @@ var NO_MOUNTS = /* @__PURE__ */ new Set();
15069
15366
  function assignVolumes(elements, html) {
15070
15367
  const chainOf = createTemplateChain(html);
15071
15368
  const innermost = (chain) => chain.length > 0 ? chain[chain.length - 1] : -1;
15072
- const treeOf = (root) => innermost(chainOf(root.tagStart));
15369
+ const treeOf2 = (root) => innermost(chainOf(root.tagStart));
15073
15370
  const rootTrees = /* @__PURE__ */ new Set();
15074
15371
  for (const element2 of elements) {
15075
- if (element2.mountPath === null && !element2.bindComponent) rootTrees.add(treeOf(element2));
15372
+ if (element2.mountPath === null && !element2.bindComponent) rootTrees.add(treeOf2(element2));
15076
15373
  }
15077
15374
  const volumes = /* @__PURE__ */ new Map();
15078
15375
  for (const element2 of elements) {
@@ -15089,7 +15386,7 @@ function assignVolumes(elements, html) {
15089
15386
  if (list === void 0) volumes.set(tree, [element2]);
15090
15387
  else list.push(element2);
15091
15388
  }
15092
- return { treeOf, volumes };
15389
+ return { treeOf: treeOf2, volumes };
15093
15390
  }
15094
15391
  function validateEntry(entry, pathSet, paths, scopes, msgs) {
15095
15392
  const { key } = entry;
@@ -16077,8 +16374,8 @@ function validateVolumeBlock2(block, mountPath, msgs, out) {
16077
16374
  }
16078
16375
  function validateBindComponentSource(html, tagStart, tagEnd, hasInlineScript, msgs, out) {
16079
16376
  const tag = html.slice(tagStart, tagEnd);
16080
- const names = parseAttributeNames(tag);
16081
- const sources = BIND_COMPONENT_SOURCE_ATTRIBUTES.filter((name) => names.has(name));
16377
+ const names2 = parseAttributeNames(tag);
16378
+ const sources = BIND_COMPONENT_SOURCE_ATTRIBUTES.filter((name) => names2.has(name));
16082
16379
  if (hasInlineScript) sources.push('<script type="module">');
16083
16380
  if (sources.length === 0) return;
16084
16381
  const prop = /\sbind-component\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'<>`=]+))/i.exec(tag);
@@ -16206,7 +16503,7 @@ var OLD_DECLARATION_KEY = /(^|[{,;\s])(\$streams|\$updatedCallback)(?=\s*(?:[:(]
16206
16503
  function collectDeclarationAliasReads(script) {
16207
16504
  return collectStateThisReads(script, OLD_DECLARATION_MEMBER_NAMES);
16208
16505
  }
16209
- function collectStateThisReads(script, names) {
16506
+ function collectStateThisReads(script, names2) {
16210
16507
  if (!hasDefaultExportObject(script)) return null;
16211
16508
  const out = [];
16212
16509
  const take = (refs, base) => {
@@ -16215,10 +16512,10 @@ function collectStateThisReads(script, names) {
16215
16512
  return true;
16216
16513
  };
16217
16514
  for (const callable of analyzeCallableBodies(script)) {
16218
- if (!take(collectThisMemberRefs(callable.body, names), callable.bodyStart)) return null;
16515
+ if (!take(collectThisMemberRefs(callable.body, names2), callable.bodyStart)) return null;
16219
16516
  }
16220
16517
  for (const handler of analyzeWatchHandlerSources(script)) {
16221
- const refs = handler.kind === "body" ? collectThisMemberRefs(handler.text, names) : collectThisMemberRefsInValue(handler.text, names);
16518
+ const refs = handler.kind === "body" ? collectThisMemberRefs(handler.text, names2) : collectThisMemberRefsInValue(handler.text, names2);
16222
16519
  if (!take(refs, handler.start)) return null;
16223
16520
  }
16224
16521
  out.sort((a, b) => a.start - b.start);
@@ -16426,6 +16723,26 @@ function validateGetterLocationReads(script, scriptStart, locale2) {
16426
16723
  }
16427
16724
  return out;
16428
16725
  }
16726
+ var CLOCK_READ = /\bDate\s*\.\s*now\s*\(|\bnew\s+Date\s*\(\s*\)|\bperformance\s*\.\s*now\s*\(/;
16727
+ function validateGetterClockReads(script, scriptStart, locale2) {
16728
+ if (!/Date|performance/.test(script)) return [];
16729
+ const msgs = getMessages(locale2);
16730
+ const out = [];
16731
+ for (const getter of analyzeCallableBodies(script)) {
16732
+ if (getter.kind !== "getter" || getter.accessor !== "get") continue;
16733
+ const read = CLOCK_READ.exec(maskCommentsAndStrings(getter.body));
16734
+ if (read === null) continue;
16735
+ const text3 = read[0].startsWith("new") ? "new Date()" : read[0].startsWith("performance") ? "performance.now()" : "Date.now()";
16736
+ out.push({
16737
+ code: WcsDiagnosticCode.GetterUntrackedRead,
16738
+ start: scriptStart + getter.bodyStart + read.index,
16739
+ end: scriptStart + getter.bodyStart + read.index + read[0].length,
16740
+ message: msgs.getterReadsClock(text3),
16741
+ severity: "warning"
16742
+ });
16743
+ }
16744
+ return out;
16745
+ }
16429
16746
  function onHandlerStateParam(script, handler) {
16430
16747
  if (handler.kind === "body") {
16431
16748
  const head = script.slice(Math.max(0, handler.start - 400), handler.start);
@@ -16623,15 +16940,22 @@ function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2
16623
16940
  const reported = /* @__PURE__ */ new Set();
16624
16941
  while ((match = PATH_TEST_LITERAL.exec(body)) !== null) {
16625
16942
  const path = match[2];
16626
- if (path.length === 0 || !declared.has(path) || bound.has(path)) continue;
16627
- if (reported.has(path)) continue;
16943
+ if (path.length === 0 || bound.has(path) || reported.has(path)) continue;
16944
+ let message;
16945
+ if (declared.has(path)) {
16946
+ message = msgs.updatedCallbackUnbound(path);
16947
+ } else {
16948
+ const near = suggestion(path, [...declared, ...bound], msgs);
16949
+ if (near === "") continue;
16950
+ message = msgs.updatedCallbackUnknownPath(path) + near;
16951
+ }
16628
16952
  reported.add(path);
16629
16953
  const quoteAt = match.index + match[0].length - path.length - 1;
16630
16954
  out.push({
16631
16955
  code: WcsDiagnosticCode.UpdatedCallbackUnbound,
16632
16956
  start: block.contentStart + callback.bodyStart + quoteAt,
16633
16957
  end: block.contentStart + callback.bodyStart + quoteAt + path.length,
16634
- message: msgs.updatedCallbackUnbound(path),
16958
+ message,
16635
16959
  severity: "warning"
16636
16960
  });
16637
16961
  }
@@ -16644,8 +16968,8 @@ function collectBoundPaths(html, stateTagName, bindAttrName) {
16644
16968
  for (const occurrence of index.occurrences) {
16645
16969
  bound.add(occurrence.path);
16646
16970
  if (!occurrence.path.startsWith(".")) continue;
16647
- const forPath = getInnermostForPath(html, occurrence.pathRange.start, bindAttrName);
16648
- if (forPath === null || forPath.startsWith(".")) continue;
16971
+ const forPath = getResolvedForListPath(html, occurrence.pathRange.start, bindAttrName);
16972
+ if (forPath === null) continue;
16649
16973
  bound.add(
16650
16974
  occurrence.path === "." ? `${forPath}.*` : `${forPath}.*.${occurrence.path.slice(1)}`
16651
16975
  );
@@ -16718,6 +17042,7 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
16718
17042
  out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
16719
17043
  out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
16720
17044
  out.push(...validateGetterLocationReads(block.content, block.contentStart, locale2));
17045
+ out.push(...validateGetterClockReads(block.content, block.contentStart, locale2));
16721
17046
  out.push(...validateOnHandlerThis(block.content, block.contentStart, locale2));
16722
17047
  out.push(...validateInPlaceChanges(block.content, block.contentStart, locale2));
16723
17048
  }
@@ -16726,6 +17051,169 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
16726
17051
  return out;
16727
17052
  }
16728
17053
 
17054
+ // src/service/scriptStateUseValidator.ts
17055
+ var COMMAND_USE = /\.\s*\$command\s*\.\s*([A-Za-z_$][\w$]*)/dg;
17056
+ var EMIT_AFTER = /^\s*\.\s*emit\s*\(/;
17057
+ var THIS_WRITE = /\bthis\s*\.\s*([A-Za-z_][\w$]*)\s*(?:=(?![=>])|[-+*/%&|^]=|\*\*=|<<=|>>>?=|&&=|\|\|=|\?\?=|\+\+|--)/dg;
17058
+ var PRE_THIS_WRITE = /(?:\+\+|--)\s*this\s*\.\s*([A-Za-z_][\w$]*)/dg;
17059
+ function validateScriptStateUses(html, stateTagName = "wcs-state", locale2, bindAttrName = "data-wcs") {
17060
+ const blocks = parseLoadedScriptBlocks(html, stateTagName).filter((block) => isReadableStateScript(block.content));
17061
+ if (blocks.length === 0) return [];
17062
+ const msgs = getMessages(locale2);
17063
+ const out = [];
17064
+ let receivers = null;
17065
+ let markupEmits = null;
17066
+ const bindings = () => {
17067
+ if (receivers !== null) return;
17068
+ receivers = /* @__PURE__ */ new Set();
17069
+ markupEmits = /* @__PURE__ */ new Map();
17070
+ for (const o of buildReferenceIndex(html, { bindAttribute: bindAttrName, stateTagName }).occurrences) {
17071
+ if (!o.path.startsWith("$command.")) continue;
17072
+ const name = o.path.slice("$command.".length);
17073
+ const prop = o.propName ?? "";
17074
+ if (prop.startsWith("command.")) receivers.add(name);
17075
+ else if (prop.startsWith("on") && !markupEmits.has(name)) markupEmits.set(name, o.pathRange);
17076
+ }
17077
+ };
17078
+ for (const block of blocks) {
17079
+ const paths = analyzeStatePaths(block.content);
17080
+ const masked = maskCommentsAndStrings(block.content);
17081
+ const declared = paths.filter((p) => p.kind === "command").map((p) => p.path.slice("$command.".length));
17082
+ if (block.mountPath === null && declared.length > 0) {
17083
+ const emitted = /* @__PURE__ */ new Map();
17084
+ COMMAND_USE.lastIndex = 0;
17085
+ let match;
17086
+ while ((match = COMMAND_USE.exec(masked)) !== null) {
17087
+ const name = match[1];
17088
+ const [start, end] = match.indices[1];
17089
+ if (!declared.includes(name)) {
17090
+ out.push({
17091
+ code: WcsDiagnosticCode.TokenUndeclared,
17092
+ start: block.contentStart + start,
17093
+ end: block.contentStart + end,
17094
+ // `$command` holds the declared tokens only: `.emit()` on what it lacks is a TypeError
17095
+ message: msgs.commandTokenUndeclared(`$command.${name}`) + suggestion(name, declared, msgs),
17096
+ severity: "error"
17097
+ });
17098
+ } else if (!emitted.has(name) && EMIT_AFTER.test(masked.slice(match.index + match[0].length))) {
17099
+ emitted.set(name, { start: block.contentStart + start, end: block.contentStart + end });
17100
+ }
17101
+ }
17102
+ bindings();
17103
+ for (const name of declared) {
17104
+ if (receivers.has(name)) continue;
17105
+ const at2 = emitted.get(name) ?? markupEmits.get(name);
17106
+ if (at2 === void 0) continue;
17107
+ out.push({
17108
+ code: WcsDiagnosticCode.CommandTokenUnbound,
17109
+ start: at2.start,
17110
+ end: at2.end,
17111
+ message: msgs.commandTokenUnbound(`$command.${name}`),
17112
+ severity: "warning"
17113
+ });
17114
+ }
17115
+ }
17116
+ const keys = [...new Set(paths.map((p) => p.path.split(".")[0]).filter((key) => key !== "" && !key.startsWith("$")))];
17117
+ const reported = /* @__PURE__ */ new Set();
17118
+ for (const pattern of [THIS_WRITE, PRE_THIS_WRITE]) {
17119
+ pattern.lastIndex = 0;
17120
+ let match;
17121
+ while ((match = pattern.exec(masked)) !== null) {
17122
+ const key = match[1];
17123
+ if (keys.includes(key) || reported.has(key)) continue;
17124
+ const near = suggestion(key, keys, msgs);
17125
+ if (near === "") continue;
17126
+ reported.add(key);
17127
+ const [start, end] = match.indices[1];
17128
+ out.push({
17129
+ code: WcsDiagnosticCode.WritePathMissing,
17130
+ start: block.contentStart + start,
17131
+ end: block.contentStart + end,
17132
+ message: msgs.writePathMissing(key) + near,
17133
+ severity: "warning"
17134
+ });
17135
+ }
17136
+ }
17137
+ }
17138
+ return out;
17139
+ }
17140
+
17141
+ // src/service/scriptReadValidator.ts
17142
+ function validateScriptReads(html, stateTagName = "wcs-state", locale2, fileReader, applicationSchema) {
17143
+ const blocks = parseLoadedScriptBlocks(html, stateTagName);
17144
+ if (blocks.length === 0) return [];
17145
+ const msgs = getMessages(locale2);
17146
+ const index = getStatePathIndex(html, stateTagName, fileReader);
17147
+ const candidates = mergeSchemaCandidates(index.paths, applicationSchema);
17148
+ const candidateSet = new Set(candidates.map((p) => p.path));
17149
+ let tree = null;
17150
+ const out = [];
17151
+ for (const block of blocks) {
17152
+ const spread = applicationSchema === void 0 ? findTopLevelSpread(block.content) : null;
17153
+ if (spread !== null) {
17154
+ out.push({
17155
+ code: WcsDiagnosticCode.PathDynamicUnknown,
17156
+ start: block.contentStart + spread.start,
17157
+ end: block.contentStart + spread.end,
17158
+ message: msgs.pathDynamicUnknownSpread(block.content.slice(spread.start, spread.end)),
17159
+ severity: "info"
17160
+ });
17161
+ }
17162
+ const prefix = block.mountPath === null || block.mountPath === "" ? "" : `${block.mountPath}.`;
17163
+ for (const callable of analyzeCallableBodies(block.content)) {
17164
+ for (const read of collectGetterReads(callable.body) ?? []) {
17165
+ if (read.written || read.path.split(".").some((s) => s === "" || s.startsWith("$")) || hasRecursionWildcard(read.path)) continue;
17166
+ const path = prefix + read.path;
17167
+ if (pathExistsInCandidates(path, candidates, candidateSet)) continue;
17168
+ if (applicationSchema !== void 0 ? resolveIndexedSchemaPath(applicationSchema, path).kind !== "nonexistent" : isUnresolvedPath(path, index.scopes)) continue;
17169
+ const near = nearName(path, tree ??= treeOf(candidates));
17170
+ if (near === null) continue;
17171
+ const said = suggestion(near.segment, near.names, msgs);
17172
+ if (said === "") continue;
17173
+ const at2 = block.contentStart + callable.bodyStart;
17174
+ out.push({
17175
+ code: WcsDiagnosticCode.ReadPathMissing,
17176
+ start: at2 + read.start,
17177
+ end: read.form === "member" ? at2 + read.start + statePart(callable.body.slice(read.start, read.end)) : at2 + read.end,
17178
+ message: msgs.readPathMissing(read.path, !read.path.includes(".")) + said,
17179
+ // a stateSchema says the path is not there, as for a binding's `wcs/path-nonexistent`
17180
+ severity: applicationSchema !== void 0 ? "error" : "warning"
17181
+ });
17182
+ }
17183
+ }
17184
+ }
17185
+ return out;
17186
+ }
17187
+ var THIS_PART = /^this\s*(?:\?\.\s*|\.\s*)(?:[A-Za-z_$][\w$]*)|^this\s*(?:\?\.)?\s*\[\s*(["'`])[^"'`]*\1\s*\]/;
17188
+ function statePart(text3) {
17189
+ return THIS_PART.exec(text3)?.[0].length ?? text3.length;
17190
+ }
17191
+ function treeOf(candidates) {
17192
+ const tree = /* @__PURE__ */ new Map();
17193
+ for (const c of candidates) {
17194
+ let parent = "";
17195
+ for (const segment of c.path.split(".")) {
17196
+ let names2 = tree.get(parent);
17197
+ if (names2 === void 0) tree.set(parent, names2 = /* @__PURE__ */ new Set());
17198
+ names2.add(segment);
17199
+ parent = parent === "" ? segment : `${parent}.${segment}`;
17200
+ }
17201
+ }
17202
+ return tree;
17203
+ }
17204
+ function nearName(path, tree) {
17205
+ let parent = "";
17206
+ for (const segment of (hasIndexSegment(path) ? toWildcardForm(path) : path).split(".")) {
17207
+ const names2 = tree.get(parent);
17208
+ if (names2?.has(segment)) {
17209
+ parent = parent === "" ? segment : `${parent}.${segment}`;
17210
+ continue;
17211
+ }
17212
+ return { segment, names: [...names2 ?? []].filter((name) => name !== "*" && !name.startsWith("$")) };
17213
+ }
17214
+ return null;
17215
+ }
17216
+
16729
17217
  // src/core/sidecar/discover.ts
16730
17218
  var APPLICATION_MANIFEST_FILENAME = "wcstack.manifest.json";
16731
17219
  var MAX_ASCEND = 16;
@@ -16750,13 +17238,129 @@ function applicationSchemaOf(loaded) {
16750
17238
  return void 0;
16751
17239
  }
16752
17240
 
17241
+ // src/core/offsetToPosition.ts
17242
+ function createPositionMapper(text3) {
17243
+ const lineStarts = [0];
17244
+ for (let i = 0; i < text3.length; i++) {
17245
+ const c = text3.charCodeAt(i);
17246
+ if (c === 10) {
17247
+ lineStarts.push(i + 1);
17248
+ } else if (c === 13) {
17249
+ if (text3.charCodeAt(i + 1) === 10) i++;
17250
+ lineStarts.push(i + 1);
17251
+ }
17252
+ }
17253
+ return (offset2) => {
17254
+ const clamped = Math.max(0, Math.min(offset2, text3.length));
17255
+ let lo = 0;
17256
+ let hi = lineStarts.length - 1;
17257
+ while (lo < hi) {
17258
+ const mid = lo + hi + 1 >> 1;
17259
+ if (lineStarts[mid] <= clamped) lo = mid;
17260
+ else hi = mid - 1;
17261
+ }
17262
+ return { line: lo + 1, column: clamped - lineStarts[lo] + 1 };
17263
+ };
17264
+ }
17265
+
17266
+ // src/core/externalState.ts
17267
+ var OPEN = '<script type="module">';
17268
+ var CLOSE = "</script>";
17269
+ function inlineExternalStates(html, stateTagName, fileReader) {
17270
+ let text3 = "";
17271
+ let from = 0;
17272
+ let shift = 0;
17273
+ const states = [];
17274
+ for (const element2 of parseWcsStateElements(html, stateTagName)) {
17275
+ const src = element2.srcSpan;
17276
+ if (src === void 0 || element2.srcAttr === void 0 || element2.stateAttr !== void 0 || element2.bindComponent) continue;
17277
+ const file = readStateScriptFile(element2.srcAttr, fileReader);
17278
+ if (file === void 0) continue;
17279
+ const blanks = [src];
17280
+ if (element2.jsonSpan !== void 0) blanks.push(element2.jsonSpan);
17281
+ blanks.sort((a, b) => a.start - b.start);
17282
+ for (const span of blanks) {
17283
+ text3 += html.slice(from, span.start) + " ".repeat(span.end - span.start);
17284
+ from = span.end;
17285
+ }
17286
+ text3 += html.slice(from, element2.tagEnd);
17287
+ from = element2.tagEnd;
17288
+ const content = file.text.replace(/<\/(script)/gi, "<\\$1");
17289
+ const at2 = element2.tagEnd + shift;
17290
+ const length = OPEN.length + content.length + CLOSE.length;
17291
+ text3 += OPEN + content + CLOSE;
17292
+ states.push({
17293
+ path: file.path,
17294
+ text: file.text,
17295
+ at: at2,
17296
+ contentAt: at2 + OPEN.length,
17297
+ length,
17298
+ valueStart: src.valueStart,
17299
+ valueEnd: src.valueEnd,
17300
+ position: createPositionMapper(file.text)
17301
+ });
17302
+ shift += length;
17303
+ }
17304
+ if (states.length === 0) return null;
17305
+ return { text: text3 + html.slice(from), states };
17306
+ }
17307
+ function locateInlined(page, offset2) {
17308
+ let shift = 0;
17309
+ for (const state of page.states) {
17310
+ if (offset2 < state.at) break;
17311
+ if (offset2 < state.at + state.length) {
17312
+ return { state, offset: Math.min(Math.max(offset2 - state.contentAt, 0), state.text.length) };
17313
+ }
17314
+ shift += state.length;
17315
+ }
17316
+ return { offset: offset2 - shift };
17317
+ }
17318
+
16753
17319
  // src/core/validateDocument.ts
16754
17320
  function validateDocument(text3, options = {}) {
17321
+ const stateTagName = options.stateTagName ?? "wcs-state";
17322
+ const fileReader = options.fileReader;
17323
+ const applicationSchema = options.applicationSchema ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.schema : void 0);
17324
+ const found = validateText(text3, options, applicationSchema);
17325
+ const page = fileReader === void 0 ? null : inlineExternalStates(text3, stateTagName, fileReader);
17326
+ if (page === null) return found;
17327
+ const key = (d) => [d.code, d.start, d.end, d.message, d.origin?.path, d.origin?.start].join("\0");
17328
+ const seen2 = new Set(found.map(key));
17329
+ const out = [...found];
17330
+ for (const d of validateText(page.text, options, applicationSchema)) {
17331
+ const start = locateInlined(page, d.start);
17332
+ const end = locateInlined(page, d.end);
17333
+ let mapped;
17334
+ if (start.state !== void 0) {
17335
+ const state = start.state;
17336
+ const fileEnd = end.state === state ? Math.max(end.offset, start.offset) : start.offset;
17337
+ mapped = {
17338
+ ...d,
17339
+ start: state.valueStart,
17340
+ end: state.valueEnd,
17341
+ origin: {
17342
+ path: state.path,
17343
+ start: start.offset,
17344
+ end: fileEnd,
17345
+ startPosition: state.position(start.offset),
17346
+ endPosition: state.position(fileEnd)
17347
+ }
17348
+ };
17349
+ } else {
17350
+ mapped = { ...d, start: start.offset, end: end.state === void 0 ? Math.max(end.offset, start.offset) : start.offset };
17351
+ }
17352
+ const k = key(mapped);
17353
+ if (seen2.has(k)) continue;
17354
+ seen2.add(k);
17355
+ out.push(mapped);
17356
+ }
17357
+ return sortDiagnostics(out);
17358
+ }
17359
+ function validateText(text3, options, applicationSchema) {
16755
17360
  const bindAttribute = options.bindAttribute ?? "data-wcs";
16756
17361
  const stateTagName = options.stateTagName ?? "wcs-state";
16757
17362
  const locale2 = options.locale;
16758
17363
  const fileReader = options.fileReader;
16759
- const applicationSchema = options.applicationSchema ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.schema : void 0);
16760
17364
  const out = [];
16761
17365
  out.push(...validateBindings(text3, bindAttribute, stateTagName, locale2, fileReader, applicationSchema));
16762
17366
  out.push(...validateTemplateSyntax(text3, stateTagName, bindAttribute, locale2, fileReader, applicationSchema));
@@ -16765,6 +17369,8 @@ function validateDocument(text3, options = {}) {
16765
17369
  out.push(...validateAriaAttributes(text3, bindAttribute, locale2));
16766
17370
  out.push(...validateDocumentEnv(text3, locale2));
16767
17371
  out.push(...validateSemantics(text3, stateTagName, locale2, bindAttribute));
17372
+ out.push(...validateScriptStateUses(text3, stateTagName, locale2, bindAttribute));
17373
+ out.push(...validateScriptReads(text3, stateTagName, locale2, fileReader, applicationSchema));
16768
17374
  out.push(...validateArrayMutations(text3, stateTagName, locale2));
16769
17375
  out.push(...validateWatchDeclarations(text3, stateTagName, locale2, fileReader));
16770
17376
  out.push(...validateScanDeclarations(text3, stateTagName, locale2));