@wcstack/typescript 4.3.0 → 4.4.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` の添字の本数がパスの `*` の本数と噛み合わない。
@@ -448,8 +463,8 @@ function derefUnion(node, rootDefs) {
448
463
  }
449
464
  return { kind: "ok", nodes: out };
450
465
  }
451
- function firstDefined(nodes, pick) {
452
- for (const n of nodes) {
466
+ function firstDefined(nodes2, pick) {
467
+ for (const n of nodes2) {
453
468
  const v = pick(n);
454
469
  if (v !== void 0) return v;
455
470
  }
@@ -1012,8 +1027,8 @@ function checkPathLikeGetPathInfo(path) {
1012
1027
  function recursionUnsupported(path) {
1013
1028
  raise(1101, [path]);
1014
1029
  }
1015
- function parseStatePart(statePart) {
1016
- const [stateAndPath, filters] = splitFilters(statePart, "output", cacheFilterInfos);
1030
+ function parseStatePart(statePart2) {
1031
+ const [stateAndPath, filters] = splitFilters(statePart2, "output", cacheFilterInfos);
1017
1032
  if (stateAndPath.includes("@")) {
1018
1033
  raise(32, [stateAndPath]);
1019
1034
  }
@@ -1022,7 +1037,7 @@ function parseStatePart(statePart) {
1022
1037
  const body = isLoopRelative ? statePathName.slice(DELIMITER.length) : statePathName;
1023
1038
  const hasEmptySegment = body.length > 0 && body.split(DELIMITER).some((segment) => segment.length === 0);
1024
1039
  if (hasEmptySegment || !isLoopRelative && body.length === 0) {
1025
- raise(119, [statePart]);
1040
+ raise(119, [statePart2]);
1026
1041
  }
1027
1042
  checkPathLikeGetPathInfo(statePathName);
1028
1043
  return {
@@ -1584,7 +1599,7 @@ function parseBindTextsForElement(bindText) {
1584
1599
  raise(101, [bindText2]);
1585
1600
  }
1586
1601
  const propPart = bindText2.slice(0, separatorIndex).trim();
1587
- const statePart = bindText2.slice(separatorIndex + 1).trim();
1602
+ const statePart2 = bindText2.slice(separatorIndex + 1).trim();
1588
1603
  const keyword = propPart.split(MODIFIER_SEPARATOR)[0].split(FILTER_SEPARATOR)[0].trim();
1589
1604
  if (KEYWORDS_WITHOUT_MODIFIERS.has(keyword)) {
1590
1605
  if (keyword !== propPart) {
@@ -1592,20 +1607,20 @@ function parseBindTextsForElement(bindText) {
1592
1607
  }
1593
1608
  let stateResult2;
1594
1609
  if (keyword === ELSE_KEYWORD) {
1595
- if (statePart.length > 0) {
1610
+ if (statePart2.length > 0) {
1596
1611
  raise(103, [bindText2]);
1597
1612
  }
1598
1613
  stateResult2 = { statePathName: "#else", outFilters: [] };
1599
1614
  } else if (keyword === SPREAD_PROP) {
1600
- if (statePart.length === 0) {
1615
+ if (statePart2.length === 0) {
1601
1616
  raise(104, [bindText2]);
1602
1617
  }
1603
- stateResult2 = parseStatePart(statePart);
1618
+ stateResult2 = parseStatePart(statePart2);
1604
1619
  if (stateResult2.outFilters.length > 0) {
1605
1620
  raise(105, [bindText2]);
1606
1621
  }
1607
1622
  } else {
1608
- stateResult2 = parseStatePart(statePart);
1623
+ stateResult2 = parseStatePart(statePart2);
1609
1624
  if (keyword === "for" && stateResult2.outFilters.length > 0) {
1610
1625
  raise(121, [bindText2]);
1611
1626
  }
@@ -1622,7 +1637,7 @@ function parseBindTextsForElement(bindText) {
1622
1637
  if (keyword === CLASS_NAMESPACE || keyword === ATTR_NAMESPACE) {
1623
1638
  raise(122, [bindText2, keyword]);
1624
1639
  }
1625
- const stateResult = parseStatePart(statePart);
1640
+ const stateResult = parseStatePart(statePart2);
1626
1641
  const propResult = parsePropPart(propPart);
1627
1642
  const [head] = propResult.propSegments;
1628
1643
  if (head === "" && propResult.propSegments.length > 1) {
@@ -1868,6 +1883,9 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
1868
1883
  const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
1869
1884
  const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
1870
1885
  const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
1886
+ const contentAt = pos + 1 + stateTagName.length;
1887
+ const srcSpan = extractAttributeSpan(wcsMatch.tagContent, "src", contentAt);
1888
+ const jsonSpan = extractAttributeSpan(wcsMatch.tagContent, "json", contentAt);
1871
1889
  const tagStart = pos;
1872
1890
  const tagEnd = wcsMatch.end;
1873
1891
  pos = wcsMatch.end;
@@ -1911,7 +1929,18 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
1911
1929
  wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
1912
1930
  }
1913
1931
  }
1914
- elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
1932
+ elements.push({
1933
+ mountPath,
1934
+ bindComponent,
1935
+ jsonAttr,
1936
+ stateAttr,
1937
+ srcAttr,
1938
+ scriptBlocks,
1939
+ tagStart,
1940
+ tagEnd,
1941
+ ...srcSpan !== null ? { srcSpan } : {},
1942
+ ...jsonSpan !== null ? { jsonSpan } : {}
1943
+ });
1915
1944
  pos = wcsEnd;
1916
1945
  if (wcsCloseIdx !== -1) pos = afterCloseTag(html, wcsCloseIdx);
1917
1946
  }
@@ -2046,13 +2075,13 @@ function findCloseTag(html, startPos, tagName, lower2 = asciiLowerCase(html)) {
2046
2075
  return -1;
2047
2076
  }
2048
2077
  function parseAttributeNames(tagContent) {
2049
- const names = /* @__PURE__ */ new Set();
2078
+ const names2 = /* @__PURE__ */ new Set();
2050
2079
  const attribute = /([^\s"'<>/=]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'<>`=]+))?/g;
2051
2080
  let match;
2052
2081
  while ((match = attribute.exec(tagContent)) !== null) {
2053
- names.add(match[1].toLowerCase());
2082
+ names2.add(match[1].toLowerCase());
2054
2083
  }
2055
- return names;
2084
+ return names2;
2056
2085
  }
2057
2086
  function extractAttribute(tagContent, attrName) {
2058
2087
  const regex = new RegExp(
@@ -2063,6 +2092,16 @@ function extractAttribute(tagContent, attrName) {
2063
2092
  if (!match) return null;
2064
2093
  return match[1] ?? match[2] ?? match[3] ?? null;
2065
2094
  }
2095
+ function extractAttributeSpan(tagContent, attrName, at2) {
2096
+ const regex = new RegExp(`(^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`, "i");
2097
+ const match = regex.exec(tagContent);
2098
+ if (match === null) return null;
2099
+ const start = at2 + match.index + match[1].length;
2100
+ const end = at2 + match.index + match[0].length;
2101
+ const value = match[2] ?? match[3] ?? match[4] ?? "";
2102
+ const valueEnd = match[4] === void 0 ? end - 1 : end;
2103
+ return { start, end, valueStart: valueEnd - value.length, valueEnd };
2104
+ }
2066
2105
  var IGNORE_VOID_ELEMENTS = /* @__PURE__ */ new Set([
2067
2106
  "area",
2068
2107
  "base",
@@ -3417,17 +3456,25 @@ function hasDefaultExportObject(scriptContent) {
3417
3456
  return locateDefaultExportObject(scriptContent) !== null;
3418
3457
  }
3419
3458
  function hasTopLevelSpread(scriptContent) {
3459
+ return findTopLevelSpread(scriptContent) !== null;
3460
+ }
3461
+ function findTopLevelSpread(scriptContent) {
3420
3462
  const root = locateDefaultExportObject(scriptContent);
3421
- if (!root) return false;
3463
+ if (!root) return null;
3422
3464
  const scan = maskCommentsAndStrings(root.content);
3423
3465
  let depth = 0;
3466
+ let start = -1;
3424
3467
  for (let i = 0; i < scan.length; i++) {
3425
3468
  const ch = scan[i];
3426
3469
  if (ch === "(" || ch === "[" || ch === "{") depth++;
3427
3470
  else if (ch === ")" || ch === "]" || ch === "}") depth--;
3428
- else if (depth === 0 && ch === "." && scan.startsWith("...", i)) return true;
3471
+ else if (depth === 0 && start < 0 && ch === "." && scan.startsWith("...", i)) start = i;
3472
+ else if (depth === 0 && start >= 0 && ch === ",") return spreadRange(root.start, scan, start, i);
3429
3473
  }
3430
- return false;
3474
+ return start < 0 ? null : spreadRange(root.start, scan, start, scan.length);
3475
+ }
3476
+ function spreadRange(base, scan, start, stop) {
3477
+ return { start: base + start, end: base + start + scan.slice(start, stop).trimEnd().length };
3431
3478
  }
3432
3479
  function analyzeObjectEntries(scriptContent, key, allowEmptyKeys = false) {
3433
3480
  const root = locateDefaultExportObject(scriptContent);
@@ -4381,9 +4428,9 @@ function derefSchemaNodes(node, defs) {
4381
4428
  }
4382
4429
  return out;
4383
4430
  }
4384
- function schemaTypeHint(nodes) {
4431
+ function schemaTypeHint(nodes2) {
4385
4432
  const hints = /* @__PURE__ */ new Set();
4386
- for (const n of nodes) {
4433
+ for (const n of nodes2) {
4387
4434
  const types2 = typeof n.type === "string" ? [n.type] : Array.isArray(n.type) ? n.type : [];
4388
4435
  if (types2.length > 0) {
4389
4436
  for (const t of types2) {
@@ -4422,10 +4469,10 @@ function collectSchemaObjectPaths(node, prefix, paths, defs, depth) {
4422
4469
  }
4423
4470
  }
4424
4471
  function pushSchemaValuePaths(path, node, paths, defs, depth) {
4425
- const nodes = derefSchemaNodes(node, defs);
4426
- const typeHint = schemaTypeHint(nodes);
4472
+ const nodes2 = derefSchemaNodes(node, defs);
4473
+ const typeHint = schemaTypeHint(nodes2);
4427
4474
  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");
4475
+ const items = nodes2.map((n) => n.items).find((i) => i !== void 0 && i !== null && typeof i === "object");
4429
4476
  const isArray2 = items !== void 0 || (typeHint?.split("|").includes("array") ?? false);
4430
4477
  if (isArray2) {
4431
4478
  const itemNodes = items !== void 0 ? derefSchemaNodes(items, defs) : [];
@@ -4442,7 +4489,7 @@ function pushSchemaValuePaths(path, node, paths, defs, depth) {
4442
4489
  }
4443
4490
  return;
4444
4491
  }
4445
- if (nodes.some((n) => n.properties !== void 0)) {
4492
+ if (nodes2.some((n) => n.properties !== void 0)) {
4446
4493
  collectSchemaObjectPaths(node, path, paths, defs, depth + 1);
4447
4494
  }
4448
4495
  }
@@ -4541,9 +4588,12 @@ function fromJson(text3) {
4541
4588
  const paths = analyzeJsonPaths(text3);
4542
4589
  return { paths, readable: paths.length > 0 || isJsonObject(text3) };
4543
4590
  }
4591
+ function scriptReadable(script, paths) {
4592
+ return !hasTopLevelSpread(script) && (paths.length > 0 || hasDefaultExportObject(script));
4593
+ }
4544
4594
  function fromScript(script) {
4545
4595
  const paths = analyzeStatePaths(script);
4546
- return { paths, readable: paths.length > 0 || isReadableStateScript(script) };
4596
+ return { paths, readable: scriptReadable(script, paths) };
4547
4597
  }
4548
4598
  function resolveElementPathsRaw(element2, html, fileReader) {
4549
4599
  let readable = false;
@@ -4566,19 +4616,23 @@ function resolveElementPathsRaw(element2, html, fileReader) {
4566
4616
  readable ||= resolved.readable;
4567
4617
  }
4568
4618
  if (element2.scriptBlocks.length > 0) {
4569
- const paths = element2.scriptBlocks.flatMap((block) => analyzeStatePaths(block.content));
4619
+ const blocks = element2.scriptBlocks.map((block) => ({ script: block.content, paths: analyzeStatePaths(block.content) }));
4570
4620
  return {
4571
- paths,
4572
- readable: paths.length > 0 || readable || element2.scriptBlocks.some((block) => isReadableStateScript(block.content))
4621
+ paths: blocks.flatMap((block) => block.paths),
4622
+ readable: readable || blocks.some((block) => scriptReadable(block.script, block.paths))
4573
4623
  };
4574
4624
  }
4575
4625
  return readable ? { paths: [], readable } : UNREADABLE;
4576
4626
  }
4577
4627
  function readStateScript(srcPath, fileReader) {
4578
- if (srcPath.endsWith(".js")) {
4579
- return fileReader(srcPath.replace(/\.js$/, ".ts")) || fileReader(srcPath) || void 0;
4628
+ return readStateScriptFile(srcPath, fileReader)?.text;
4629
+ }
4630
+ function readStateScriptFile(srcPath, fileReader) {
4631
+ const paths = srcPath.endsWith(".js") ? [srcPath.replace(/\.js$/, ".ts"), srcPath] : srcPath.endsWith(".ts") ? [srcPath] : [];
4632
+ for (const path of paths) {
4633
+ const text3 = fileReader(path);
4634
+ if (text3) return { path, text: text3 };
4580
4635
  }
4581
- if (srcPath.endsWith(".ts")) return fileReader(srcPath) || void 0;
4582
4636
  return void 0;
4583
4637
  }
4584
4638
  function resolveSrcAttribute(srcPath, fileReader) {
@@ -4611,10 +4665,6 @@ function isInsideForTemplate(html, offset2, bindAttrName = "data-wcs") {
4611
4665
  }
4612
4666
  return false;
4613
4667
  }
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
4668
  var BINDING_SYNTAX_MARKER = `[${WcsDiagnosticCode.BindingSyntax}]`;
4619
4669
  function isForBindingRefused(bindText) {
4620
4670
  try {
@@ -4860,10 +4910,10 @@ function analyzeElementContexts(html, offsets, bindAttrName = "data-wcs", stateT
4860
4910
  }
4861
4911
  const match3 = bindAttr.exec(attrs);
4862
4912
  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("#");
4913
+ const names2 = value === null ? [] : propertyNamesOf(value);
4914
+ const structural = name === "template" && names2.length === 1 && STRUCTURAL_DIRECTIVES2.has(names2[0]) && !value.slice(0, Math.max(0, indexOfOutsideQuotes2(value, ":"))).includes("#");
4865
4915
  const plainTemplate = name === "template" && !structural;
4866
- const blocks = plainTemplate && stack.some((e) => e.name === "template") || name !== "template" && names.some((n) => CONTENT_PROPERTIES.has(n));
4916
+ const blocks = plainTemplate && stack.some((e) => e.name === "template") || name !== "template" && names2.some((n) => CONTENT_PROPERTIES.has(n));
4867
4917
  const scope = plainTemplate ? { hasOwnState: false, id: extractAttribute(attrs, "id"), underRouter: stack.some((e) => e.name === ROUTER_TAG) } : null;
4868
4918
  stack.push({ name, structural, scope, blocks });
4869
4919
  }
@@ -6320,7 +6370,7 @@ pp$8.parseClassElement = function(constructorAllowsSuper) {
6320
6370
  }
6321
6371
  var ecmaVersion = this.options.ecmaVersion;
6322
6372
  var node = this.startNode();
6323
- var keyName = "";
6373
+ var keyName2 = "";
6324
6374
  var isGenerator = false;
6325
6375
  var isAsync = false;
6326
6376
  var kind = "method";
@@ -6333,34 +6383,34 @@ pp$8.parseClassElement = function(constructorAllowsSuper) {
6333
6383
  if (this.isClassElementNameStart() || this.type === types$1.star) {
6334
6384
  isStatic = true;
6335
6385
  } else {
6336
- keyName = "static";
6386
+ keyName2 = "static";
6337
6387
  }
6338
6388
  }
6339
6389
  node.static = isStatic;
6340
- if (!keyName && ecmaVersion >= 8 && this.eatContextual("async")) {
6390
+ if (!keyName2 && ecmaVersion >= 8 && this.eatContextual("async")) {
6341
6391
  if ((this.isClassElementNameStart() || this.type === types$1.star) && !this.canInsertSemicolon()) {
6342
6392
  isAsync = true;
6343
6393
  } else {
6344
- keyName = "async";
6394
+ keyName2 = "async";
6345
6395
  }
6346
6396
  }
6347
- if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {
6397
+ if (!keyName2 && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {
6348
6398
  isGenerator = true;
6349
6399
  }
6350
- if (!keyName && !isAsync && !isGenerator) {
6400
+ if (!keyName2 && !isAsync && !isGenerator) {
6351
6401
  var lastValue = this.value;
6352
6402
  if (this.eatContextual("get") || this.eatContextual("set")) {
6353
6403
  if (this.isClassElementNameStart()) {
6354
6404
  kind = lastValue;
6355
6405
  } else {
6356
- keyName = lastValue;
6406
+ keyName2 = lastValue;
6357
6407
  }
6358
6408
  }
6359
6409
  }
6360
- if (keyName) {
6410
+ if (keyName2) {
6361
6411
  node.computed = false;
6362
6412
  node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);
6363
- node.key.name = keyName;
6413
+ node.key.name = keyName2;
6364
6414
  this.finishNode(node.key, "Identifier");
6365
6415
  } else {
6366
6416
  this.parseClassElementName(node);
@@ -6661,7 +6711,7 @@ pp$8.parseExportSpecifier = function(exports$1) {
6661
6711
  return this.finishNode(node, "ExportSpecifier");
6662
6712
  };
6663
6713
  pp$8.parseExportSpecifiers = function(exports$1) {
6664
- var nodes = [], first = true;
6714
+ var nodes2 = [], first = true;
6665
6715
  this.expect(types$1.braceL);
6666
6716
  while (!this.eat(types$1.braceR)) {
6667
6717
  if (!first) {
@@ -6672,9 +6722,9 @@ pp$8.parseExportSpecifiers = function(exports$1) {
6672
6722
  } else {
6673
6723
  first = false;
6674
6724
  }
6675
- nodes.push(this.parseExportSpecifier(exports$1));
6725
+ nodes2.push(this.parseExportSpecifier(exports$1));
6676
6726
  }
6677
- return nodes;
6727
+ return nodes2;
6678
6728
  };
6679
6729
  pp$8.parseImport = function(node) {
6680
6730
  this.next();
@@ -6719,16 +6769,16 @@ pp$8.parseImportNamespaceSpecifier = function() {
6719
6769
  return this.finishNode(node, "ImportNamespaceSpecifier");
6720
6770
  };
6721
6771
  pp$8.parseImportSpecifiers = function() {
6722
- var nodes = [], first = true;
6772
+ var nodes2 = [], first = true;
6723
6773
  if (this.type === types$1.name) {
6724
- nodes.push(this.parseImportDefaultSpecifier());
6774
+ nodes2.push(this.parseImportDefaultSpecifier());
6725
6775
  if (!this.eat(types$1.comma)) {
6726
- return nodes;
6776
+ return nodes2;
6727
6777
  }
6728
6778
  }
6729
6779
  if (this.type === types$1.star) {
6730
- nodes.push(this.parseImportNamespaceSpecifier());
6731
- return nodes;
6780
+ nodes2.push(this.parseImportNamespaceSpecifier());
6781
+ return nodes2;
6732
6782
  }
6733
6783
  this.expect(types$1.braceL);
6734
6784
  while (!this.eat(types$1.braceR)) {
@@ -6740,14 +6790,14 @@ pp$8.parseImportSpecifiers = function() {
6740
6790
  } else {
6741
6791
  first = false;
6742
6792
  }
6743
- nodes.push(this.parseImportSpecifier());
6793
+ nodes2.push(this.parseImportSpecifier());
6744
6794
  }
6745
- return nodes;
6795
+ return nodes2;
6746
6796
  };
6747
6797
  pp$8.parseWithClause = function() {
6748
- var nodes = [];
6798
+ var nodes2 = [];
6749
6799
  if (!this.eat(types$1._with)) {
6750
- return nodes;
6800
+ return nodes2;
6751
6801
  }
6752
6802
  this.expect(types$1.braceL);
6753
6803
  var attributeKeys = {};
@@ -6762,14 +6812,14 @@ pp$8.parseWithClause = function() {
6762
6812
  first = false;
6763
6813
  }
6764
6814
  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 + "'");
6815
+ var keyName2 = attr.key.type === "Identifier" ? attr.key.name : attr.key.value;
6816
+ if (hasOwn(attributeKeys, keyName2)) {
6817
+ this.raiseRecoverable(attr.key.start, "Duplicate attribute key '" + keyName2 + "'");
6768
6818
  }
6769
- attributeKeys[keyName] = true;
6770
- nodes.push(attr);
6819
+ attributeKeys[keyName2] = true;
6820
+ nodes2.push(attr);
6771
6821
  }
6772
- return nodes;
6822
+ return nodes2;
6773
6823
  };
6774
6824
  pp$8.parseImportAttribute = function() {
6775
6825
  var node = this.startNode();
@@ -10918,7 +10968,7 @@ function visitDestructure(pattern, prefix, scope, out) {
10918
10968
  emit(out, segments, { form: "destructure", callee: false, written: false }, property.start, property.end);
10919
10969
  }
10920
10970
  }
10921
- function collectThisMemberRefs(body, names) {
10971
+ function collectThisMemberRefs(body, names2) {
10922
10972
  let program;
10923
10973
  try {
10924
10974
  program = parse3(PREFIX + body + SUFFIX, { ecmaVersion: "latest", sourceType: "module" });
@@ -10927,11 +10977,11 @@ function collectThisMemberRefs(body, names) {
10927
10977
  }
10928
10978
  const fn = unwrapWrapper(program);
10929
10979
  if (fn === null) return null;
10930
- return walkThisMembers(fn.body, names, PREFIX.length);
10980
+ return walkThisMembers(fn.body, names2, PREFIX.length);
10931
10981
  }
10932
10982
  var VALUE_PREFIX = "(async function* () {\n(";
10933
10983
  var VALUE_SUFFIX = ")\n})";
10934
- function collectThisMemberRefsInValue(value, names) {
10984
+ function collectThisMemberRefsInValue(value, names2) {
10935
10985
  let program;
10936
10986
  try {
10937
10987
  program = parse3(VALUE_PREFIX + value + VALUE_SUFFIX, { ecmaVersion: "latest", sourceType: "module" });
@@ -10944,9 +10994,9 @@ function collectThisMemberRefsInValue(value, names) {
10944
10994
  if (statement.type !== "ExpressionStatement") return null;
10945
10995
  const expression = statement.expression;
10946
10996
  if (expression.type !== "FunctionExpression") return [];
10947
- return walkThisMembers(expression.body, names, VALUE_PREFIX.length);
10997
+ return walkThisMembers(expression.body, names2, VALUE_PREFIX.length);
10948
10998
  }
10949
- function walkThisMembers(root, names, offsetShift) {
10999
+ function walkThisMembers(root, names2, offsetShift) {
10950
11000
  const out = [];
10951
11001
  const walk = (node, thisIsState) => {
10952
11002
  if (isClassNode(node)) return;
@@ -10957,7 +11007,7 @@ function walkThisMembers(root, names, offsetShift) {
10957
11007
  }
10958
11008
  if (thisIsState && node.type === "MemberExpression" && node.object.type === "ThisExpression") {
10959
11009
  const hit = thisMemberName(node);
10960
- if (hit !== null && names.has(hit.name)) {
11010
+ if (hit !== null && names2.has(hit.name)) {
10961
11011
  out.push({ name: hit.name, start: hit.start - offsetShift, end: hit.end - offsetShift });
10962
11012
  }
10963
11013
  }
@@ -11124,13 +11174,13 @@ function findInPlaceChanges(text3, kind, root) {
11124
11174
  const collect2 = (node) => {
11125
11175
  if (isFunctionNode(node)) {
11126
11176
  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);
11177
+ const names2 = /* @__PURE__ */ new Set();
11178
+ for (const param of node.params) bindingNames(param, names2);
11179
+ names2.forEach(bind);
11130
11180
  } else if (node.type === "VariableDeclarator") {
11131
- const names = /* @__PURE__ */ new Set();
11132
- bindingNames(node.id, names);
11133
- names.forEach(bind);
11181
+ const names2 = /* @__PURE__ */ new Set();
11182
+ bindingNames(node.id, names2);
11183
+ names2.forEach(bind);
11134
11184
  const init = node.init ? unwrapChain(node.init) : null;
11135
11185
  if (init !== null && node.id.type === "Identifier" && init.type === "MemberExpression") {
11136
11186
  const chain = memberChain(init, source);
@@ -11148,9 +11198,9 @@ function findInPlaceChanges(text3, kind, root) {
11148
11198
  }
11149
11199
  }
11150
11200
  } else if (node.type === "CatchClause" && node.param) {
11151
- const names = /* @__PURE__ */ new Set();
11152
- bindingNames(node.param, names);
11153
- names.forEach(bind);
11201
+ const names2 = /* @__PURE__ */ new Set();
11202
+ bindingNames(node.param, names2);
11203
+ names2.forEach(bind);
11154
11204
  } else if (node.type === "ClassDeclaration" && node.id !== null) {
11155
11205
  bind(node.id.name);
11156
11206
  } else if (node.type === "AssignmentExpression" && node.left.type === "Identifier") {
@@ -11212,14 +11262,14 @@ function findLocationRead(body) {
11212
11262
  }
11213
11263
  const wrapper = unwrapWrapper(program);
11214
11264
  if (wrapper === null) return null;
11215
- const names = /* @__PURE__ */ new Set();
11265
+ const names2 = /* @__PURE__ */ new Set();
11216
11266
  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);
11267
+ if (isFunctionNode(node)) for (const param of node.params) bindingNames(param, names2);
11268
+ if (node.type === "VariableDeclarator") bindingNames(node.id, names2);
11219
11269
  forEachChild(node, scan);
11220
11270
  };
11221
11271
  scan(wrapper.body);
11222
- if (names.has("location")) return null;
11272
+ if (names2.has("location")) return null;
11223
11273
  const isLocation = (node) => {
11224
11274
  const n = unwrapChain(node);
11225
11275
  if (n.type === "Identifier") return n.name === "location";
@@ -11296,6 +11346,13 @@ var ja = {
11296
11346
  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
11347
  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
11348
  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`,
11349
+ 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`,
11350
+ 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`,
11351
+ 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`,
11352
+ 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`,
11353
+ 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`,
11354
+ 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`,
11355
+ 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
11356
  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
11357
  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
11358
  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 +11389,7 @@ var ja = {
11332
11389
  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
11390
  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
11391
  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}`,
11392
+ 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
11393
  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
11394
  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
11395
  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',
@@ -11497,6 +11554,13 @@ var en = {
11497
11554
  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
11555
  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
11556
  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`,
11557
+ 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`,
11558
+ updatedCallbackUnknownPath: (p) => `"${p}" is not a path of this state, so it is never in $renderedCallback's paths and this branch never runs`,
11559
+ 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})`,
11560
+ writePathMissing: (k) => `"${k}" is not a key the state declares. The write creates a new key and reaches no binding of a declared one`,
11561
+ 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`,
11562
+ 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)`,
11563
+ 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
11564
  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
11565
  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
11566
  handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
@@ -11533,7 +11597,7 @@ var en = {
11533
11597
  behaviorValueType: (key, type) => `$behavior "${key}" must be ${type === "boolean" ? "true or false" : type} (the runtime throws at load time)`,
11534
11598
  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
11599
  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}`,
11600
+ featureUnknown: (name, names2, suggestion2) => `"${name}" is not an add-on (${names2.join(", ")})${suggestion2}`,
11537
11601
  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
11602
  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
11603
  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">',
@@ -11785,11 +11849,11 @@ var DELEGATED_EVENTS = /* @__PURE__ */ new Set([
11785
11849
  ]);
11786
11850
  var CUSTOM_ELEMENT_OWN_EVENTS = /* @__PURE__ */ new Set(["input", "change", "submit"]);
11787
11851
  function collectCurrentTargetHandlers(elements, html, fileReader) {
11788
- const names = /* @__PURE__ */ new Set();
11852
+ const names2 = /* @__PURE__ */ new Set();
11789
11853
  forEachStateMethod(elements, html, fileReader, "currentTarget", (name, params, body) => {
11790
- if (readsEventCurrentTarget(params, body)) names.add(name);
11854
+ if (readsEventCurrentTarget(params, body)) names2.add(name);
11791
11855
  });
11792
- return names;
11856
+ return names2;
11793
11857
  }
11794
11858
  function collectPreventDefaultHandlers(elements, html, fileReader) {
11795
11859
  const handlers = /* @__PURE__ */ new Map();
@@ -12036,6 +12100,18 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
12036
12100
  message: `${verdict.message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
12037
12101
  severity: verdict.severity
12038
12102
  });
12103
+ } else if (writesBack(attr, propNoMod, parsed.property)) {
12104
+ const target = scopedPaths.find((p) => p.path === checkPath);
12105
+ if (target !== void 0 && target.kind === "computed" && target.settable !== true) {
12106
+ const pathStart = bindingStart + binding.indexOf(parsed.path);
12107
+ diagnostics.push({
12108
+ code: WcsDiagnosticCode.PathReadonly,
12109
+ start: pathStart,
12110
+ end: pathStart + pathTrimmed.length,
12111
+ message: msgs.pathReadonlyTwoWay(checkPath, propNoMod),
12112
+ severity: "error"
12113
+ });
12114
+ }
12039
12115
  }
12040
12116
  }
12041
12117
  }
@@ -12222,6 +12298,19 @@ function openTagBefore(html, valueStart) {
12222
12298
  const lt2 = html.lastIndexOf("<", valueStart);
12223
12299
  return lt2 === -1 ? "" : html.slice(lt2, valueStart);
12224
12300
  }
12301
+ function writesBack(attr, prop, written) {
12302
+ if (written.split("#").slice(1).some((modifier) => modifier.trim() === "ro")) return false;
12303
+ if (prop === "radio" || prop === "checkbox") return true;
12304
+ const tag = attr.tagName;
12305
+ if (tag === void 0 || tag.includes("-")) return false;
12306
+ if (tag === "input") {
12307
+ const type = (attr.inputType || "text").toLowerCase();
12308
+ if (type === "button") return false;
12309
+ if ((type === "radio" || type === "checkbox") && prop === "checked") return true;
12310
+ return prop === "value" || prop === "valueAsNumber" || prop === "valueAsDate";
12311
+ }
12312
+ return (tag === "select" || tag === "textarea") && prop === "value";
12313
+ }
12225
12314
  function findAllBindAttributes(html, attrName) {
12226
12315
  const attrs = [];
12227
12316
  const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -12235,7 +12324,8 @@ function findAllBindAttributes(html, attrName) {
12235
12324
  const valueStart = tag.start + match.index + match[0].length;
12236
12325
  const valueEnd = html.indexOf(quote, valueStart);
12237
12326
  if (valueEnd === -1 || valueEnd > tag.end) continue;
12238
- attrs.push({ value: html.slice(valueStart, valueEnd), valueStart, tagName: tag.tagName });
12327
+ const inputType = tag.tagName === "input" ? extractAttribute(attrsText, "type") ?? void 0 : void 0;
12328
+ attrs.push({ value: html.slice(valueStart, valueEnd), valueStart, tagName: tag.tagName, ...inputType !== void 0 ? { inputType } : {} });
12239
12329
  regex.lastIndex = valueEnd - tag.start;
12240
12330
  }
12241
12331
  }
@@ -14387,6 +14477,162 @@ var BUILTIN_TAGS = {
14387
14477
  }
14388
14478
  };
14389
14479
 
14480
+ // src/service/pageElements.ts
14481
+ var MAX_FILES = 32;
14482
+ var JS_TYPES = /* @__PURE__ */ new Set(["", "module", "text/javascript", "application/javascript", "text/ecmascript"]);
14483
+ function pageScripts(html) {
14484
+ const inline = [];
14485
+ const srcs = [];
14486
+ const re = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/script\s*>/gi;
14487
+ const text3 = html.replace(/<!--[\s\S]*?-->/g, (c) => " ".repeat(c.length));
14488
+ let m;
14489
+ while ((m = re.exec(text3)) !== null) {
14490
+ const attrs = m[1];
14491
+ const type = (extractAttribute(attrs, "type") ?? "").trim().toLowerCase();
14492
+ if (!JS_TYPES.has(type)) continue;
14493
+ const src = extractAttribute(attrs, "src");
14494
+ if (src !== null) srcs.push(src);
14495
+ else if (m[2].trim() !== "") inline.push(m[2]);
14496
+ }
14497
+ return { inline, srcs };
14498
+ }
14499
+ function joinRelative(from, spec) {
14500
+ if (!spec.startsWith("./") && !spec.startsWith("../")) return null;
14501
+ const dir = from.includes("/") ? from.slice(0, from.lastIndexOf("/") + 1) : "";
14502
+ const out = [];
14503
+ for (const seg of `${dir}${spec}`.split("/")) {
14504
+ if (seg === "" || seg === ".") continue;
14505
+ if (seg === ".." && out.length > 0 && out[out.length - 1] !== "..") out.pop();
14506
+ else out.push(seg);
14507
+ }
14508
+ return `./${out.join("/")}`;
14509
+ }
14510
+ function parseScript(source) {
14511
+ for (const sourceType of ["module", "script"]) {
14512
+ try {
14513
+ return parse3(source, { ecmaVersion: "latest", sourceType, allowHashBang: true });
14514
+ } catch {
14515
+ }
14516
+ }
14517
+ return null;
14518
+ }
14519
+ function* nodes(node) {
14520
+ yield node;
14521
+ for (const value of Object.values(node)) {
14522
+ if (Array.isArray(value)) {
14523
+ for (const v of value) if (v !== null && typeof v === "object" && typeof v.type === "string") yield* nodes(v);
14524
+ } else if (value !== null && typeof value === "object" && typeof value.type === "string") {
14525
+ yield* nodes(value);
14526
+ }
14527
+ }
14528
+ }
14529
+ var keyName = (key, computed) => computed ? null : key.type === "Identifier" ? key.name : key.type === "Literal" && typeof key.value === "string" ? key.value : null;
14530
+ function names(obj, key) {
14531
+ const prop = obj.properties.find((p) => p.type === "Property" && keyName(p.key, p.computed) === key);
14532
+ if (prop === void 0 || prop.type !== "Property") return void 0;
14533
+ if (prop.value.type !== "ArrayExpression") return null;
14534
+ const out = [];
14535
+ for (const el of prop.value.elements) {
14536
+ if (el === null || el.type !== "ObjectExpression" || el.properties.some((p) => p.type !== "Property")) return null;
14537
+ const name = el.properties.find((p) => p.type === "Property" && keyName(p.key, p.computed) === "name");
14538
+ if (name === void 0 || name.type !== "Property" || name.value.type !== "Literal" || typeof name.value.value !== "string") return null;
14539
+ out.push(name.value.value);
14540
+ }
14541
+ return out;
14542
+ }
14543
+ function contractOf(value) {
14544
+ if (value.type !== "ObjectExpression" || value.properties.some((p) => p.type !== "Property")) return "unknown";
14545
+ const properties = names(value, "properties");
14546
+ const inputs = names(value, "inputs");
14547
+ const commands = names(value, "commands");
14548
+ if (properties === null || inputs === null || commands === null) return "unknown";
14549
+ return {
14550
+ properties: properties ?? [],
14551
+ inputs: Object.fromEntries((inputs ?? []).map((n) => [n, null])),
14552
+ commands: commands ?? []
14553
+ };
14554
+ }
14555
+ function declaredBy(cls) {
14556
+ let declared = "none";
14557
+ for (const member of cls.body.body) {
14558
+ if (member.type !== "PropertyDefinition" && member.type !== "MethodDefinition" || !member.static) continue;
14559
+ if (keyName(member.key, member.computed) !== "wcBindable") continue;
14560
+ if (member.type === "PropertyDefinition") {
14561
+ declared = member.value === null || member.value === void 0 ? "unknown" : contractOf(member.value);
14562
+ } else {
14563
+ const body = member.kind === "get" ? member.value.body.body : [];
14564
+ const ret = body.length === 1 && body[0].type === "ReturnStatement" ? body[0].argument : null;
14565
+ declared = ret === null || ret === void 0 ? "unknown" : contractOf(ret);
14566
+ }
14567
+ }
14568
+ const sup = cls.superClass;
14569
+ return { declared, superName: sup !== null && sup !== void 0 && sup.type === "Identifier" ? sup.name : null };
14570
+ }
14571
+ function isDefine(callee) {
14572
+ if (callee.type !== "MemberExpression" || keyName(callee.property, callee.computed) !== "define") return false;
14573
+ const o = callee.object;
14574
+ return o.type === "Identifier" && o.name === "customElements" || o.type === "MemberExpression" && keyName(o.property, o.computed) === "customElements";
14575
+ }
14576
+ function collectPageElementContracts(html, fileReader) {
14577
+ const { inline, srcs } = pageScripts(html);
14578
+ const programs = [];
14579
+ const read = /* @__PURE__ */ new Set();
14580
+ const queue = srcs.map((spec) => ({ from: "", spec: spec.startsWith(".") ? spec : `./${spec}` }));
14581
+ const visit2 = (program, from) => {
14582
+ programs.push(program);
14583
+ for (const n of nodes(program)) {
14584
+ const source = n.type === "ImportDeclaration" || n.type === "ExportAllDeclaration" || n.type === "ExportNamedDeclaration" ? n.source : n.type === "ImportExpression" ? n.source : null;
14585
+ if (source !== null && source !== void 0 && source.type === "Literal" && typeof source.value === "string") queue.push({ from, spec: source.value });
14586
+ }
14587
+ };
14588
+ for (const source of inline) {
14589
+ const program = parseScript(source);
14590
+ if (program !== null) visit2(program, "");
14591
+ }
14592
+ while (queue.length > 0 && fileReader !== void 0 && read.size < MAX_FILES) {
14593
+ const { from, spec } = queue.shift();
14594
+ const path = joinRelative(from, spec);
14595
+ if (path === null || read.has(path) || !/\.m?js$/.test(path)) continue;
14596
+ read.add(path);
14597
+ const text3 = fileReader(path);
14598
+ const program = text3 === void 0 ? null : parseScript(text3);
14599
+ if (program !== null) visit2(program, path);
14600
+ }
14601
+ const classes = /* @__PURE__ */ new Map();
14602
+ const defines = [];
14603
+ for (const program of programs) {
14604
+ for (const n of nodes(program)) {
14605
+ if (n.type === "ClassDeclaration" && n.id !== null && !classes.has(n.id.name)) classes.set(n.id.name, declaredBy(n));
14606
+ else if (n.type === "VariableDeclarator" && n.id.type === "Identifier" && n.init?.type === "ClassExpression" && !classes.has(n.id.name)) {
14607
+ classes.set(n.id.name, declaredBy(n.init));
14608
+ } else if (n.type === "CallExpression" && isDefine(n.callee) && n.arguments.length >= 2) {
14609
+ const [tag, cls] = n.arguments;
14610
+ if (tag.type !== "Literal" || typeof tag.value !== "string") continue;
14611
+ if (cls.type === "ClassExpression") defines.push({ tag: tag.value, info: declaredBy(cls) });
14612
+ else if (cls.type === "Identifier") defines.push({ tag: tag.value, info: cls.name });
14613
+ }
14614
+ }
14615
+ }
14616
+ const resolve = (info, depth) => {
14617
+ if (info.declared === "unknown") return null;
14618
+ if (info.declared !== "none") return info.declared;
14619
+ const sup = info.superName === null ? void 0 : classes.get(info.superName);
14620
+ return sup === void 0 || depth > 16 ? null : resolve(sup, depth + 1);
14621
+ };
14622
+ const out = /* @__PURE__ */ new Map();
14623
+ for (const { tag, info } of defines) {
14624
+ const name = tag.toLowerCase();
14625
+ if (name.startsWith("wcs-") || out.has(name)) continue;
14626
+ const cls = typeof info === "string" ? classes.get(info) : info;
14627
+ const contract = cls === void 0 ? null : resolve(cls, 0);
14628
+ if (contract !== null) out.set(name, contract);
14629
+ }
14630
+ return out;
14631
+ }
14632
+ function mayDefineElements(html) {
14633
+ return /customElements|<script\b[^>]*\bsrc\s*=/i.test(html);
14634
+ }
14635
+
14390
14636
  // src/service/ioNodeValidator.ts
14391
14637
  var HTML_SINK_PROPERTIES = /* @__PURE__ */ new Set(["html", "innerHTML", "outerHTML", "srcdoc"]);
14392
14638
  var STRUCTURAL_DIRECTIVES3 = /* @__PURE__ */ new Set(["for", "if", "elseif", "else"]);
@@ -14401,7 +14647,6 @@ function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-s
14401
14647
  const diagnostics = [];
14402
14648
  const msgs = getMessages(locale2);
14403
14649
  const occurrences = findBuiltinTagOccurrences(html);
14404
- if (occurrences.length === 0) return diagnostics;
14405
14650
  let statePaths = null;
14406
14651
  const getPaths = () => statePaths ??= getStatePathsFromHtml(html, stateTagName, fileReader);
14407
14652
  for (const occ of occurrences) {
@@ -14429,32 +14674,35 @@ function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-s
14429
14674
  }
14430
14675
  if (contract.properties.length === 0 && contract.commands.length === 0 && Object.keys(contract.inputs).length === 0) continue;
14431
14676
  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
- );
14677
+ checkBindings(occ.tagName, contract, bindAttr.value, valueStart, hasManual, getPaths, diagnostics, msgs);
14678
+ }
14679
+ if (mayDefineElements(html)) {
14680
+ const own2 = findPageTagOccurrences(html, bindAttribute);
14681
+ const contracts = own2.length > 0 ? collectPageElementContracts(html, fileReader) : null;
14682
+ for (const occ of own2) {
14683
+ const contract = contracts.get(occ.tagName);
14684
+ if (contract === void 0) continue;
14685
+ if (contract.properties.length === 0 && contract.commands.length === 0 && Object.keys(contract.inputs).length === 0) continue;
14686
+ const bindAttr = extractAttributeValue(occ.attrsText, bindAttribute);
14687
+ checkBindings(occ.tagName, contract, bindAttr.value, occ.attrsStart + bindAttr.valueOffsetInAttrs, false, getPaths, diagnostics, msgs);
14454
14688
  }
14455
14689
  }
14456
14690
  return diagnostics;
14457
14691
  }
14692
+ function checkBindings(tagName, contract, value, valueStart, hasManual, getPaths, diagnostics, msgs) {
14693
+ let exprOffset = 0;
14694
+ for (const expr of splitBindingExpressions(value)) {
14695
+ const exprStart = valueStart + exprOffset;
14696
+ exprOffset += expr.length + 1;
14697
+ const parsed = parseBindingExpression(expr);
14698
+ const property = parsed.property;
14699
+ if (!property) continue;
14700
+ const propIndex = expr.indexOf(property);
14701
+ const start = propIndex === -1 ? exprStart : exprStart + propIndex;
14702
+ const end = propIndex === -1 ? exprStart + expr.length : start + property.length;
14703
+ validateBindingAgainstContract(tagName, contract, parsed, property, start, end, hasManual, getPaths, diagnostics, msgs);
14704
+ }
14705
+ }
14458
14706
  function validateBindingAgainstContract(tagName, contract, parsed, property, start, end, hasManual, getPaths, diagnostics, msgs) {
14459
14707
  const hashIndex = property.indexOf("#");
14460
14708
  const modifiers = hashIndex === -1 ? "" : property.slice(hashIndex + 1);
@@ -14513,6 +14761,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
14513
14761
  }
14514
14762
  return;
14515
14763
  }
14764
+ if (!tagName.startsWith("wcs-") && property.includes(".")) return;
14516
14765
  if (!contract.properties.includes(property) && !(property in contract.inputs) && !HTML_SINK_PROPERTIES.has(property)) {
14517
14766
  const members = [...contract.properties, ...inputNames];
14518
14767
  diagnostics.push({
@@ -14526,7 +14775,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
14526
14775
  });
14527
14776
  return;
14528
14777
  }
14529
- if (property === "trigger" && "trigger" in contract.inputs && parsed.path) {
14778
+ if (tagName.startsWith("wcs-") && property === "trigger" && "trigger" in contract.inputs && parsed.path) {
14530
14779
  const cand = findDataSlot(getPaths(), parsed.path);
14531
14780
  if (cand?.rawInitial === "true") {
14532
14781
  diagnostics.push({
@@ -14603,6 +14852,17 @@ function findBuiltinTagOccurrences(html) {
14603
14852
  }
14604
14853
  return out;
14605
14854
  }
14855
+ function findPageTagOccurrences(html, bindAttribute) {
14856
+ const out = [];
14857
+ for (const region of findStartTagRegions(html)) {
14858
+ const tagName = region.tagName;
14859
+ if (!tagName.includes("-") || tagName.startsWith("wcs-")) continue;
14860
+ const attrsText = html.slice(region.start, region.end);
14861
+ if (extractAttributeValue(attrsText, bindAttribute) === null) continue;
14862
+ out.push({ tagName, tagStart: region.start - tagName.length - 1, attrsText, attrsStart: region.start });
14863
+ }
14864
+ return out;
14865
+ }
14606
14866
  function extractAttributeValue(attrsText, attrName) {
14607
14867
  const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14608
14868
  const regex = new RegExp(`(?:^|\\s)${escaped}\\s*=\\s*(["'])`, "i");
@@ -15048,7 +15308,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale2, fi
15048
15308
  const scopes = {
15049
15309
  mounts: volumes?.scopes.mounts ?? [],
15050
15310
  unresolvedMounts: volumes?.scopes.unresolvedMounts ?? NO_MOUNTS,
15051
- rootUnresolved: own2.length === 0 && !isReadableStateScript(block.content)
15311
+ rootUnresolved: !scriptReadable(block.content, own2)
15052
15312
  };
15053
15313
  for (const entry of entries) {
15054
15314
  const diagnostic = validateEntry(entry, pathSet, paths, scopes, msgs);
@@ -15069,10 +15329,10 @@ var NO_MOUNTS = /* @__PURE__ */ new Set();
15069
15329
  function assignVolumes(elements, html) {
15070
15330
  const chainOf = createTemplateChain(html);
15071
15331
  const innermost = (chain) => chain.length > 0 ? chain[chain.length - 1] : -1;
15072
- const treeOf = (root) => innermost(chainOf(root.tagStart));
15332
+ const treeOf2 = (root) => innermost(chainOf(root.tagStart));
15073
15333
  const rootTrees = /* @__PURE__ */ new Set();
15074
15334
  for (const element2 of elements) {
15075
- if (element2.mountPath === null && !element2.bindComponent) rootTrees.add(treeOf(element2));
15335
+ if (element2.mountPath === null && !element2.bindComponent) rootTrees.add(treeOf2(element2));
15076
15336
  }
15077
15337
  const volumes = /* @__PURE__ */ new Map();
15078
15338
  for (const element2 of elements) {
@@ -15089,7 +15349,7 @@ function assignVolumes(elements, html) {
15089
15349
  if (list === void 0) volumes.set(tree, [element2]);
15090
15350
  else list.push(element2);
15091
15351
  }
15092
- return { treeOf, volumes };
15352
+ return { treeOf: treeOf2, volumes };
15093
15353
  }
15094
15354
  function validateEntry(entry, pathSet, paths, scopes, msgs) {
15095
15355
  const { key } = entry;
@@ -16077,8 +16337,8 @@ function validateVolumeBlock2(block, mountPath, msgs, out) {
16077
16337
  }
16078
16338
  function validateBindComponentSource(html, tagStart, tagEnd, hasInlineScript, msgs, out) {
16079
16339
  const tag = html.slice(tagStart, tagEnd);
16080
- const names = parseAttributeNames(tag);
16081
- const sources = BIND_COMPONENT_SOURCE_ATTRIBUTES.filter((name) => names.has(name));
16340
+ const names2 = parseAttributeNames(tag);
16341
+ const sources = BIND_COMPONENT_SOURCE_ATTRIBUTES.filter((name) => names2.has(name));
16082
16342
  if (hasInlineScript) sources.push('<script type="module">');
16083
16343
  if (sources.length === 0) return;
16084
16344
  const prop = /\sbind-component\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'<>`=]+))/i.exec(tag);
@@ -16206,7 +16466,7 @@ var OLD_DECLARATION_KEY = /(^|[{,;\s])(\$streams|\$updatedCallback)(?=\s*(?:[:(]
16206
16466
  function collectDeclarationAliasReads(script) {
16207
16467
  return collectStateThisReads(script, OLD_DECLARATION_MEMBER_NAMES);
16208
16468
  }
16209
- function collectStateThisReads(script, names) {
16469
+ function collectStateThisReads(script, names2) {
16210
16470
  if (!hasDefaultExportObject(script)) return null;
16211
16471
  const out = [];
16212
16472
  const take = (refs, base) => {
@@ -16215,10 +16475,10 @@ function collectStateThisReads(script, names) {
16215
16475
  return true;
16216
16476
  };
16217
16477
  for (const callable of analyzeCallableBodies(script)) {
16218
- if (!take(collectThisMemberRefs(callable.body, names), callable.bodyStart)) return null;
16478
+ if (!take(collectThisMemberRefs(callable.body, names2), callable.bodyStart)) return null;
16219
16479
  }
16220
16480
  for (const handler of analyzeWatchHandlerSources(script)) {
16221
- const refs = handler.kind === "body" ? collectThisMemberRefs(handler.text, names) : collectThisMemberRefsInValue(handler.text, names);
16481
+ const refs = handler.kind === "body" ? collectThisMemberRefs(handler.text, names2) : collectThisMemberRefsInValue(handler.text, names2);
16222
16482
  if (!take(refs, handler.start)) return null;
16223
16483
  }
16224
16484
  out.sort((a, b) => a.start - b.start);
@@ -16426,6 +16686,26 @@ function validateGetterLocationReads(script, scriptStart, locale2) {
16426
16686
  }
16427
16687
  return out;
16428
16688
  }
16689
+ var CLOCK_READ = /\bDate\s*\.\s*now\s*\(|\bnew\s+Date\s*\(\s*\)|\bperformance\s*\.\s*now\s*\(/;
16690
+ function validateGetterClockReads(script, scriptStart, locale2) {
16691
+ if (!/Date|performance/.test(script)) return [];
16692
+ const msgs = getMessages(locale2);
16693
+ const out = [];
16694
+ for (const getter of analyzeCallableBodies(script)) {
16695
+ if (getter.kind !== "getter" || getter.accessor !== "get") continue;
16696
+ const read = CLOCK_READ.exec(maskCommentsAndStrings(getter.body));
16697
+ if (read === null) continue;
16698
+ const text3 = read[0].startsWith("new") ? "new Date()" : read[0].startsWith("performance") ? "performance.now()" : "Date.now()";
16699
+ out.push({
16700
+ code: WcsDiagnosticCode.GetterUntrackedRead,
16701
+ start: scriptStart + getter.bodyStart + read.index,
16702
+ end: scriptStart + getter.bodyStart + read.index + read[0].length,
16703
+ message: msgs.getterReadsClock(text3),
16704
+ severity: "warning"
16705
+ });
16706
+ }
16707
+ return out;
16708
+ }
16429
16709
  function onHandlerStateParam(script, handler) {
16430
16710
  if (handler.kind === "body") {
16431
16711
  const head = script.slice(Math.max(0, handler.start - 400), handler.start);
@@ -16623,15 +16903,22 @@ function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2
16623
16903
  const reported = /* @__PURE__ */ new Set();
16624
16904
  while ((match = PATH_TEST_LITERAL.exec(body)) !== null) {
16625
16905
  const path = match[2];
16626
- if (path.length === 0 || !declared.has(path) || bound.has(path)) continue;
16627
- if (reported.has(path)) continue;
16906
+ if (path.length === 0 || bound.has(path) || reported.has(path)) continue;
16907
+ let message;
16908
+ if (declared.has(path)) {
16909
+ message = msgs.updatedCallbackUnbound(path);
16910
+ } else {
16911
+ const near = suggestion(path, [...declared, ...bound], msgs);
16912
+ if (near === "") continue;
16913
+ message = msgs.updatedCallbackUnknownPath(path) + near;
16914
+ }
16628
16915
  reported.add(path);
16629
16916
  const quoteAt = match.index + match[0].length - path.length - 1;
16630
16917
  out.push({
16631
16918
  code: WcsDiagnosticCode.UpdatedCallbackUnbound,
16632
16919
  start: block.contentStart + callback.bodyStart + quoteAt,
16633
16920
  end: block.contentStart + callback.bodyStart + quoteAt + path.length,
16634
- message: msgs.updatedCallbackUnbound(path),
16921
+ message,
16635
16922
  severity: "warning"
16636
16923
  });
16637
16924
  }
@@ -16644,8 +16931,8 @@ function collectBoundPaths(html, stateTagName, bindAttrName) {
16644
16931
  for (const occurrence of index.occurrences) {
16645
16932
  bound.add(occurrence.path);
16646
16933
  if (!occurrence.path.startsWith(".")) continue;
16647
- const forPath = getInnermostForPath(html, occurrence.pathRange.start, bindAttrName);
16648
- if (forPath === null || forPath.startsWith(".")) continue;
16934
+ const forPath = getResolvedForListPath(html, occurrence.pathRange.start, bindAttrName);
16935
+ if (forPath === null) continue;
16649
16936
  bound.add(
16650
16937
  occurrence.path === "." ? `${forPath}.*` : `${forPath}.*.${occurrence.path.slice(1)}`
16651
16938
  );
@@ -16718,6 +17005,7 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
16718
17005
  out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
16719
17006
  out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
16720
17007
  out.push(...validateGetterLocationReads(block.content, block.contentStart, locale2));
17008
+ out.push(...validateGetterClockReads(block.content, block.contentStart, locale2));
16721
17009
  out.push(...validateOnHandlerThis(block.content, block.contentStart, locale2));
16722
17010
  out.push(...validateInPlaceChanges(block.content, block.contentStart, locale2));
16723
17011
  }
@@ -16726,6 +17014,169 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
16726
17014
  return out;
16727
17015
  }
16728
17016
 
17017
+ // src/service/scriptStateUseValidator.ts
17018
+ var COMMAND_USE = /\.\s*\$command\s*\.\s*([A-Za-z_$][\w$]*)/dg;
17019
+ var EMIT_AFTER = /^\s*\.\s*emit\s*\(/;
17020
+ var THIS_WRITE = /\bthis\s*\.\s*([A-Za-z_][\w$]*)\s*(?:=(?![=>])|[-+*/%&|^]=|\*\*=|<<=|>>>?=|&&=|\|\|=|\?\?=|\+\+|--)/dg;
17021
+ var PRE_THIS_WRITE = /(?:\+\+|--)\s*this\s*\.\s*([A-Za-z_][\w$]*)/dg;
17022
+ function validateScriptStateUses(html, stateTagName = "wcs-state", locale2, bindAttrName = "data-wcs") {
17023
+ const blocks = parseLoadedScriptBlocks(html, stateTagName).filter((block) => isReadableStateScript(block.content));
17024
+ if (blocks.length === 0) return [];
17025
+ const msgs = getMessages(locale2);
17026
+ const out = [];
17027
+ let receivers = null;
17028
+ let markupEmits = null;
17029
+ const bindings = () => {
17030
+ if (receivers !== null) return;
17031
+ receivers = /* @__PURE__ */ new Set();
17032
+ markupEmits = /* @__PURE__ */ new Map();
17033
+ for (const o of buildReferenceIndex(html, { bindAttribute: bindAttrName, stateTagName }).occurrences) {
17034
+ if (!o.path.startsWith("$command.")) continue;
17035
+ const name = o.path.slice("$command.".length);
17036
+ const prop = o.propName ?? "";
17037
+ if (prop.startsWith("command.")) receivers.add(name);
17038
+ else if (prop.startsWith("on") && !markupEmits.has(name)) markupEmits.set(name, o.pathRange);
17039
+ }
17040
+ };
17041
+ for (const block of blocks) {
17042
+ const paths = analyzeStatePaths(block.content);
17043
+ const masked = maskCommentsAndStrings(block.content);
17044
+ const declared = paths.filter((p) => p.kind === "command").map((p) => p.path.slice("$command.".length));
17045
+ if (block.mountPath === null && declared.length > 0) {
17046
+ const emitted = /* @__PURE__ */ new Map();
17047
+ COMMAND_USE.lastIndex = 0;
17048
+ let match;
17049
+ while ((match = COMMAND_USE.exec(masked)) !== null) {
17050
+ const name = match[1];
17051
+ const [start, end] = match.indices[1];
17052
+ if (!declared.includes(name)) {
17053
+ out.push({
17054
+ code: WcsDiagnosticCode.TokenUndeclared,
17055
+ start: block.contentStart + start,
17056
+ end: block.contentStart + end,
17057
+ // `$command` holds the declared tokens only: `.emit()` on what it lacks is a TypeError
17058
+ message: msgs.commandTokenUndeclared(`$command.${name}`) + suggestion(name, declared, msgs),
17059
+ severity: "error"
17060
+ });
17061
+ } else if (!emitted.has(name) && EMIT_AFTER.test(masked.slice(match.index + match[0].length))) {
17062
+ emitted.set(name, { start: block.contentStart + start, end: block.contentStart + end });
17063
+ }
17064
+ }
17065
+ bindings();
17066
+ for (const name of declared) {
17067
+ if (receivers.has(name)) continue;
17068
+ const at2 = emitted.get(name) ?? markupEmits.get(name);
17069
+ if (at2 === void 0) continue;
17070
+ out.push({
17071
+ code: WcsDiagnosticCode.CommandTokenUnbound,
17072
+ start: at2.start,
17073
+ end: at2.end,
17074
+ message: msgs.commandTokenUnbound(`$command.${name}`),
17075
+ severity: "warning"
17076
+ });
17077
+ }
17078
+ }
17079
+ const keys = [...new Set(paths.map((p) => p.path.split(".")[0]).filter((key) => key !== "" && !key.startsWith("$")))];
17080
+ const reported = /* @__PURE__ */ new Set();
17081
+ for (const pattern of [THIS_WRITE, PRE_THIS_WRITE]) {
17082
+ pattern.lastIndex = 0;
17083
+ let match;
17084
+ while ((match = pattern.exec(masked)) !== null) {
17085
+ const key = match[1];
17086
+ if (keys.includes(key) || reported.has(key)) continue;
17087
+ const near = suggestion(key, keys, msgs);
17088
+ if (near === "") continue;
17089
+ reported.add(key);
17090
+ const [start, end] = match.indices[1];
17091
+ out.push({
17092
+ code: WcsDiagnosticCode.WritePathMissing,
17093
+ start: block.contentStart + start,
17094
+ end: block.contentStart + end,
17095
+ message: msgs.writePathMissing(key) + near,
17096
+ severity: "warning"
17097
+ });
17098
+ }
17099
+ }
17100
+ }
17101
+ return out;
17102
+ }
17103
+
17104
+ // src/service/scriptReadValidator.ts
17105
+ function validateScriptReads(html, stateTagName = "wcs-state", locale2, fileReader, applicationSchema) {
17106
+ const blocks = parseLoadedScriptBlocks(html, stateTagName);
17107
+ if (blocks.length === 0) return [];
17108
+ const msgs = getMessages(locale2);
17109
+ const index = getStatePathIndex(html, stateTagName, fileReader);
17110
+ const candidates = mergeSchemaCandidates(index.paths, applicationSchema);
17111
+ const candidateSet = new Set(candidates.map((p) => p.path));
17112
+ let tree = null;
17113
+ const out = [];
17114
+ for (const block of blocks) {
17115
+ const spread = applicationSchema === void 0 ? findTopLevelSpread(block.content) : null;
17116
+ if (spread !== null) {
17117
+ out.push({
17118
+ code: WcsDiagnosticCode.PathDynamicUnknown,
17119
+ start: block.contentStart + spread.start,
17120
+ end: block.contentStart + spread.end,
17121
+ message: msgs.pathDynamicUnknownSpread(block.content.slice(spread.start, spread.end)),
17122
+ severity: "info"
17123
+ });
17124
+ }
17125
+ const prefix = block.mountPath === null || block.mountPath === "" ? "" : `${block.mountPath}.`;
17126
+ for (const callable of analyzeCallableBodies(block.content)) {
17127
+ for (const read of collectGetterReads(callable.body) ?? []) {
17128
+ if (read.written || read.path.split(".").some((s) => s === "" || s.startsWith("$")) || hasRecursionWildcard(read.path)) continue;
17129
+ const path = prefix + read.path;
17130
+ if (pathExistsInCandidates(path, candidates, candidateSet)) continue;
17131
+ if (applicationSchema !== void 0 ? resolveIndexedSchemaPath(applicationSchema, path).kind !== "nonexistent" : isUnresolvedPath(path, index.scopes)) continue;
17132
+ const near = nearName(path, tree ??= treeOf(candidates));
17133
+ if (near === null) continue;
17134
+ const said = suggestion(near.segment, near.names, msgs);
17135
+ if (said === "") continue;
17136
+ const at2 = block.contentStart + callable.bodyStart;
17137
+ out.push({
17138
+ code: WcsDiagnosticCode.ReadPathMissing,
17139
+ start: at2 + read.start,
17140
+ end: read.form === "member" ? at2 + read.start + statePart(callable.body.slice(read.start, read.end)) : at2 + read.end,
17141
+ message: msgs.readPathMissing(read.path, !read.path.includes(".")) + said,
17142
+ // a stateSchema says the path is not there, as for a binding's `wcs/path-nonexistent`
17143
+ severity: applicationSchema !== void 0 ? "error" : "warning"
17144
+ });
17145
+ }
17146
+ }
17147
+ }
17148
+ return out;
17149
+ }
17150
+ var THIS_PART = /^this\s*(?:\?\.\s*|\.\s*)(?:[A-Za-z_$][\w$]*)|^this\s*(?:\?\.)?\s*\[\s*(["'`])[^"'`]*\1\s*\]/;
17151
+ function statePart(text3) {
17152
+ return THIS_PART.exec(text3)?.[0].length ?? text3.length;
17153
+ }
17154
+ function treeOf(candidates) {
17155
+ const tree = /* @__PURE__ */ new Map();
17156
+ for (const c of candidates) {
17157
+ let parent = "";
17158
+ for (const segment of c.path.split(".")) {
17159
+ let names2 = tree.get(parent);
17160
+ if (names2 === void 0) tree.set(parent, names2 = /* @__PURE__ */ new Set());
17161
+ names2.add(segment);
17162
+ parent = parent === "" ? segment : `${parent}.${segment}`;
17163
+ }
17164
+ }
17165
+ return tree;
17166
+ }
17167
+ function nearName(path, tree) {
17168
+ let parent = "";
17169
+ for (const segment of (hasIndexSegment(path) ? toWildcardForm(path) : path).split(".")) {
17170
+ const names2 = tree.get(parent);
17171
+ if (names2?.has(segment)) {
17172
+ parent = parent === "" ? segment : `${parent}.${segment}`;
17173
+ continue;
17174
+ }
17175
+ return { segment, names: [...names2 ?? []].filter((name) => name !== "*" && !name.startsWith("$")) };
17176
+ }
17177
+ return null;
17178
+ }
17179
+
16729
17180
  // src/core/sidecar/discover.ts
16730
17181
  var APPLICATION_MANIFEST_FILENAME = "wcstack.manifest.json";
16731
17182
  var MAX_ASCEND = 16;
@@ -16750,13 +17201,129 @@ function applicationSchemaOf(loaded) {
16750
17201
  return void 0;
16751
17202
  }
16752
17203
 
17204
+ // src/core/offsetToPosition.ts
17205
+ function createPositionMapper(text3) {
17206
+ const lineStarts = [0];
17207
+ for (let i = 0; i < text3.length; i++) {
17208
+ const c = text3.charCodeAt(i);
17209
+ if (c === 10) {
17210
+ lineStarts.push(i + 1);
17211
+ } else if (c === 13) {
17212
+ if (text3.charCodeAt(i + 1) === 10) i++;
17213
+ lineStarts.push(i + 1);
17214
+ }
17215
+ }
17216
+ return (offset2) => {
17217
+ const clamped = Math.max(0, Math.min(offset2, text3.length));
17218
+ let lo = 0;
17219
+ let hi = lineStarts.length - 1;
17220
+ while (lo < hi) {
17221
+ const mid = lo + hi + 1 >> 1;
17222
+ if (lineStarts[mid] <= clamped) lo = mid;
17223
+ else hi = mid - 1;
17224
+ }
17225
+ return { line: lo + 1, column: clamped - lineStarts[lo] + 1 };
17226
+ };
17227
+ }
17228
+
17229
+ // src/core/externalState.ts
17230
+ var OPEN = '<script type="module">';
17231
+ var CLOSE = "</script>";
17232
+ function inlineExternalStates(html, stateTagName, fileReader) {
17233
+ let text3 = "";
17234
+ let from = 0;
17235
+ let shift = 0;
17236
+ const states = [];
17237
+ for (const element2 of parseWcsStateElements(html, stateTagName)) {
17238
+ const src = element2.srcSpan;
17239
+ if (src === void 0 || element2.srcAttr === void 0 || element2.stateAttr !== void 0 || element2.bindComponent) continue;
17240
+ const file = readStateScriptFile(element2.srcAttr, fileReader);
17241
+ if (file === void 0) continue;
17242
+ const blanks = [src];
17243
+ if (element2.jsonSpan !== void 0) blanks.push(element2.jsonSpan);
17244
+ blanks.sort((a, b) => a.start - b.start);
17245
+ for (const span of blanks) {
17246
+ text3 += html.slice(from, span.start) + " ".repeat(span.end - span.start);
17247
+ from = span.end;
17248
+ }
17249
+ text3 += html.slice(from, element2.tagEnd);
17250
+ from = element2.tagEnd;
17251
+ const content = file.text.replace(/<\/(script)/gi, "<\\$1");
17252
+ const at2 = element2.tagEnd + shift;
17253
+ const length = OPEN.length + content.length + CLOSE.length;
17254
+ text3 += OPEN + content + CLOSE;
17255
+ states.push({
17256
+ path: file.path,
17257
+ text: file.text,
17258
+ at: at2,
17259
+ contentAt: at2 + OPEN.length,
17260
+ length,
17261
+ valueStart: src.valueStart,
17262
+ valueEnd: src.valueEnd,
17263
+ position: createPositionMapper(file.text)
17264
+ });
17265
+ shift += length;
17266
+ }
17267
+ if (states.length === 0) return null;
17268
+ return { text: text3 + html.slice(from), states };
17269
+ }
17270
+ function locateInlined(page, offset2) {
17271
+ let shift = 0;
17272
+ for (const state of page.states) {
17273
+ if (offset2 < state.at) break;
17274
+ if (offset2 < state.at + state.length) {
17275
+ return { state, offset: Math.min(Math.max(offset2 - state.contentAt, 0), state.text.length) };
17276
+ }
17277
+ shift += state.length;
17278
+ }
17279
+ return { offset: offset2 - shift };
17280
+ }
17281
+
16753
17282
  // src/core/validateDocument.ts
16754
17283
  function validateDocument(text3, options = {}) {
17284
+ const stateTagName = options.stateTagName ?? "wcs-state";
17285
+ const fileReader = options.fileReader;
17286
+ const applicationSchema = options.applicationSchema ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.schema : void 0);
17287
+ const found = validateText(text3, options, applicationSchema);
17288
+ const page = fileReader === void 0 ? null : inlineExternalStates(text3, stateTagName, fileReader);
17289
+ if (page === null) return found;
17290
+ const key = (d) => [d.code, d.start, d.end, d.message, d.origin?.path, d.origin?.start].join("\0");
17291
+ const seen2 = new Set(found.map(key));
17292
+ const out = [...found];
17293
+ for (const d of validateText(page.text, options, applicationSchema)) {
17294
+ const start = locateInlined(page, d.start);
17295
+ const end = locateInlined(page, d.end);
17296
+ let mapped;
17297
+ if (start.state !== void 0) {
17298
+ const state = start.state;
17299
+ const fileEnd = end.state === state ? Math.max(end.offset, start.offset) : start.offset;
17300
+ mapped = {
17301
+ ...d,
17302
+ start: state.valueStart,
17303
+ end: state.valueEnd,
17304
+ origin: {
17305
+ path: state.path,
17306
+ start: start.offset,
17307
+ end: fileEnd,
17308
+ startPosition: state.position(start.offset),
17309
+ endPosition: state.position(fileEnd)
17310
+ }
17311
+ };
17312
+ } else {
17313
+ mapped = { ...d, start: start.offset, end: end.state === void 0 ? Math.max(end.offset, start.offset) : start.offset };
17314
+ }
17315
+ const k = key(mapped);
17316
+ if (seen2.has(k)) continue;
17317
+ seen2.add(k);
17318
+ out.push(mapped);
17319
+ }
17320
+ return sortDiagnostics(out);
17321
+ }
17322
+ function validateText(text3, options, applicationSchema) {
16755
17323
  const bindAttribute = options.bindAttribute ?? "data-wcs";
16756
17324
  const stateTagName = options.stateTagName ?? "wcs-state";
16757
17325
  const locale2 = options.locale;
16758
17326
  const fileReader = options.fileReader;
16759
- const applicationSchema = options.applicationSchema ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.schema : void 0);
16760
17327
  const out = [];
16761
17328
  out.push(...validateBindings(text3, bindAttribute, stateTagName, locale2, fileReader, applicationSchema));
16762
17329
  out.push(...validateTemplateSyntax(text3, stateTagName, bindAttribute, locale2, fileReader, applicationSchema));
@@ -16765,6 +17332,8 @@ function validateDocument(text3, options = {}) {
16765
17332
  out.push(...validateAriaAttributes(text3, bindAttribute, locale2));
16766
17333
  out.push(...validateDocumentEnv(text3, locale2));
16767
17334
  out.push(...validateSemantics(text3, stateTagName, locale2, bindAttribute));
17335
+ out.push(...validateScriptStateUses(text3, stateTagName, locale2, bindAttribute));
17336
+ out.push(...validateScriptReads(text3, stateTagName, locale2, fileReader, applicationSchema));
16768
17337
  out.push(...validateArrayMutations(text3, stateTagName, locale2));
16769
17338
  out.push(...validateWatchDeclarations(text3, stateTagName, locale2, fileReader));
16770
17339
  out.push(...validateScanDeclarations(text3, stateTagName, locale2));