@wcstack/typescript 2.6.1 → 3.1.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.
@@ -15,7 +15,7 @@ var __copyProps = (to, from, except, desc) => {
15
15
  }
16
16
  return to;
17
17
  };
18
- var __toCommonJS = (mod3) => __copyProps(__defProp({}, "__esModule", { value: true }), mod3);
18
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
19
19
 
20
20
  // src/schemaCore.ts
21
21
  var schemaCore_exports = {};
@@ -133,9 +133,10 @@ var WcsDiagnosticCode = {
133
133
  RecursionDeclarationInvalid: "wcs/recursion-declaration-invalid",
134
134
  TypeAnnotation: "wcs/type-annotation",
135
135
  TemplateSyntax: "wcs/template-syntax",
136
- // @wcstack/state 3.0 が拒否する(または読み方を変える)書き方の予告(info)。判定はランタイムの
137
- // [wcs/v3-migration] と同じ正本(@wcstack/state/parser の findV3MigrationIssues)。2.x 系だけの code。
138
- V3Migration: "wcs/v3-migration",
136
+ // ランタイムの正本パーサが [wcs/binding-syntax] で拒否する書き方(@wcstack/state 3.0 の文法の
137
+ // 厳格化: 閉じていない引用符・2 つ目の '#'・else: の値・構造ディレクティブの修飾子・空のフィルタ)。
138
+ // 判定は正本パーサに委ねる(service/bindingSyntaxValidator.ts)。
139
+ BindingSyntax: "wcs/binding-syntax",
139
140
  // --- <wcs-state> script: array reactivity hazards ---
140
141
  // 配列破壊的メソッド呼び出し(push 等 9 種)。Proxy を素通りしリアクティブ更新されない。
141
142
  // 同一参照の自己再代入でも要素の追加・削除は反映されない(docs/array-mutation-diagnostic-design.md §3)。
@@ -146,6 +147,9 @@ var WcsDiagnosticCode = {
146
147
  // --- built-in wcs-* tag contract (generated/builtinTags.generated.ts が正本) ---
147
148
  // 未知メンバーへのバインド(プロパティ / command. / eventToken. キー)。黙って無視される。
148
149
  TagMemberUnknown: "wcs/tag-member-unknown",
150
+ // タグのメンバー名が "on" で始まる(`once` 等)のに先頭ドット無しで束縛した: ランタイムはイベント束縛にして
151
+ // "ce" イベントを待ち、値は届かない。明示のプロパティ形 `.once:` を提案する(@wcstack/state 3.1・要件 B5 / 3.x 計画 D36)
152
+ OnPrefixedMember: "wcs/on-prefixed-member",
149
153
  // wcBindable 無宣言タグ(wcs-fetch-header 等のヘルパー)への spread。
150
154
  // ランタイム(expandSpread)は raiseError で落とす。
151
155
  SpreadNoBindable: "wcs/spread-no-bindable",
@@ -690,22 +694,15 @@ function valueMustBeDate(fnName) {
690
694
  function valueMustBeArray(fnName) {
691
695
  raiseError(`filter ${fnName} requires an array value`);
692
696
  }
693
- var warned = /* @__PURE__ */ new Set();
694
- function warnV3Migration(message) {
695
- if (warned.has(message)) {
696
- return;
697
- }
698
- warned.add(message);
699
- console.warn(`[@wcstack/state] [wcs/v3-migration] ${message} See "Preparing for 3.0" in the @wcstack/state README.`);
700
- }
701
697
  function validateNumberString(value) {
702
698
  if (!value || isNaN(Number(value))) {
703
699
  return false;
704
700
  }
705
701
  return true;
706
702
  }
707
- var eq = (options) => {
703
+ var eq = (options, literals) => {
708
704
  const opt = options?.[0] ?? optionsRequired("eq");
705
+ const literal2 = literals !== void 0 && literals.length > 0 ? literals[0] : opt;
709
706
  return (value) => {
710
707
  if (typeof value === "number") {
711
708
  if (!validateNumberString(opt)) {
@@ -716,11 +713,12 @@ var eq = (options) => {
716
713
  if (typeof value === "string") {
717
714
  return value === opt;
718
715
  }
719
- return value === opt;
716
+ return value === literal2;
720
717
  };
721
718
  };
722
- var ne = (options) => {
719
+ var ne = (options, literals) => {
723
720
  const opt = options?.[0] ?? optionsRequired("ne");
721
+ const literal2 = literals !== void 0 && literals.length > 0 ? literals[0] : opt;
724
722
  return (value) => {
725
723
  if (typeof value === "number") {
726
724
  if (!validateNumberString(opt)) {
@@ -731,7 +729,7 @@ var ne = (options) => {
731
729
  if (typeof value === "string") {
732
730
  return value !== opt;
733
731
  }
734
- return value !== opt;
732
+ return value !== literal2;
735
733
  };
736
734
  };
737
735
  var not = (_options) => {
@@ -1124,29 +1122,18 @@ var hms = (options) => {
1124
1122
  return `${hours}${opt}${minutes}${opt}${seconds}`;
1125
1123
  };
1126
1124
  };
1127
- function warnBigIntZero(fnName, value) {
1128
- if (value === 0n) {
1129
- warnV3Migration(`"${fnName}" got 0n: 3.0 treats it as falsy.`);
1130
- }
1131
- }
1132
1125
  var falsy = (_options) => {
1133
- return (value) => {
1134
- warnBigIntZero("falsy", value);
1135
- return value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value);
1136
- };
1126
+ return (value) => !value;
1137
1127
  };
1138
1128
  var truthy = (_options) => {
1139
- return (value) => {
1140
- warnBigIntZero("truthy", value);
1141
- return value !== false && value !== null && value !== void 0 && value !== 0 && value !== "" && !Number.isNaN(value);
1142
- };
1129
+ return (value) => !!value;
1143
1130
  };
1144
- var defaults = (options) => {
1131
+ var defaults = (options, literals) => {
1145
1132
  const opt = options?.[0] ?? optionsRequired("defaults");
1133
+ const fallback = literals !== void 0 && literals.length > 0 ? literals[0] : opt;
1146
1134
  return (value) => {
1147
- warnBigIntZero("defaults", value);
1148
- if (value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value)) {
1149
- return opt;
1135
+ if (!value) {
1136
+ return fallback;
1150
1137
  }
1151
1138
  return value;
1152
1139
  };
@@ -1366,6 +1353,7 @@ function getWcsManifest() {
1366
1353
  elseKeyword: ELSE_KEYWORD,
1367
1354
  spread: SPREAD_PROP,
1368
1355
  eventPropertyPrefix: EVENT_PROP_PREFIX,
1356
+ explicitPropertyPrefix: DELIMITER,
1369
1357
  propNamespaces: {
1370
1358
  eventToken: EVENT_TOKEN_NAMESPACE,
1371
1359
  command: COMMAND_NAMESPACE,
@@ -1461,11 +1449,11 @@ function foldSuffixIndexes(suffix) {
1461
1449
  }
1462
1450
  function foldRecursion(spec, path) {
1463
1451
  if (!path.startsWith(spec.anchor)) return null;
1464
- const unit3 = "." + spec.repeat;
1452
+ const unit2 = "." + spec.repeat;
1465
1453
  let cursor = spec.anchor.length;
1466
1454
  let depth = 0;
1467
- while (path.startsWith(unit3, cursor)) {
1468
- cursor += unit3.length;
1455
+ while (path.startsWith(unit2, cursor)) {
1456
+ cursor += unit2.length;
1469
1457
  depth++;
1470
1458
  }
1471
1459
  if (cursor !== path.length && path.charCodeAt(cursor) !== 46) return null;
@@ -1475,9 +1463,9 @@ function matchesRecursion(specs, path, has) {
1475
1463
  for (const spec of specs) {
1476
1464
  const folded = foldRecursion(spec, path);
1477
1465
  if (folded === null) continue;
1478
- const unit3 = "." + spec.repeat;
1466
+ const unit2 = "." + spec.repeat;
1479
1467
  for (let depth = folded.depth; depth >= 0; depth--) {
1480
- const rest = unit3.repeat(folded.depth - depth) + folded.rest;
1468
+ const rest = unit2.repeat(folded.depth - depth) + folded.rest;
1481
1469
  if (has(spec.anchor + rest)) return true;
1482
1470
  if (has(spec.recursiveAnchor + rest)) return true;
1483
1471
  for (let dot = rest.lastIndexOf("."); dot > 0; dot = rest.lastIndexOf(".", dot - 1)) {
@@ -1491,10 +1479,10 @@ function owningGetterSuffix(spec, getterSuffixes, path) {
1491
1479
  const pattern = indexSegmentsToWildcard(path);
1492
1480
  const folded = foldRecursion(spec, pattern);
1493
1481
  if (folded === null) return null;
1494
- const unit3 = "." + spec.repeat;
1482
+ const unit2 = "." + spec.repeat;
1495
1483
  for (const suffix of getterSuffixes) {
1496
1484
  for (let depth = folded.depth; depth >= 0; depth--) {
1497
- const expansion = spec.anchor + unit3.repeat(depth) + suffix;
1485
+ const expansion = spec.anchor + unit2.repeat(depth) + suffix;
1498
1486
  if (pattern === expansion || pattern.startsWith(expansion + ".")) return suffix;
1499
1487
  }
1500
1488
  }
@@ -1506,10 +1494,10 @@ function indexSegmentsToWildcard(path) {
1506
1494
  function concreteExpansionSuffix(spec, getterSuffixes, key) {
1507
1495
  const folded = foldRecursion(spec, key);
1508
1496
  if (folded === null) return null;
1509
- const unit3 = "." + spec.repeat;
1497
+ const unit2 = "." + spec.repeat;
1510
1498
  for (const suffix of getterSuffixes) {
1511
1499
  for (let depth = folded.depth; depth >= 0; depth--) {
1512
- if (key === spec.anchor + unit3.repeat(depth) + suffix) return suffix;
1500
+ if (key === spec.anchor + unit2.repeat(depth) + suffix) return suffix;
1513
1501
  }
1514
1502
  }
1515
1503
  return null;
@@ -1541,9 +1529,9 @@ function impliedStructurePaths(spec) {
1541
1529
  return out;
1542
1530
  }
1543
1531
  function structuralWriteTarget(spec, suffix) {
1544
- const unit3 = "." + spec.repeat;
1532
+ const unit2 = "." + spec.repeat;
1545
1533
  let rest = suffix;
1546
- while (rest.startsWith(unit3)) rest = rest.slice(unit3.length);
1534
+ while (rest.startsWith(unit2)) rest = rest.slice(unit2.length);
1547
1535
  if (rest.length === 0) return "node";
1548
1536
  if (rest === "." + spec.repeatList + ".length") return "length";
1549
1537
  const segments = spec.repeatList.split(".");
@@ -1553,15 +1541,15 @@ function structuralWriteTarget(spec, suffix) {
1553
1541
  return null;
1554
1542
  }
1555
1543
  function sameFamily(spec, a, b) {
1556
- const unit3 = "." + spec.repeat;
1544
+ const unit2 = "." + spec.repeat;
1557
1545
  const shorter = a.length <= b.length ? a : b;
1558
1546
  const longer = a.length <= b.length ? b : a;
1559
1547
  if (!longer.endsWith(shorter)) return false;
1560
1548
  const gap = longer.slice(0, longer.length - shorter.length);
1561
1549
  if (gap.length === 0) return true;
1562
- if (gap.length % unit3.length !== 0) return false;
1563
- for (let cursor = 0; cursor < gap.length; cursor += unit3.length) {
1564
- if (!gap.startsWith(unit3, cursor)) return false;
1550
+ if (gap.length % unit2.length !== 0) return false;
1551
+ for (let cursor = 0; cursor < gap.length; cursor += unit2.length) {
1552
+ if (!gap.startsWith(unit2, cursor)) return false;
1565
1553
  }
1566
1554
  return true;
1567
1555
  }
@@ -2751,8 +2739,8 @@ function matchOpenTag(html, pos, tagName) {
2751
2739
  const nameStart = pos + 1;
2752
2740
  const nameEnd = nameStart + tagName.length;
2753
2741
  if (nameEnd > html.length) return null;
2754
- const slice3 = html.slice(nameStart, nameEnd);
2755
- if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
2742
+ const slice2 = html.slice(nameStart, nameEnd);
2743
+ if (slice2.toLowerCase() !== tagName.toLowerCase()) return null;
2756
2744
  const charAfter = html[nameEnd];
2757
2745
  if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
2758
2746
  return null;
@@ -3002,8 +2990,8 @@ function getForTemplateDepthAt(html, openPos, offset2, bindAttrName) {
3002
2990
  }
3003
2991
 
3004
2992
  // src/core/messages.ts
3005
- function resolveLocale(locale3) {
3006
- if (locale3 === void 0 || locale3 === "" || /^ja\b|^ja[-_]/i.test(locale3) || locale3.toLowerCase() === "ja") return "ja";
2993
+ function resolveLocale(locale2) {
2994
+ if (locale2 === void 0 || locale2 === "" || /^ja\b|^ja[-_]/i.test(locale2) || locale2.toLowerCase() === "ja") return "ja";
3007
2995
  return "en";
3008
2996
  }
3009
2997
  var JA_EXPECTED_LABEL = {
@@ -3014,8 +3002,8 @@ var JA_EXPECTED_LABEL = {
3014
3002
  var ja = {
3015
3003
  spreadFilterNotAllowed: () => `\u30B9\u30D7\u30EC\u30C3\u30C9\u306E\u30BF\u30FC\u30B2\u30C3\u30C8\u306B\u30D5\u30A3\u30EB\u30BF\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
3016
3004
  spreadTargetRequired: () => `\u30B9\u30D7\u30EC\u30C3\u30C9\u306B\u306F\u30BF\u30FC\u30B2\u30C3\u30C8\u30D1\u30B9\u304C\u5FC5\u8981\u3067\u3059`,
3017
- v3Migration: (detail) => `3.0 \u3078\u306E\u6E96\u5099\uFF082.x \u3067\u306F\u3053\u306E\u307E\u307E\u52D5\u304D\u307E\u3059\uFF09: ${detail}`,
3018
3005
  structuralMustBeSingle: (d) => `'${d}' \u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u306F\u5358\u72EC\u3067\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08';' \u3067\u4ED6\u306E\u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u3068\u4F75\u8A18\u3067\u304D\u307E\u305B\u3093\u3002\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09`,
3006
+ bindingSyntax: (detail) => `\u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u306E\u69CB\u6587\u30A8\u30E9\u30FC\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09: ${detail}`,
3019
3007
  eventTokenUndeclared: (t) => `\u30A4\u30D9\u30F3\u30C8\u30C8\u30FC\u30AF\u30F3 "${t}" \u306F $eventTokens \u306B\u5BA3\u8A00\u3055\u308C\u3066\u3044\u307E\u305B\u3093`,
3020
3008
  commandRhsFormat: () => `command \u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u306E\u53F3\u8FBA\u306B\u306F $command.<name>\uFF08$commandTokens \u3067\u5BA3\u8A00\uFF09\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044`,
3021
3009
  commandTokenUndeclared: (t) => `\u30B3\u30DE\u30F3\u30C9\u30C8\u30FC\u30AF\u30F3 "${t}" \u306F $commandTokens \u306B\u5BA3\u8A00\u3055\u308C\u3066\u3044\u307E\u305B\u3093`,
@@ -3080,6 +3068,7 @@ var ja = {
3080
3068
  arrayMutation: (m, alt) => `\u914D\u5217\u306E\u7834\u58CA\u7684\u30E1\u30BD\u30C3\u30C9 "${m}" \u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\uFF08\u540C\u4E00\u53C2\u7167\u306E\u81EA\u5DF1\u518D\u4EE3\u5165\u3067\u3082\u8981\u7D20\u306E\u8FFD\u52A0\u30FB\u524A\u9664\u306F\u53CD\u6620\u3055\u308C\u307E\u305B\u3093\uFF09\u3002\u975E\u7834\u58CA\u30E1\u30BD\u30C3\u30C9\u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: ${alt}\uFF09\u3002`,
3081
3069
  arrayIndexAssign: (sp) => `\u914D\u5217\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3078\u306E\u76F4\u63A5\u4EE3\u5165\u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\u3002this["${sp}"] \u306E\u3088\u3046\u306A\u30C9\u30C3\u30C8\u30D1\u30B9\u4EE3\u5165\u3001\u307E\u305F\u306F with() \u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
3082
3070
  tagMemberUnknown: (prop, tag) => `"${prop}" \u306F <${tag}> \u306E wcBindable \u30E1\u30F3\u30D0\u30FC\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u672A\u77E5\u30E1\u30F3\u30D0\u30FC\u3078\u306E\u30D0\u30A4\u30F3\u30C9\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059\uFF09`,
3071
+ onPrefixedMember: (member, tag) => `"${member}" \u306F <${tag}> \u306E\u30E1\u30F3\u30D0\u30FC\u3067\u3059\u304C\u3001"on" \u3067\u59CB\u307E\u308B\u540D\u524D\u306F\u30A4\u30D9\u30F3\u30C8\u675F\u7E1B\u306B\u306A\u308A\uFF08"${member.slice(2)}" \u30A4\u30D9\u30F3\u30C8\u3092\u5F85\u3064\uFF09\u3001\u5024\u306F\u5C4A\u304D\u307E\u305B\u3093\u3002\u30D7\u30ED\u30D1\u30C6\u30A3\u3068\u3057\u3066\u675F\u7E1B\u3059\u308B\u306B\u306F ".${member}:" \u3068\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.1\uFF09`,
3083
3072
  tagCommandUnknown: (name, tag, declared) => `"${name}" \u306F <${tag}> \u306E command \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
3084
3073
  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`,
3085
3074
  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`,
@@ -3203,8 +3192,8 @@ var EN_EXPECTED_LABEL = {
3203
3192
  var en = {
3204
3193
  spreadFilterNotAllowed: () => `Filters cannot be applied to a spread target`,
3205
3194
  spreadTargetRequired: () => `Spread requires a target path`,
3206
- v3Migration: (detail) => `Preparing for 3.0 (this still runs on 2.x): ${detail}`,
3207
3195
  structuralMustBeSingle: (d) => `'${d}' must be the only binding in this attribute (it cannot be combined with ';'; the runtime throws at load time)`,
3196
+ bindingSyntax: (detail) => `Binding syntax error (the runtime throws at load time): ${detail}`,
3208
3197
  eventTokenUndeclared: (t) => `Event token "${t}" is not declared in $eventTokens`,
3209
3198
  commandRhsFormat: () => `The right side of a command binding must be $command.<name> (declared in $commandTokens)`,
3210
3199
  commandTokenUndeclared: (t) => `Command token "${t}" is not declared in $commandTokens`,
@@ -3269,6 +3258,7 @@ var en = {
3269
3258
  arrayMutation: (m, alt) => `Destructive array method "${m}" does not trigger a reactive update (re-assigning the same reference does not reflect added/removed elements either). Use a non-destructive method with reassignment (e.g. ${alt}).`,
3270
3259
  arrayIndexAssign: (sp) => `Assigning directly to an array index does not trigger a reactive update. Use a dot-path assignment like this["${sp}"], or with() plus reassignment.`,
3271
3260
  tagMemberUnknown: (prop, tag) => `"${prop}" is not a wcBindable member of <${tag}> (bindings to unknown members are silently ignored)`,
3261
+ onPrefixedMember: (member, tag) => `"${member}" is a member of <${tag}>, but a name starting with "on" makes an event binding (it listens for a "${member.slice(2)}" event) and the value never arrives. Write ".${member}:" to bind the property (@wcstack/state 3.1)`,
3272
3262
  tagCommandUnknown: (name, tag, declared) => `"${name}" is not a command of <${tag}> (declared: ${declared})`,
3273
3263
  spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
3274
3264
  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})`,
@@ -3385,15 +3375,15 @@ var en = {
3385
3375
  recursionInMountedComponent: (subject) => `${subject} is not run by a mounted component (bind-component); the runtime warns with wcs/mount-dollar-declaration and drops it. Declare $recursion and "**" getters on the root state \u2014 the anchor path is resolved against the root tree`
3386
3376
  };
3387
3377
  var CATALOGS = { ja, en };
3388
- function getMessages(locale3) {
3389
- return CATALOGS[resolveLocale(locale3)];
3378
+ function getMessages(locale2) {
3379
+ return CATALOGS[resolveLocale(locale2)];
3390
3380
  }
3391
3381
 
3392
3382
  // src/service/bindingValidator.ts
3393
3383
  var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
3394
- function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader, applicationSchema) {
3384
+ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, fileReader, applicationSchema) {
3395
3385
  const diagnostics = [];
3396
- const msgs = getMessages(locale3);
3386
+ const msgs = getMessages(locale2);
3397
3387
  const statePaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
3398
3388
  const attrs = findAllBindAttributes(html, attrName);
3399
3389
  let structuralTemplates = null;
@@ -3950,8 +3940,8 @@ function isLiteral(value) {
3950
3940
  }
3951
3941
 
3952
3942
  // src/service/stateTypeValidator.ts
3953
- function validateStateTypes(html, stateTagName = "wcs-state", locale3) {
3954
- const msgs = getMessages(locale3);
3943
+ function validateStateTypes(html, stateTagName = "wcs-state", locale2) {
3944
+ const msgs = getMessages(locale2);
3955
3945
  const blocks = parseWcsScriptBlocks(html, stateTagName);
3956
3946
  const diagnostics = [];
3957
3947
  for (const block of blocks) {
@@ -4078,8 +4068,8 @@ function isApiRoot(root) {
4078
4068
  // src/service/nestedAssignValidator.ts
4079
4069
  var NESTED_ASSIGN = new RegExp(`${ROOT_DOT}(${CHAIN_ONE_PLUS})${ASSIGN_TAIL}`, "g");
4080
4070
  var PRE_NESTED_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_DOT}(${CHAIN_ONE_PLUS})`, "g");
4081
- function validateNestedAssigns(html, stateTagName = "wcs-state", locale3) {
4082
- const msgs = getMessages(locale3);
4071
+ function validateNestedAssigns(html, stateTagName = "wcs-state", locale2) {
4072
+ const msgs = getMessages(locale2);
4083
4073
  const blocks = parseWcsScriptBlocks(html, stateTagName);
4084
4074
  const diagnostics = [];
4085
4075
  for (const block of blocks) {
@@ -4130,8 +4120,8 @@ var PRE_BRACKET_INDEX = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}(${BRACKETS_ONLY
4130
4120
  function toAccessor(path) {
4131
4121
  return /^[A-Za-z_]\w*$/.test(path) ? `this.${path}` : `this["${path}"]`;
4132
4122
  }
4133
- function validateArrayMutations(html, stateTagName = "wcs-state", locale3) {
4134
- const msgs = getMessages(locale3);
4123
+ function validateArrayMutations(html, stateTagName = "wcs-state", locale2) {
4124
+ const msgs = getMessages(locale2);
4135
4125
  const blocks = parseWcsScriptBlocks(html, stateTagName);
4136
4126
  const diagnostics = [];
4137
4127
  for (const block of blocks) {
@@ -4241,9 +4231,9 @@ function isInsideTag(html, offset2, tagName) {
4241
4231
  }
4242
4232
 
4243
4233
  // src/service/templateSyntaxValidator.ts
4244
- function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader, applicationSchema) {
4234
+ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale2, fileReader, applicationSchema) {
4245
4235
  const diagnostics = [];
4246
- const msgs = getMessages(locale3);
4236
+ const msgs = getMessages(locale2);
4247
4237
  const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
4248
4238
  const defaultSchema = applicationSchema;
4249
4239
  const missingVerdict = (path, displayPath, pathSet2, scoped) => {
@@ -4401,6 +4391,10 @@ var ELSE_KEYWORD2 = "else";
4401
4391
  var SPREAD_PROP2 = "...";
4402
4392
  var EVENT_PROP_PREFIX2 = "on";
4403
4393
  var EVENT_TOKEN_NAMESPACE2 = "eventToken";
4394
+ var COMMAND_NAMESPACE2 = "command";
4395
+ var CLASS_NAMESPACE2 = "class";
4396
+ var ATTR_NAMESPACE2 = "attr";
4397
+ var STYLE_NAMESPACE2 = "style";
4404
4398
  var INDEX_PARAM_PREFIX2 = "$";
4405
4399
  var tmpIndexByIndexName2 = {};
4406
4400
  for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
@@ -4509,43 +4503,6 @@ var PathInfo = class {
4509
4503
  this.wildcardCount = wildcardCount;
4510
4504
  }
4511
4505
  };
4512
- function editDistance(a, b, max) {
4513
- if (Math.abs(a.length - b.length) > max) {
4514
- return max + 1;
4515
- }
4516
- const prev = new Array(b.length + 1);
4517
- const curr = new Array(b.length + 1);
4518
- for (let j = 0; j <= b.length; j++) {
4519
- prev[j] = j;
4520
- }
4521
- for (let i = 1; i <= a.length; i++) {
4522
- curr[0] = i;
4523
- for (let j = 1; j <= b.length; j++) {
4524
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
4525
- curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
4526
- }
4527
- for (let j = 0; j <= b.length; j++) {
4528
- prev[j] = curr[j];
4529
- }
4530
- }
4531
- return prev[b.length];
4532
- }
4533
- function didYouMean(input, candidates) {
4534
- if (input.length === 0) {
4535
- return "";
4536
- }
4537
- const folded = input.toLowerCase();
4538
- let best = null;
4539
- let bestDistance = 3;
4540
- for (const candidate of candidates) {
4541
- const distance = editDistance(folded, candidate.toLowerCase(), 2);
4542
- if (distance < bestDistance) {
4543
- best = candidate;
4544
- bestDistance = distance;
4545
- }
4546
- }
4547
- return best !== null ? ` Did you mean "${best}"?` : "";
4548
- }
4549
4506
  var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
4550
4507
  var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
4551
4508
  "if",
@@ -4553,660 +4510,10 @@ var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
4553
4510
  "else",
4554
4511
  "for"
4555
4512
  ]);
4556
- var _config2 = {
4557
- locale: "en"
4558
- };
4559
- var config2 = _config2;
4560
- function optionsRequired2(fnName) {
4561
- raiseError2(`filter ${fnName} requires at least one option`);
4562
- }
4563
- function optionMustBeNumber2(fnName) {
4564
- raiseError2(`filter ${fnName} requires a number as option`);
4565
- }
4566
- function valueMustBeNumber2(fnName) {
4567
- raiseError2(`filter ${fnName} requires a number value`);
4568
- }
4569
- function valueMustBeBoolean2(fnName) {
4570
- raiseError2(`filter ${fnName} requires a boolean value`);
4571
- }
4572
- function valueMustBeDate2(fnName) {
4573
- raiseError2(`filter ${fnName} requires a date value`);
4574
- }
4575
- function valueMustBeArray2(fnName) {
4576
- raiseError2(`filter ${fnName} requires an array value`);
4577
- }
4578
- var NO_ARGS = /* @__PURE__ */ new Set([
4579
- "not",
4580
- "abs",
4581
- "uc",
4582
- "lc",
4583
- "cap",
4584
- "trim",
4585
- "rev",
4586
- "int",
4587
- "float",
4588
- "date",
4589
- "time",
4590
- "datetime",
4591
- "falsy",
4592
- "truthy",
4593
- "boolean",
4594
- "number",
4595
- "string",
4596
- "null"
4597
- ]);
4598
- var TWO_ARGS = /* @__PURE__ */ new Set(["clamp", "slice", "substr", "pad", "truncate"]);
4599
- function maxFilterArgs(name) {
4600
- return NO_ARGS.has(name) ? 0 : TWO_ARGS.has(name) ? 2 : 1;
4601
- }
4602
- var STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set(["for", "if", "elseif", "else", "..."]);
4603
- var TYPED_LITERAL = /(?:^|\|)\s*(eq|ne|defaults)\s*\(\s*(true|false|null)\s*\)/g;
4604
- function hasUnterminatedQuote(text) {
4605
- let quote = null;
4606
- for (let i = 0; i < text.length; i++) {
4607
- const c = text[i];
4608
- if (quote !== null) {
4609
- if (c === quote)
4610
- quote = null;
4611
- } else if (c === "'" || c === '"') {
4612
- quote = c;
4613
- }
4614
- }
4615
- return quote !== null;
4616
- }
4617
- function checkStatePart(text, parsed, issues) {
4618
- if (hasUnterminatedQuote(text)) {
4619
- issues.push(`"${text}": 3.0 rejects the unterminated quote.`);
4620
- }
4621
- for (const [, fnName, literal2] of text.matchAll(TYPED_LITERAL)) {
4622
- issues.push(`"${fnName}(${literal2})": 3.0 reads an unquoted ${literal2} as a ${literal2 === "null" ? "null" : "boolean"}, not the text. Write ${fnName}('${literal2}') to keep the text.`);
4623
- }
4624
- if (parsed !== null) {
4625
- issues.push(...findFilterArityIssues([parsed]));
4626
- }
4627
- }
4628
- function findFilterArityIssues(results) {
4629
- const issues = [];
4630
- for (const result of results) {
4631
- for (const filter of [...result.inFilters, ...result.outFilters]) {
4632
- const max = maxFilterArgs(filter.filterName);
4633
- if (filter.args.length > max) {
4634
- issues.push(`"${filter.filterName}" takes at most ${max} argument(s); 3.0 rejects more.`);
4635
- }
4636
- }
4637
- }
4638
- return issues;
4639
- }
4640
- function findV3MigrationIssues(expr, parsed = null) {
4641
- const issues = [];
4642
- const colon = expr.indexOf(":");
4643
- if (colon === -1)
4644
- return issues;
4645
- const propPart = expr.slice(0, colon).trim();
4646
- const modifierParts = propPart.split("#");
4647
- const keyword = modifierParts[0].split("|")[0].trim();
4648
- if (modifierParts.length > 2) {
4649
- issues.push(`"${propPart}": 3.0 rejects a second "#". Write "${modifierParts[0].trim()}#${modifierParts.slice(1).map((m) => m.trim()).join(",")}".`);
4650
- }
4651
- if (keyword === "else" && expr.slice(colon + 1).trim().length > 0) {
4652
- issues.push(`"${expr}": 3.0 rejects a value after "else:".`);
4653
- }
4654
- if (STRUCTURAL_KEYWORDS.has(keyword) && keyword !== propPart) {
4655
- issues.push(`"${propPart}": 3.0 rejects modifiers and filters on "${keyword}". Write "${keyword}:".`);
4656
- }
4657
- if ((keyword === "radio" || keyword === "checkbox") && keyword !== propPart) {
4658
- issues.push(`"${propPart}": 3.0 keeps this a ${keyword} binding that honours the modifiers (2.x binds a property named "${keyword}").`);
4659
- }
4660
- checkStatePart(expr.slice(colon + 1).trim(), parsed, issues);
4661
- return issues;
4662
- }
4663
- function findEmbeddedV3MigrationIssues(expression, parsed = null) {
4664
- const issues = [];
4665
- checkStatePart(expression, parsed, issues);
4666
- return issues;
4667
- }
4668
- var warned2 = /* @__PURE__ */ new Set();
4669
- function warnV3Migration2(message) {
4670
- if (warned2.has(message)) {
4671
- return;
4672
- }
4673
- warned2.add(message);
4674
- console.warn(`[@wcstack/state] [wcs/v3-migration] ${message} See "Preparing for 3.0" in the @wcstack/state README.`);
4675
- }
4676
- function validateNumberString2(value) {
4677
- if (!value || isNaN(Number(value))) {
4678
- return false;
4679
- }
4680
- return true;
4681
- }
4682
- var eq2 = (options) => {
4683
- const opt = options?.[0] ?? optionsRequired2("eq");
4684
- return (value) => {
4685
- if (typeof value === "number") {
4686
- if (!validateNumberString2(opt)) {
4687
- optionMustBeNumber2("eq");
4688
- }
4689
- return value === Number(opt);
4690
- }
4691
- if (typeof value === "string") {
4692
- return value === opt;
4693
- }
4694
- return value === opt;
4695
- };
4696
- };
4697
- var ne2 = (options) => {
4698
- const opt = options?.[0] ?? optionsRequired2("ne");
4699
- return (value) => {
4700
- if (typeof value === "number") {
4701
- if (!validateNumberString2(opt)) {
4702
- optionMustBeNumber2("ne");
4703
- }
4704
- return value !== Number(opt);
4705
- }
4706
- if (typeof value === "string") {
4707
- return value !== opt;
4708
- }
4709
- return value !== opt;
4710
- };
4711
- };
4712
- var not2 = (_options) => {
4713
- return (value) => {
4714
- if (typeof value !== "boolean") {
4715
- valueMustBeBoolean2("not");
4716
- }
4717
- return !value;
4718
- };
4719
- };
4720
- var lt2 = (options) => {
4721
- const opt = options?.[0] ?? optionsRequired2("lt");
4722
- if (!validateNumberString2(opt)) {
4723
- optionMustBeNumber2("lt");
4724
- }
4725
- return (value) => {
4726
- if (typeof value !== "number") {
4727
- valueMustBeNumber2("lt");
4728
- }
4729
- return value < Number(opt);
4730
- };
4731
- };
4732
- var le2 = (options) => {
4733
- const opt = options?.[0] ?? optionsRequired2("le");
4734
- if (!validateNumberString2(opt)) {
4735
- optionMustBeNumber2("le");
4736
- }
4737
- return (value) => {
4738
- if (typeof value !== "number") {
4739
- valueMustBeNumber2("le");
4740
- }
4741
- return value <= Number(opt);
4742
- };
4743
- };
4744
- var gt2 = (options) => {
4745
- const opt = options?.[0] ?? optionsRequired2("gt");
4746
- if (!validateNumberString2(opt)) {
4747
- optionMustBeNumber2("gt");
4748
- }
4749
- return (value) => {
4750
- if (typeof value !== "number") {
4751
- valueMustBeNumber2("gt");
4752
- }
4753
- return value > Number(opt);
4754
- };
4755
- };
4756
- var ge2 = (options) => {
4757
- const opt = options?.[0] ?? optionsRequired2("ge");
4758
- if (!validateNumberString2(opt)) {
4759
- optionMustBeNumber2("ge");
4760
- }
4761
- return (value) => {
4762
- if (typeof value !== "number") {
4763
- valueMustBeNumber2("ge");
4764
- }
4765
- return value >= Number(opt);
4766
- };
4767
- };
4768
- var inc2 = (options) => {
4769
- const opt = options?.[0] ?? optionsRequired2("inc");
4770
- if (!validateNumberString2(opt)) {
4771
- optionMustBeNumber2("inc");
4772
- }
4773
- return (value) => {
4774
- if (typeof value !== "number") {
4775
- valueMustBeNumber2("inc");
4776
- }
4777
- return value + Number(opt);
4778
- };
4779
- };
4780
- var dec2 = (options) => {
4781
- const opt = options?.[0] ?? optionsRequired2("dec");
4782
- if (!validateNumberString2(opt)) {
4783
- optionMustBeNumber2("dec");
4784
- }
4785
- return (value) => {
4786
- if (typeof value !== "number") {
4787
- valueMustBeNumber2("dec");
4788
- }
4789
- return value - Number(opt);
4790
- };
4791
- };
4792
- var mul2 = (options) => {
4793
- const opt = options?.[0] ?? optionsRequired2("mul");
4794
- if (!validateNumberString2(opt)) {
4795
- optionMustBeNumber2("mul");
4796
- }
4797
- return (value) => {
4798
- if (typeof value !== "number") {
4799
- valueMustBeNumber2("mul");
4800
- }
4801
- return value * Number(opt);
4802
- };
4803
- };
4804
- var div2 = (options) => {
4805
- const opt = options?.[0] ?? optionsRequired2("div");
4806
- if (!validateNumberString2(opt)) {
4807
- optionMustBeNumber2("div");
4808
- }
4809
- return (value) => {
4810
- if (typeof value !== "number") {
4811
- valueMustBeNumber2("div");
4812
- }
4813
- return value / Number(opt);
4814
- };
4815
- };
4816
- var mod2 = (options) => {
4817
- const opt = options?.[0] ?? optionsRequired2("mod");
4818
- if (!validateNumberString2(opt)) {
4819
- optionMustBeNumber2("mod");
4820
- }
4821
- return (value) => {
4822
- if (typeof value !== "number") {
4823
- valueMustBeNumber2("mod");
4824
- }
4825
- return value % Number(opt);
4826
- };
4827
- };
4828
- var abs2 = (_options) => {
4829
- return (value) => {
4830
- if (typeof value !== "number") {
4831
- valueMustBeNumber2("abs");
4832
- }
4833
- return Math.abs(value);
4834
- };
4835
- };
4836
- var clamp2 = (options) => {
4837
- const opt1 = options?.[0] ?? optionsRequired2("clamp");
4838
- if (!validateNumberString2(opt1)) {
4839
- optionMustBeNumber2("clamp");
4840
- }
4841
- const opt2 = options?.[1] ?? optionsRequired2("clamp");
4842
- if (!validateNumberString2(opt2)) {
4843
- optionMustBeNumber2("clamp");
4844
- }
4845
- const min = Number(opt1);
4846
- const max = Number(opt2);
4847
- return (value) => {
4848
- if (typeof value !== "number") {
4849
- valueMustBeNumber2("clamp");
4850
- }
4851
- return Math.min(Math.max(value, min), max);
4852
- };
4853
- };
4854
- var fix2 = (options) => {
4855
- const opt = options?.[0] ?? "0";
4856
- if (!validateNumberString2(opt)) {
4857
- optionMustBeNumber2("fix");
4858
- }
4859
- return (value) => {
4860
- if (typeof value !== "number") {
4861
- valueMustBeNumber2("fix");
4862
- }
4863
- return value.toFixed(Number(opt));
4864
- };
4865
- };
4866
- var locale2 = (options) => {
4867
- const explicit = options?.[0];
4868
- return (value) => {
4869
- if (typeof value !== "number") {
4870
- valueMustBeNumber2("locale");
4871
- }
4872
- return value.toLocaleString(explicit ?? config2.locale);
4873
- };
4874
- };
4875
- var uc2 = (_options) => {
4876
- return (value) => {
4877
- return String(value).toUpperCase();
4878
- };
4879
- };
4880
- var lc2 = (_options) => {
4881
- return (value) => {
4882
- return String(value).toLowerCase();
4883
- };
4884
- };
4885
- var cap2 = (_options) => {
4886
- return (value) => {
4887
- const v = String(value);
4888
- if (v.length === 0) {
4889
- return v;
4890
- }
4891
- if (v.length === 1) {
4892
- return v.toUpperCase();
4893
- }
4894
- return v.charAt(0).toUpperCase() + v.slice(1);
4895
- };
4896
- };
4897
- var trim2 = (_options) => {
4898
- return (value) => {
4899
- return String(value).trim();
4900
- };
4901
- };
4902
- var slice2 = (options) => {
4903
- const numberedOpts = [];
4904
- const opt1 = options?.[0] ?? optionsRequired2("slice");
4905
- if (!validateNumberString2(opt1)) {
4906
- optionMustBeNumber2("slice");
4907
- }
4908
- numberedOpts.push(Number(opt1));
4909
- const opt2 = options?.[1];
4910
- if (typeof opt2 !== "undefined") {
4911
- if (!validateNumberString2(opt2)) {
4912
- optionMustBeNumber2("slice");
4913
- }
4914
- numberedOpts.push(Number(opt2));
4915
- }
4916
- return (value) => {
4917
- return String(value).slice(...numberedOpts);
4918
- };
4919
- };
4920
- var substr2 = (options) => {
4921
- const opt1 = options?.[0] ?? optionsRequired2("substr");
4922
- if (!validateNumberString2(opt1)) {
4923
- optionMustBeNumber2("substr");
4924
- }
4925
- const opt2 = options?.[1] ?? optionsRequired2("substr");
4926
- if (!validateNumberString2(opt2)) {
4927
- optionMustBeNumber2("substr");
4928
- }
4929
- return (value) => {
4930
- return String(value).substr(Number(opt1), Number(opt2));
4931
- };
4932
- };
4933
- var pad2 = (options) => {
4934
- const opt1 = options?.[0] ?? optionsRequired2("pad");
4935
- if (!validateNumberString2(opt1)) {
4936
- optionMustBeNumber2("pad");
4937
- }
4938
- const opt2 = options?.[1] ?? "0";
4939
- return (value) => {
4940
- return String(value).padStart(Number(opt1), opt2);
4941
- };
4942
- };
4943
- var rep2 = (options) => {
4944
- const opt = options?.[0] ?? optionsRequired2("rep");
4945
- if (!validateNumberString2(opt)) {
4946
- optionMustBeNumber2("rep");
4947
- }
4948
- return (value) => {
4949
- return String(value).repeat(Number(opt));
4950
- };
4951
- };
4952
- var rev2 = (_options) => {
4953
- return (value) => {
4954
- return String(value).split("").reverse().join("");
4955
- };
4956
- };
4957
- var int2 = (_options) => {
4958
- return (value) => {
4959
- return parseInt(String(value), 10);
4960
- };
4961
- };
4962
- var float2 = (_options) => {
4963
- return (value) => {
4964
- return parseFloat(String(value));
4965
- };
4966
- };
4967
- var round2 = (options) => {
4968
- const opt = options?.[0] ?? "0";
4969
- if (!validateNumberString2(opt)) {
4970
- optionMustBeNumber2("round");
4971
- }
4972
- return (value) => {
4973
- if (typeof value !== "number") {
4974
- valueMustBeNumber2("round");
4975
- }
4976
- const optValue = Math.pow(10, Number(opt));
4977
- return Math.round(value * optValue) / optValue;
4978
- };
4979
- };
4980
- var floor2 = (options) => {
4981
- const opt = options?.[0] ?? "0";
4982
- if (!validateNumberString2(opt)) {
4983
- optionMustBeNumber2("floor");
4984
- }
4985
- return (value) => {
4986
- if (typeof value !== "number") {
4987
- valueMustBeNumber2("floor");
4988
- }
4989
- const optValue = Math.pow(10, Number(opt));
4990
- return Math.floor(value * optValue) / optValue;
4991
- };
4992
- };
4993
- var ceil2 = (options) => {
4994
- const opt = options?.[0] ?? "0";
4995
- if (!validateNumberString2(opt)) {
4996
- optionMustBeNumber2("ceil");
4997
- }
4998
- return (value) => {
4999
- if (typeof value !== "number") {
5000
- valueMustBeNumber2("ceil");
5001
- }
5002
- const optValue = Math.pow(10, Number(opt));
5003
- return Math.ceil(value * optValue) / optValue;
5004
- };
5005
- };
5006
- var percent2 = (options) => {
5007
- const opt = options?.[0] ?? "0";
5008
- if (!validateNumberString2(opt)) {
5009
- optionMustBeNumber2("percent");
5010
- }
5011
- return (value) => {
5012
- if (typeof value !== "number") {
5013
- valueMustBeNumber2("percent");
5014
- }
5015
- return `${(value * 100).toFixed(Number(opt))}%`;
5016
- };
5017
- };
5018
- var unit2 = (options) => {
5019
- const opt = options?.[0] ?? optionsRequired2("unit");
5020
- return (value) => {
5021
- if (value === null || typeof value === "undefined") {
5022
- return value;
5023
- }
5024
- return String(value) + opt;
5025
- };
5026
- };
5027
- var join2 = (options) => {
5028
- const opt = options?.[0] ?? ", ";
5029
- return (value) => {
5030
- if (!Array.isArray(value)) {
5031
- valueMustBeArray2("join");
5032
- }
5033
- return value.join(opt);
5034
- };
5035
- };
5036
- var truncate2 = (options) => {
5037
- const opt1 = options?.[0] ?? optionsRequired2("truncate");
5038
- if (!validateNumberString2(opt1)) {
5039
- optionMustBeNumber2("truncate");
5040
- }
5041
- const maxLength = Number(opt1);
5042
- const suffix = options?.[1] ?? "\u2026";
5043
- return (value) => {
5044
- const v = String(value);
5045
- if (v.length <= maxLength) {
5046
- return v;
5047
- }
5048
- return v.slice(0, maxLength) + suffix;
5049
- };
5050
- };
5051
- var date2 = (options) => {
5052
- const explicit = options?.[0];
5053
- return (value) => {
5054
- if (!(value instanceof Date)) {
5055
- valueMustBeDate2("date");
5056
- }
5057
- return value.toLocaleDateString(explicit ?? config2.locale);
5058
- };
5059
- };
5060
- var time2 = (options) => {
5061
- const explicit = options?.[0];
5062
- return (value) => {
5063
- if (!(value instanceof Date)) {
5064
- valueMustBeDate2("time");
5065
- }
5066
- return value.toLocaleTimeString(explicit ?? config2.locale);
5067
- };
5068
- };
5069
- var datetime2 = (options) => {
5070
- const explicit = options?.[0];
5071
- return (value) => {
5072
- if (!(value instanceof Date)) {
5073
- valueMustBeDate2("datetime");
5074
- }
5075
- return value.toLocaleString(explicit ?? config2.locale);
5076
- };
5077
- };
5078
- var ymd2 = (options) => {
5079
- const opt = options?.[0] ?? "-";
5080
- return (value) => {
5081
- if (!(value instanceof Date)) {
5082
- valueMustBeDate2("ymd");
5083
- }
5084
- const year = value.getFullYear().toString();
5085
- const month = (value.getMonth() + 1).toString().padStart(2, "0");
5086
- const day = value.getDate().toString().padStart(2, "0");
5087
- return `${year}${opt}${month}${opt}${day}`;
5088
- };
5089
- };
5090
- var hms2 = (options) => {
5091
- const opt = options?.[0] ?? ":";
5092
- return (value) => {
5093
- if (!(value instanceof Date)) {
5094
- valueMustBeDate2("hms");
5095
- }
5096
- const hours = value.getHours().toString().padStart(2, "0");
5097
- const minutes = value.getMinutes().toString().padStart(2, "0");
5098
- const seconds = value.getSeconds().toString().padStart(2, "0");
5099
- return `${hours}${opt}${minutes}${opt}${seconds}`;
5100
- };
5101
- };
5102
- function warnBigIntZero2(fnName, value) {
5103
- if (value === 0n) {
5104
- warnV3Migration2(`"${fnName}" got 0n: 3.0 treats it as falsy.`);
5105
- }
4513
+ var resolvedByKey = /* @__PURE__ */ new Map();
4514
+ function clearFilterResolutionCache() {
4515
+ resolvedByKey.clear();
5106
4516
  }
5107
- var falsy2 = (_options) => {
5108
- return (value) => {
5109
- warnBigIntZero2("falsy", value);
5110
- return value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value);
5111
- };
5112
- };
5113
- var truthy2 = (_options) => {
5114
- return (value) => {
5115
- warnBigIntZero2("truthy", value);
5116
- return value !== false && value !== null && value !== void 0 && value !== 0 && value !== "" && !Number.isNaN(value);
5117
- };
5118
- };
5119
- var defaults2 = (options) => {
5120
- const opt = options?.[0] ?? optionsRequired2("defaults");
5121
- return (value) => {
5122
- warnBigIntZero2("defaults", value);
5123
- if (value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value)) {
5124
- return opt;
5125
- }
5126
- return value;
5127
- };
5128
- };
5129
- var boolean2 = (_options) => {
5130
- return (value) => {
5131
- return Boolean(value);
5132
- };
5133
- };
5134
- var number2 = (_options) => {
5135
- return (value) => {
5136
- return Number(value);
5137
- };
5138
- };
5139
- var string2 = (_options) => {
5140
- return (value) => {
5141
- return String(value);
5142
- };
5143
- };
5144
- var _null2 = (_options) => {
5145
- return (value) => {
5146
- return value === "" ? null : value;
5147
- };
5148
- };
5149
- var builtinFilters2 = {
5150
- "eq": eq2,
5151
- "ne": ne2,
5152
- "not": not2,
5153
- "lt": lt2,
5154
- "le": le2,
5155
- "gt": gt2,
5156
- "ge": ge2,
5157
- "inc": inc2,
5158
- "dec": dec2,
5159
- "mul": mul2,
5160
- "div": div2,
5161
- "mod": mod2,
5162
- "abs": abs2,
5163
- "clamp": clamp2,
5164
- "fix": fix2,
5165
- "locale": locale2,
5166
- "uc": uc2,
5167
- "lc": lc2,
5168
- "cap": cap2,
5169
- "trim": trim2,
5170
- "slice": slice2,
5171
- "substr": substr2,
5172
- "pad": pad2,
5173
- "rep": rep2,
5174
- "rev": rev2,
5175
- "truncate": truncate2,
5176
- "join": join2,
5177
- "int": int2,
5178
- "float": float2,
5179
- "round": round2,
5180
- "floor": floor2,
5181
- "ceil": ceil2,
5182
- "percent": percent2,
5183
- "unit": unit2,
5184
- "date": date2,
5185
- "time": time2,
5186
- "datetime": datetime2,
5187
- "ymd": ymd2,
5188
- "hms": hms2,
5189
- "falsy": falsy2,
5190
- "truthy": truthy2,
5191
- "defaults": defaults2,
5192
- "boolean": boolean2,
5193
- "number": number2,
5194
- "string": string2,
5195
- "null": _null2
5196
- };
5197
- var outputBuiltinFilters2 = builtinFilters2;
5198
- var inputBuiltinFilters = builtinFilters2;
5199
- var builtinFiltersByFilterIOType = {
5200
- "input": inputBuiltinFilters,
5201
- "output": outputBuiltinFilters2
5202
- };
5203
- var builtinFilterFn = (name, options) => (filters) => {
5204
- const filter = filters[name];
5205
- if (!filter) {
5206
- raiseError2(`[wcs/filter-unknown] filter not found: ${name}.${didYouMean(name, Object.keys(filters))}${LINT_HINT}`);
5207
- }
5208
- return filter(options);
5209
- };
5210
4517
  function finalizeArg(text, firstQuoteStart, lastQuoteEnd) {
5211
4518
  const startLimit = firstQuoteStart === -1 ? text.length : firstQuoteStart;
5212
4519
  let start = 0;
@@ -5220,15 +4527,30 @@ function finalizeArg(text, firstQuoteStart, lastQuoteEnd) {
5220
4527
  }
5221
4528
  return text.slice(start, end);
5222
4529
  }
5223
- function parseFilterArgs(argsText) {
4530
+ var NUMBER_LITERAL = /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
4531
+ function toLiteral(text, quoted) {
4532
+ if (quoted)
4533
+ return text;
4534
+ if (text === "true")
4535
+ return true;
4536
+ if (text === "false")
4537
+ return false;
4538
+ if (text === "null")
4539
+ return null;
4540
+ return NUMBER_LITERAL.test(text) ? Number(text) : text;
4541
+ }
4542
+ function parseFilterArgsWithLiterals(argsText) {
5224
4543
  const args = [];
4544
+ const literals = [];
5225
4545
  let current2 = "";
5226
4546
  let inQuote = null;
5227
4547
  let hasQuote = false;
5228
4548
  let firstQuoteStart = -1;
5229
4549
  let lastQuoteEnd = -1;
5230
4550
  const flush = () => {
5231
- args.push(finalizeArg(current2, firstQuoteStart, lastQuoteEnd));
4551
+ const arg = finalizeArg(current2, firstQuoteStart, lastQuoteEnd);
4552
+ args.push(arg);
4553
+ literals.push(toLiteral(arg, hasQuote));
5232
4554
  current2 = "";
5233
4555
  hasQuote = false;
5234
4556
  firstQuoteStart = -1;
@@ -5255,19 +4577,21 @@ function parseFilterArgs(argsText) {
5255
4577
  current2 += char;
5256
4578
  }
5257
4579
  }
4580
+ if (inQuote !== null) {
4581
+ raiseError2(`[wcs/binding-syntax] unterminated ${inQuote} quote in the filter arguments "(${argsText})". Close the quote.${LINT_HINT}`);
4582
+ }
5258
4583
  const last = finalizeArg(current2, firstQuoteStart, lastQuoteEnd);
5259
4584
  if (last || hasQuote) {
5260
4585
  args.push(last);
4586
+ literals.push(toLiteral(last, hasQuote));
5261
4587
  }
5262
- return args;
4588
+ return { args, literals };
5263
4589
  }
5264
- var filterFnByKey = /* @__PURE__ */ new Map();
5265
4590
  function clearFilterFnCacheForTooling() {
5266
- filterFnByKey.clear();
4591
+ clearFilterResolutionCache();
5267
4592
  }
5268
- function parseFilters(filterTextList, filterIOType) {
5269
- const builtinFilters3 = builtinFiltersByFilterIOType[filterIOType];
5270
- const filters = filterTextList.map((filterText) => {
4593
+ function parseFilters(filterTextList, _filterIOType) {
4594
+ return filterTextList.map((filterText) => {
5271
4595
  const openParenIndex = filterText.indexOf("(");
5272
4596
  const closeParenIndex = filterText.lastIndexOf(")");
5273
4597
  if (openParenIndex !== -1 && closeParenIndex === -1) {
@@ -5276,45 +4600,59 @@ function parseFilters(filterTextList, filterIOType) {
5276
4600
  if (closeParenIndex !== -1 && openParenIndex === -1) {
5277
4601
  raiseError2(`Invalid filter format: missing opening parenthesis in "${filterText}"`);
5278
4602
  }
4603
+ const filterName = (openParenIndex === -1 ? filterText : filterText.substring(0, openParenIndex)).trim();
4604
+ if (filterName.length === 0) {
4605
+ raiseError2(`[wcs/binding-syntax] an empty filter in "${filterTextList.join("|")}" \u2014 remove the extra "|" or name the filter.${LINT_HINT}`);
4606
+ }
5279
4607
  if (openParenIndex === -1) {
5280
- const filterName = filterText.trim();
5281
- const filterKey = `${filterName}():${filterIOType}`;
5282
- let filterFn = filterFnByKey.get(filterKey);
5283
- if (typeof filterFn === "undefined") {
5284
- filterFn = builtinFilterFn(filterName, [])(builtinFilters3);
5285
- filterFnByKey.set(filterKey, filterFn);
5286
- }
5287
- return {
5288
- filterName,
5289
- args: [],
5290
- filterFn
5291
- };
5292
- } else {
5293
- const argsText = filterText.substring(openParenIndex + 1, closeParenIndex);
5294
- const filterName = filterText.substring(0, openParenIndex).trim();
5295
- const args = parseFilterArgs(argsText);
5296
- const filterKey = `${filterName}(${args.join(",")}):${filterIOType}`;
5297
- let filterFn = filterFnByKey.get(filterKey);
5298
- if (typeof filterFn === "undefined") {
5299
- filterFn = builtinFilterFn(filterName, args)(builtinFilters3);
5300
- filterFnByKey.set(filterKey, filterFn);
5301
- }
5302
- return {
5303
- filterName,
5304
- args,
5305
- filterFn
5306
- };
4608
+ return { filterName, args: [], literals: [] };
5307
4609
  }
4610
+ const argsText = filterText.substring(openParenIndex + 1, closeParenIndex);
4611
+ return { filterName, ...parseFilterArgsWithLiterals(argsText) };
5308
4612
  });
5309
- return filters;
5310
4613
  }
5311
4614
  var trimFn = (s) => s.trim();
4615
+ var isQuote = (c) => c === "'" || c === '"';
4616
+ function indexOfOutsideQuotes(text, char) {
4617
+ let quote = null;
4618
+ for (let i = 0; i < text.length; i++) {
4619
+ const c = text[i];
4620
+ if (quote !== null) {
4621
+ if (c === quote)
4622
+ quote = null;
4623
+ } else if (isQuote(c)) {
4624
+ quote = c;
4625
+ } else if (c === char) {
4626
+ return i;
4627
+ }
4628
+ }
4629
+ return -1;
4630
+ }
4631
+ function splitOutsideQuotes(text, separator) {
4632
+ const parts = [];
4633
+ let quote = null;
4634
+ let start = 0;
4635
+ for (let i = 0; i < text.length; i++) {
4636
+ const c = text[i];
4637
+ if (quote !== null) {
4638
+ if (c === quote)
4639
+ quote = null;
4640
+ } else if (isQuote(c)) {
4641
+ quote = c;
4642
+ } else if (c === separator) {
4643
+ parts.push(text.slice(start, i));
4644
+ start = i + 1;
4645
+ }
4646
+ }
4647
+ parts.push(text.slice(start));
4648
+ return parts;
4649
+ }
5312
4650
  var cacheFilterInfos$1 = /* @__PURE__ */ new Map();
5313
4651
  function clearPropPartCacheForTooling() {
5314
4652
  cacheFilterInfos$1.clear();
5315
4653
  }
5316
4654
  function parsePropPart(propPart) {
5317
- const pos = propPart.indexOf(FILTER_SEPARATOR2);
4655
+ const pos = indexOfOutsideQuotes(propPart, FILTER_SEPARATOR2);
5318
4656
  let propText = "";
5319
4657
  let filterTexts = [];
5320
4658
  let filtersText = "";
@@ -5325,14 +4663,18 @@ function parsePropPart(propPart) {
5325
4663
  if (cacheFilterInfos$1.has(filtersText)) {
5326
4664
  filters = cacheFilterInfos$1.get(filtersText);
5327
4665
  } else {
5328
- filterTexts = filtersText.split(FILTER_SEPARATOR2).map(trimFn);
5329
- filters = parseFilters(filterTexts, "input");
4666
+ filterTexts = splitOutsideQuotes(filtersText, FILTER_SEPARATOR2).map(trimFn);
4667
+ filters = parseFilters(filterTexts);
5330
4668
  cacheFilterInfos$1.set(filtersText, filters);
5331
4669
  }
5332
4670
  } else {
5333
4671
  propText = propPart.trim();
5334
4672
  }
5335
- const [propName, propModifiersText] = propText.split(MODIFIER_SEPARATOR2).map(trimFn);
4673
+ const modifierParts = propText.split(MODIFIER_SEPARATOR2).map(trimFn);
4674
+ if (modifierParts.length > 2) {
4675
+ raiseError2(`[wcs/binding-syntax] "${propText}": a binding takes one modifier list after a single "${MODIFIER_SEPARATOR2}" \u2014 write "${modifierParts[0]}${MODIFIER_SEPARATOR2}${modifierParts.slice(1).join(",")}".${LINT_HINT}`);
4676
+ }
4677
+ const [propName, propModifiersText] = modifierParts;
5336
4678
  const propSegments = propName.split(DELIMITER2).map(trimFn);
5337
4679
  const propModifiers = propModifiersText ? propModifiersText.split(",").map(trimFn) : [];
5338
4680
  return {
@@ -5347,7 +4689,7 @@ function clearStatePartCacheForTooling() {
5347
4689
  cacheFilterInfos.clear();
5348
4690
  }
5349
4691
  function parseStatePart(statePart) {
5350
- const pos = statePart.indexOf(FILTER_SEPARATOR2);
4692
+ const pos = indexOfOutsideQuotes(statePart, FILTER_SEPARATOR2);
5351
4693
  let stateAndPath = "";
5352
4694
  let filterTexts = [];
5353
4695
  let filtersText = "";
@@ -5358,8 +4700,8 @@ function parseStatePart(statePart) {
5358
4700
  if (cacheFilterInfos.has(filtersText)) {
5359
4701
  filters = cacheFilterInfos.get(filtersText);
5360
4702
  } else {
5361
- filterTexts = filtersText.split(FILTER_SEPARATOR2).map(trimFn);
5362
- filters = parseFilters(filterTexts, "output");
4703
+ filterTexts = splitOutsideQuotes(filtersText, FILTER_SEPARATOR2).map(trimFn);
4704
+ filters = parseFilters(filterTexts);
5363
4705
  cacheFilterInfos.set(filtersText, filters);
5364
4706
  }
5365
4707
  } else {
@@ -5376,8 +4718,19 @@ function parseStatePart(statePart) {
5376
4718
  outFilters: filters
5377
4719
  };
5378
4720
  }
4721
+ var KEYWORDS_WITHOUT_MODIFIERS = /* @__PURE__ */ new Set([ELSE_KEYWORD2, "if", "elseif", "for", SPREAD_PROP2]);
4722
+ var EXPLICIT_PROPERTY_REJECTED_HEADS = /* @__PURE__ */ new Set([
4723
+ CLASS_NAMESPACE2,
4724
+ ATTR_NAMESPACE2,
4725
+ STYLE_NAMESPACE2,
4726
+ COMMAND_NAMESPACE2,
4727
+ EVENT_TOKEN_NAMESPACE2
4728
+ ]);
4729
+ function splitBindTexts(bindText) {
4730
+ return splitOutsideQuotes(bindText, BINDING_SEPARATOR2);
4731
+ }
5379
4732
  function parseBindTextsForElement(bindText) {
5380
- const [...bindTexts] = bindText.split(BINDING_SEPARATOR2).map(trimFn).filter((s) => s.length > 0);
4733
+ const [...bindTexts] = splitBindTexts(bindText).map(trimFn).filter((s) => s.length > 0);
5381
4734
  const results = bindTexts.map((bindText2) => {
5382
4735
  const separatorIndex = bindText2.indexOf(PROP_VALUE_SEPARATOR2);
5383
4736
  if (separatorIndex === -1) {
@@ -5385,7 +4738,14 @@ function parseBindTextsForElement(bindText) {
5385
4738
  }
5386
4739
  const propPart = bindText2.slice(0, separatorIndex).trim();
5387
4740
  const statePart = bindText2.slice(separatorIndex + 1).trim();
4741
+ const keyword = propPart.split(MODIFIER_SEPARATOR2)[0].split(FILTER_SEPARATOR2)[0].trim();
4742
+ if (keyword !== propPart && KEYWORDS_WITHOUT_MODIFIERS.has(keyword)) {
4743
+ raiseError2(`[wcs/binding-syntax] "${bindText2}": "${keyword}" takes no modifiers or filters on its left side \u2014 write "${keyword}:".${LINT_HINT}`);
4744
+ }
5388
4745
  if (propPart === ELSE_KEYWORD2) {
4746
+ if (statePart.length > 0) {
4747
+ raiseError2(`[wcs/binding-syntax] "${bindText2}": "else" takes no value \u2014 write "else:".${LINT_HINT}`);
4748
+ }
5389
4749
  const pathInfo = getPathInfo("#else");
5390
4750
  return {
5391
4751
  propName: ELSE_KEYWORD2,
@@ -5413,7 +4773,7 @@ function parseBindTextsForElement(bindText) {
5413
4773
  ...stateResult,
5414
4774
  bindingType: "spread"
5415
4775
  };
5416
- } else if (propPart === "if" || propPart === "elseif" || propPart === "for" || propPart === "radio" || propPart === "checkbox") {
4776
+ } else if (propPart === "if" || propPart === "elseif" || propPart === "for") {
5417
4777
  const stateResult = parseStatePart(statePart);
5418
4778
  return {
5419
4779
  propName: propPart,
@@ -5423,9 +4783,30 @@ function parseBindTextsForElement(bindText) {
5423
4783
  ...stateResult,
5424
4784
  bindingType: propPart
5425
4785
  };
4786
+ } else if (keyword === "radio" || keyword === "checkbox") {
4787
+ const stateResult = parseStatePart(statePart);
4788
+ const propResult = parsePropPart(propPart);
4789
+ return {
4790
+ ...propResult,
4791
+ ...stateResult,
4792
+ bindingType: keyword
4793
+ };
5426
4794
  } else {
5427
4795
  const stateResult = parseStatePart(statePart);
5428
4796
  const propResult = parsePropPart(propPart);
4797
+ if (propResult.propSegments[0] === "" && propResult.propSegments.length > 1) {
4798
+ const propSegments = propResult.propSegments.slice(1);
4799
+ if (propSegments.includes("") || EXPLICIT_PROPERTY_REJECTED_HEADS.has(propSegments[0])) {
4800
+ raiseError2(`[wcs/binding-syntax] "${propPart}": a leading "." binds an element property by name \u2014 write a non-empty property that is not a namespace (${[...EXPLICIT_PROPERTY_REJECTED_HEADS].join(", ")}).${LINT_HINT}`);
4801
+ }
4802
+ return {
4803
+ ...propResult,
4804
+ propName: propSegments.join(DELIMITER2),
4805
+ propSegments,
4806
+ ...stateResult,
4807
+ bindingType: "prop"
4808
+ };
4809
+ }
5429
4810
  if (propResult.propSegments[0] === EVENT_TOKEN_NAMESPACE2) {
5430
4811
  return {
5431
4812
  ...propResult,
@@ -5508,7 +4889,7 @@ function parseEmbeddedTextWithPositions(expression) {
5508
4889
  }
5509
4890
  function parseBindTextWithPositions(bindText) {
5510
4891
  const results = [];
5511
- const segments = bindText.split(delimiters.binding);
4892
+ const segments = splitBindTexts(bindText);
5512
4893
  let segmentStart = 0;
5513
4894
  for (const segment of segments) {
5514
4895
  const leading = segment.length - segment.trimStart().length;
@@ -5551,24 +4932,42 @@ function parseBindTextWithPositions(bindText) {
5551
4932
  return results;
5552
4933
  }
5553
4934
 
5554
- // src/service/v3MigrationValidator.ts
5555
- function validateV3Migration(html, bindAttrName = "data-wcs", locale3) {
4935
+ // src/service/bindingSyntaxValidator.ts
4936
+ var CODE_MARKER = "[wcs/binding-syntax]";
4937
+ function bindingSyntaxDetail(error) {
4938
+ if (error === null) return null;
4939
+ const at2 = error.indexOf(CODE_MARKER);
4940
+ if (at2 === -1) return null;
4941
+ const detail = error.slice(at2 + CODE_MARKER.length).trim();
4942
+ const hint = detail.indexOf(" Validate statically:");
4943
+ return hint === -1 ? detail : detail.slice(0, hint);
4944
+ }
4945
+ function validateBindingSyntax(html, bindAttrName = "data-wcs", locale2) {
5556
4946
  const diagnostics = [];
5557
- const msgs = getMessages(locale3);
5558
- const push2 = (detail, start, end) => {
5559
- diagnostics.push({ code: WcsDiagnosticCode.V3Migration, start, end, message: msgs.v3Migration(detail), severity: "info" });
5560
- };
4947
+ const msgs = getMessages(locale2);
5561
4948
  for (const attr of findAllBindAttributes(html, bindAttrName)) {
5562
4949
  for (const binding of parseBindTextWithPositions(attr.value)) {
5563
- for (const detail of findV3MigrationIssues(binding.exprText)) {
5564
- push2(detail, attr.valueStart + binding.exprRange.start, attr.valueStart + binding.exprRange.end);
5565
- }
4950
+ const detail = bindingSyntaxDetail(binding.error);
4951
+ if (detail === null) continue;
4952
+ diagnostics.push({
4953
+ code: WcsDiagnosticCode.BindingSyntax,
4954
+ start: attr.valueStart + binding.exprRange.start,
4955
+ end: attr.valueStart + binding.exprRange.end,
4956
+ message: msgs.bindingSyntax(detail),
4957
+ severity: "error"
4958
+ });
5566
4959
  }
5567
4960
  }
5568
- for (const item of [...findAllMustacheSyntax(html), ...findAllCommentBindings(html)]) {
5569
- for (const detail of findEmbeddedV3MigrationIssues(item.expression)) {
5570
- push2(detail, item.exprStart, item.exprEnd);
5571
- }
4961
+ for (const mustache of findAllMustacheSyntax(html)) {
4962
+ const detail = bindingSyntaxDetail(parseEmbeddedTextWithPositions(mustache.expression).error);
4963
+ if (detail === null) continue;
4964
+ diagnostics.push({
4965
+ code: WcsDiagnosticCode.BindingSyntax,
4966
+ start: mustache.exprStart,
4967
+ end: mustache.exprStart + mustache.expression.length,
4968
+ message: msgs.bindingSyntax(detail),
4969
+ severity: "error"
4970
+ });
5572
4971
  }
5573
4972
  return diagnostics;
5574
4973
  }
@@ -6861,9 +6260,9 @@ var DOM_COMMON_PROPERTIES = /* @__PURE__ */ new Set([
6861
6260
  ]);
6862
6261
  var STRUCTURAL_DIRECTIVES2 = /* @__PURE__ */ new Set(["for", "if", "elseif", "else"]);
6863
6262
  var EMPTYISH_SEEDS = /* @__PURE__ */ new Set(["''", '""', "``", "null", "[]", "{}"]);
6864
- function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale3, fileReader) {
6263
+ function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale2, fileReader) {
6865
6264
  const diagnostics = [];
6866
- const msgs = getMessages(locale3);
6265
+ const msgs = getMessages(locale2);
6867
6266
  const occurrences = findBuiltinTagOccurrences(html);
6868
6267
  if (occurrences.length === 0) return diagnostics;
6869
6268
  let statePaths = null;
@@ -6923,10 +6322,28 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
6923
6322
  const hashIndex = property.indexOf("#");
6924
6323
  const modifiers = hashIndex === -1 ? "" : property.slice(hashIndex + 1);
6925
6324
  property = hashIndex === -1 ? property : property.slice(0, hashIndex);
6325
+ const explicit = property.startsWith(".") && property !== "...";
6326
+ if (explicit) {
6327
+ property = property.slice(1);
6328
+ if (/^(class|style|attr|command|eventToken)(\.|$)/.test(property)) return;
6329
+ }
6926
6330
  if (property === "...") return;
6927
6331
  if (STRUCTURAL_DIRECTIVES2.has(property)) return;
6928
6332
  if (/^(class|style|attr)\./.test(property)) return;
6929
- if (/^on\w/.test(property)) return;
6333
+ if (!explicit && /^on\w/.test(property)) {
6334
+ if (contract.properties.includes(property) || property in contract.inputs) {
6335
+ diagnostics.push({
6336
+ code: WcsDiagnosticCode.OnPrefixedMember,
6337
+ start,
6338
+ end,
6339
+ severity: "warning",
6340
+ tag: tagName,
6341
+ member: property,
6342
+ message: msgs.onPrefixedMember(property, tagName)
6343
+ });
6344
+ }
6345
+ return;
6346
+ }
6930
6347
  const inputNames = Object.keys(contract.inputs);
6931
6348
  if (property.startsWith("command.")) {
6932
6349
  const name = property.slice("command.".length);
@@ -7011,7 +6428,7 @@ function suggestion(input, candidates, msgs) {
7011
6428
  let best = null;
7012
6429
  let bestDistance = 3;
7013
6430
  for (const c of candidates) {
7014
- const d = editDistance2(input.toLowerCase(), c.toLowerCase(), bestDistance);
6431
+ const d = editDistance(input.toLowerCase(), c.toLowerCase(), bestDistance);
7015
6432
  if (d < bestDistance) {
7016
6433
  best = c;
7017
6434
  bestDistance = d;
@@ -7019,7 +6436,7 @@ function suggestion(input, candidates, msgs) {
7019
6436
  }
7020
6437
  return best !== null ? msgs.didYouMean(best) : "";
7021
6438
  }
7022
- function editDistance2(a, b, bound) {
6439
+ function editDistance(a, b, bound) {
7023
6440
  if (Math.abs(a.length - b.length) >= bound) return bound;
7024
6441
  const prev = new Array(b.length + 1);
7025
6442
  const curr = new Array(b.length + 1);
@@ -7133,9 +6550,9 @@ var ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
7133
6550
  "aria-brailleroledescription",
7134
6551
  "aria-description"
7135
6552
  ]);
7136
- function validateAriaAttributes(html, bindAttribute = "data-wcs", locale3) {
6553
+ function validateAriaAttributes(html, bindAttribute = "data-wcs", locale2) {
7137
6554
  const diagnostics = [];
7138
- const msgs = getMessages(locale3);
6555
+ const msgs = getMessages(locale2);
7139
6556
  for (const attr of findAllBindAttributes(html, bindAttribute)) {
7140
6557
  let exprOffset = 0;
7141
6558
  for (const expr of splitBindingExpressions(attr.value)) {
@@ -7164,9 +6581,9 @@ function validateAriaAttributes(html, bindAttribute = "data-wcs", locale3) {
7164
6581
  }
7165
6582
 
7166
6583
  // src/service/documentEnvValidator.ts
7167
- function validateDocumentEnv(html, locale3) {
6584
+ function validateDocumentEnv(html, locale2) {
7168
6585
  const diagnostics = [];
7169
- const msgs = getMessages(locale3);
6586
+ const msgs = getMessages(locale2);
7170
6587
  const scanText = blankHtmlComments(html);
7171
6588
  const autos = findWcstackAutoScripts(scanText);
7172
6589
  const stateIndex = autos.findIndex((a) => a.pkg === "state");
@@ -7277,8 +6694,8 @@ function blankJsComments(code) {
7277
6694
 
7278
6695
  // src/service/watchDeclarationValidator.ts
7279
6696
  var STATE_NAME_SEPARATOR = "@";
7280
- function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
7281
- const msgs = getMessages(locale3);
6697
+ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale2) {
6698
+ const msgs = getMessages(locale2);
7282
6699
  const out = [];
7283
6700
  for (const block of parseWcsScriptBlocks(html, stateTagName)) {
7284
6701
  const nonObject = findNonObjectWatch(block.content);
@@ -7342,8 +6759,8 @@ function validateEntry(entry, pathSet, paths, msgs) {
7342
6759
  }
7343
6760
 
7344
6761
  // src/service/scanDeclarationValidator.ts
7345
- function validateScanDeclarations(html, stateTagName = "wcs-state", locale3) {
7346
- const msgs = getMessages(locale3);
6762
+ function validateScanDeclarations(html, stateTagName = "wcs-state", locale2) {
6763
+ const msgs = getMessages(locale2);
7347
6764
  const out = [];
7348
6765
  for (const element of parseWcsStateElements(html, stateTagName)) {
7349
6766
  const mounted = element.bindComponent;
@@ -7701,8 +7118,8 @@ var UNSUPPORTED_API_SITE = {
7701
7118
  };
7702
7119
  var BRACKET_ASSIGNMENT = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
7703
7120
  var PRE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
7704
- function validateRecursion(html, stateTagName = "wcs-state", locale3) {
7705
- const msgs = getMessages(locale3);
7121
+ function validateRecursion(html, stateTagName = "wcs-state", locale2) {
7122
+ const msgs = getMessages(locale2);
7706
7123
  const out = [];
7707
7124
  for (const element of parseWcsStateElements(html, stateTagName)) {
7708
7125
  const mounted = element.bindComponent;
@@ -8133,8 +7550,8 @@ function findStateSelector(expr, embedded = false) {
8133
7550
  const nameStart = at2 + 1 + (raw.length - raw.trimStart().length);
8134
7551
  return { start: at2, end: name.length === 0 ? at2 + 1 : nameStart + name.length, name: name.length === 0 ? "default" : name };
8135
7552
  }
8136
- function validateNamedState(html, attrName, stateTagName = "wcs-state", locale3) {
8137
- const msgs = getMessages(locale3);
7553
+ function validateNamedState(html, attrName, stateTagName = "wcs-state", locale2) {
7554
+ const msgs = getMessages(locale2);
8138
7555
  const diagnostics = [];
8139
7556
  for (const element of parseWcsStateElements(html, stateTagName)) {
8140
7557
  const tagText = html.slice(element.tagStart, element.tagEnd);
@@ -8192,8 +7609,8 @@ function findMountPathProblem(mountPath) {
8192
7609
  }
8193
7610
  return null;
8194
7611
  }
8195
- function validateMountAttributes(html, stateTagName = "wcs-state", locale3) {
8196
- const msgs = getMessages(locale3);
7612
+ function validateMountAttributes(html, stateTagName = "wcs-state", locale2) {
7613
+ const msgs = getMessages(locale2);
8197
7614
  const diagnostics = [];
8198
7615
  for (const element of parseWcsStateElements(html, stateTagName)) {
8199
7616
  if (element.mountPath === null) continue;
@@ -14336,8 +13753,8 @@ function buildReferenceIndex(html, options = {}) {
14336
13753
  // src/service/semanticValidator.ts
14337
13754
  var STATE_UPDATED_CALLBACK = "$updatedCallback";
14338
13755
  var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
14339
- function validateIndexArity(script, scriptStart, locale3) {
14340
- const msgs = getMessages(locale3);
13756
+ function validateIndexArity(script, scriptStart, locale2) {
13757
+ const msgs = getMessages(locale2);
14341
13758
  const out = [];
14342
13759
  API_CALL.lastIndex = 0;
14343
13760
  let match;
@@ -14368,8 +13785,8 @@ function validateIndexArity(script, scriptStart, locale3) {
14368
13785
  }
14369
13786
  return out;
14370
13787
  }
14371
- function validateGetterCycles(script, scriptStart, locale3) {
14372
- const msgs = getMessages(locale3);
13788
+ function validateGetterCycles(script, scriptStart, locale2) {
13789
+ const msgs = getMessages(locale2);
14373
13790
  const getters = analyzeCallableBodies(script).filter((entry) => entry.kind === "getter");
14374
13791
  if (getters.length === 0) return [];
14375
13792
  const declared = new Set(getters.map((getter) => getter.name));
@@ -14424,7 +13841,7 @@ function validateGetterCycles(script, scriptStart, locale3) {
14424
13841
  }
14425
13842
  return out;
14426
13843
  }
14427
- function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, locale3) {
13844
+ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, locale2) {
14428
13845
  const getters = analyzeCallableBodies(script).filter((entry) => entry.kind === "getter" && entry.accessor === "get");
14429
13846
  if (getters.length === 0) return [];
14430
13847
  const objectRoots = /* @__PURE__ */ new Set();
@@ -14434,7 +13851,7 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
14434
13851
  }
14435
13852
  }
14436
13853
  if (objectRoots.size === 0) return [];
14437
- const msgs = getMessages(locale3);
13854
+ const msgs = getMessages(locale2);
14438
13855
  const out = [];
14439
13856
  for (const getter of getters) {
14440
13857
  for (const read of collectGetterReads(getter.body) ?? []) {
@@ -14513,12 +13930,12 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
14513
13930
  return roots;
14514
13931
  }
14515
13932
  var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
14516
- function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3) {
13933
+ function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2) {
14517
13934
  const blocks = parseWcsScriptBlocks(html, stateTagName);
14518
13935
  if (blocks.length === 0) return [];
14519
13936
  const hasCallback = blocks.some((block) => block.content.includes(STATE_UPDATED_CALLBACK));
14520
13937
  if (!hasCallback) return [];
14521
- const msgs = getMessages(locale3);
13938
+ const msgs = getMessages(locale2);
14522
13939
  const boundPaths = collectBoundPaths(html, stateTagName, bindAttrName);
14523
13940
  const out = [];
14524
13941
  for (const block of blocks) {
@@ -14562,17 +13979,17 @@ function collectBoundPaths(html, stateTagName, bindAttrName) {
14562
13979
  }
14563
13980
  return bound;
14564
13981
  }
14565
- function validateSemantics(html, stateTagName = "wcs-state", locale3, bindAttrName = "data-wcs") {
13982
+ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrName = "data-wcs") {
14566
13983
  const out = [];
14567
13984
  const blocks = parseWcsScriptBlocks(html, stateTagName);
14568
13985
  let nestedWriteRoots = null;
14569
13986
  const getNestedWriteRoots = () => nestedWriteRoots ??= collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks);
14570
13987
  for (const block of blocks) {
14571
- out.push(...validateIndexArity(block.content, block.contentStart, locale3));
14572
- out.push(...validateGetterCycles(block.content, block.contentStart, locale3));
14573
- out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale3));
13988
+ out.push(...validateIndexArity(block.content, block.contentStart, locale2));
13989
+ out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
13990
+ out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
14574
13991
  }
14575
- out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3));
13992
+ out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2));
14576
13993
  return out;
14577
13994
  }
14578
13995
 
@@ -14604,27 +14021,27 @@ function applicationSchemaOf(loaded) {
14604
14021
  function validateDocument(text, options = {}) {
14605
14022
  const bindAttribute = options.bindAttribute ?? "data-wcs";
14606
14023
  const stateTagName = options.stateTagName ?? "wcs-state";
14607
- const locale3 = options.locale;
14024
+ const locale2 = options.locale;
14608
14025
  const fileReader = options.fileReader;
14609
14026
  const applicationSchema = options.applicationSchema ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.schema : void 0);
14610
14027
  const out = [];
14611
- out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader, applicationSchema));
14612
- out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader, applicationSchema));
14613
- out.push(...validateV3Migration(text, bindAttribute, locale3));
14614
- out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
14615
- out.push(...validateAriaAttributes(text, bindAttribute, locale3));
14616
- out.push(...validateDocumentEnv(text, locale3));
14617
- out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
14618
- out.push(...validateArrayMutations(text, stateTagName, locale3));
14619
- out.push(...validateWatchDeclarations(text, stateTagName, locale3));
14620
- out.push(...validateScanDeclarations(text, stateTagName, locale3));
14621
- out.push(...validateRecursion(text, stateTagName, locale3));
14622
- out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
14623
- out.push(...validateMountAttributes(text, stateTagName, locale3));
14624
- for (const d of validateStateTypes(text, stateTagName, locale3)) {
14028
+ out.push(...validateBindings(text, bindAttribute, stateTagName, locale2, fileReader, applicationSchema));
14029
+ out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale2, fileReader, applicationSchema));
14030
+ out.push(...validateBindingSyntax(text, bindAttribute, locale2));
14031
+ out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale2, fileReader));
14032
+ out.push(...validateAriaAttributes(text, bindAttribute, locale2));
14033
+ out.push(...validateDocumentEnv(text, locale2));
14034
+ out.push(...validateSemantics(text, stateTagName, locale2, bindAttribute));
14035
+ out.push(...validateArrayMutations(text, stateTagName, locale2));
14036
+ out.push(...validateWatchDeclarations(text, stateTagName, locale2));
14037
+ out.push(...validateScanDeclarations(text, stateTagName, locale2));
14038
+ out.push(...validateRecursion(text, stateTagName, locale2));
14039
+ out.push(...validateNamedState(text, bindAttribute, stateTagName, locale2));
14040
+ out.push(...validateMountAttributes(text, stateTagName, locale2));
14041
+ for (const d of validateStateTypes(text, stateTagName, locale2)) {
14625
14042
  out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
14626
14043
  }
14627
- for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
14044
+ for (const d of validateNestedAssigns(text, stateTagName, locale2)) {
14628
14045
  out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
14629
14046
  }
14630
14047
  return sortDiagnostics(out);