@wcstack/lint 1.30.0 → 1.32.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.
Files changed (2) hide show
  1. package/dist/cli.cjs +2434 -73
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
16
16
  }
17
17
  return to;
18
18
  };
19
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __toCommonJS = (mod3) => __copyProps(__defProp({}, "__esModule", { value: true }), mod3);
20
20
 
21
21
  // src/cli.ts
22
22
  var cli_exports = {};
@@ -27,8 +27,34 @@ __export(cli_exports, {
27
27
  resolveCliLocale: () => resolveCliLocale
28
28
  });
29
29
  module.exports = __toCommonJS(cli_exports);
30
+ var import_node_fs2 = require("node:fs");
31
+
32
+ // src/fileReader.ts
30
33
  var import_node_fs = require("node:fs");
31
34
  var import_node_path = require("node:path");
35
+ function createFileReader(htmlPath, read = (p) => (0, import_node_fs.readFileSync)(p, "utf8")) {
36
+ const base = (0, import_node_path.dirname)(htmlPath);
37
+ const cache = /* @__PURE__ */ new Map();
38
+ return (relativePath) => {
39
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(relativePath) || relativePath.startsWith("/")) {
40
+ return void 0;
41
+ }
42
+ if (cache.has(relativePath)) {
43
+ return cache.get(relativePath);
44
+ }
45
+ let content;
46
+ try {
47
+ content = read((0, import_node_path.resolve)(base, relativePath));
48
+ if (content.charCodeAt(0) === 65279) {
49
+ content = content.slice(1);
50
+ }
51
+ } catch {
52
+ content = void 0;
53
+ }
54
+ cache.set(relativePath, content);
55
+ return content;
56
+ };
57
+ }
32
58
 
33
59
  // src/core/offsetToPosition.ts
34
60
  function createPositionMapper(text) {
@@ -91,6 +117,20 @@ var WcsDiagnosticCode = {
91
117
  TokenUndeclared: "wcs/token-undeclared",
92
118
  TokenMisconfigured: "wcs/token-misconfigured",
93
119
  NestedAssign: "wcs/nested-assign",
120
+ // --- 意味論(構文・存在検査では捕まらない取り違え。service/semanticValidator.ts) ---
121
+ // `$getAll` / `$setAll` / `$resolve` の添字の本数がパスの `*` の本数と噛み合わない。
122
+ // ランタイムは同じ code で raiseError する(超過は以前は黙って無視されていた)。
123
+ IndexArity: "wcs/index-arity",
124
+ // ワイルドカードの階数がスコープの段数を超える(`matrix.*.*` を 1 段の for で読む、
125
+ // `$2` を 1 段のループで読む)。既存の「for の外」検査の深さ方向の一般化。
126
+ WildcardRank: "wcs/wildcard-rank",
127
+ // パス getter どうしの循環参照。ランタイムはアドレススタック上限まで再帰してから落ちる。
128
+ GetterCycle: "wcs/getter-cycle",
129
+ // `$updatedCallback` が、どのバインディングにも現れないパスを判定に使っている。
130
+ // 同コールバックは **binding 駆動**(live binding が適用された path しか報告しない)
131
+ // なので、その分岐は一度も実行されない。表示要素が購読の実体になる事故
132
+ // (examples/state-intersect-scroll の README に記録)の静的検出。
133
+ UpdatedCallbackUnbound: "wcs/updated-callback-unbound",
94
134
  // --- <wcs-state> script: $watch declaration ---
95
135
  // ランタイム(watch/processWatchDeclaration.ts)が raiseError で落とす宣言。
96
136
  // 越境 `@` / `$` 始まり / 空キー・空セグメント / 明らかな非関数ハンドラ。
@@ -117,6 +157,11 @@ var WcsDiagnosticCode = {
117
157
  TriggerSeededTruthy: "wcs/trigger-seeded-truthy",
118
158
  // 非 manual <wcs-storage> value バインド先の空値シード(初期書き戻しが保存値を上書き)。
119
159
  StorageSeedClobber: "wcs/storage-seed-clobber",
160
+ // --- accessibility (docs/a11y-design.md §8 / D9) ---
161
+ // `attr.aria-*` バインドの属性名が WAI-ARIA に存在しない(タイポ)。
162
+ // setAttribute はそのまま書き、支援技術は黙って無視する。severity は warning
163
+ // (error 昇格時は packages/lint/scripts/smoke-test.mjs の対ケース更新が必須)。
164
+ AriaAttrUnknown: "wcs/aria-attr-unknown",
120
165
  // --- document-level load configuration ---
121
166
  // @wcstack/state/auto より後に他 wcstack /auto が読まれている。
122
167
  ScriptOrder: "wcs/script-order",
@@ -131,6 +176,545 @@ function sortDiagnostics(diagnostics) {
131
176
  }
132
177
 
133
178
  // ../state/dist/manifest.esm.js
179
+ var _config = {
180
+ bindAttributeName: "data-wcs",
181
+ tagNames: {
182
+ state: "wcs-state"
183
+ },
184
+ locale: "en"
185
+ };
186
+ var config = _config;
187
+ function raiseError(message) {
188
+ throw new Error(`[@wcstack/state] ${message}`);
189
+ }
190
+ function optionsRequired(fnName) {
191
+ raiseError(`filter ${fnName} requires at least one option`);
192
+ }
193
+ function optionMustBeNumber(fnName) {
194
+ raiseError(`filter ${fnName} requires a number as option`);
195
+ }
196
+ function valueMustBeNumber(fnName) {
197
+ raiseError(`filter ${fnName} requires a number value`);
198
+ }
199
+ function valueMustBeBoolean(fnName) {
200
+ raiseError(`filter ${fnName} requires a boolean value`);
201
+ }
202
+ function valueMustBeDate(fnName) {
203
+ raiseError(`filter ${fnName} requires a date value`);
204
+ }
205
+ function valueMustBeArray(fnName) {
206
+ raiseError(`filter ${fnName} requires an array value`);
207
+ }
208
+ function validateNumberString(value) {
209
+ if (!value || isNaN(Number(value))) {
210
+ return false;
211
+ }
212
+ return true;
213
+ }
214
+ var eq = (options) => {
215
+ const opt = options?.[0] ?? optionsRequired("eq");
216
+ return (value) => {
217
+ if (typeof value === "number") {
218
+ if (!validateNumberString(opt)) {
219
+ optionMustBeNumber("eq");
220
+ }
221
+ return value === Number(opt);
222
+ }
223
+ if (typeof value === "string") {
224
+ return value === opt;
225
+ }
226
+ return value === opt;
227
+ };
228
+ };
229
+ var ne = (options) => {
230
+ const opt = options?.[0] ?? optionsRequired("ne");
231
+ return (value) => {
232
+ if (typeof value === "number") {
233
+ if (!validateNumberString(opt)) {
234
+ optionMustBeNumber("ne");
235
+ }
236
+ return value !== Number(opt);
237
+ }
238
+ if (typeof value === "string") {
239
+ return value !== opt;
240
+ }
241
+ return value !== opt;
242
+ };
243
+ };
244
+ var not = (_options) => {
245
+ return (value) => {
246
+ if (typeof value !== "boolean") {
247
+ valueMustBeBoolean("not");
248
+ }
249
+ return !value;
250
+ };
251
+ };
252
+ var lt = (options) => {
253
+ const opt = options?.[0] ?? optionsRequired("lt");
254
+ if (!validateNumberString(opt)) {
255
+ optionMustBeNumber("lt");
256
+ }
257
+ return (value) => {
258
+ if (typeof value !== "number") {
259
+ valueMustBeNumber("lt");
260
+ }
261
+ return value < Number(opt);
262
+ };
263
+ };
264
+ var le = (options) => {
265
+ const opt = options?.[0] ?? optionsRequired("le");
266
+ if (!validateNumberString(opt)) {
267
+ optionMustBeNumber("le");
268
+ }
269
+ return (value) => {
270
+ if (typeof value !== "number") {
271
+ valueMustBeNumber("le");
272
+ }
273
+ return value <= Number(opt);
274
+ };
275
+ };
276
+ var gt = (options) => {
277
+ const opt = options?.[0] ?? optionsRequired("gt");
278
+ if (!validateNumberString(opt)) {
279
+ optionMustBeNumber("gt");
280
+ }
281
+ return (value) => {
282
+ if (typeof value !== "number") {
283
+ valueMustBeNumber("gt");
284
+ }
285
+ return value > Number(opt);
286
+ };
287
+ };
288
+ var ge = (options) => {
289
+ const opt = options?.[0] ?? optionsRequired("ge");
290
+ if (!validateNumberString(opt)) {
291
+ optionMustBeNumber("ge");
292
+ }
293
+ return (value) => {
294
+ if (typeof value !== "number") {
295
+ valueMustBeNumber("ge");
296
+ }
297
+ return value >= Number(opt);
298
+ };
299
+ };
300
+ var inc = (options) => {
301
+ const opt = options?.[0] ?? optionsRequired("inc");
302
+ if (!validateNumberString(opt)) {
303
+ optionMustBeNumber("inc");
304
+ }
305
+ return (value) => {
306
+ if (typeof value !== "number") {
307
+ valueMustBeNumber("inc");
308
+ }
309
+ return value + Number(opt);
310
+ };
311
+ };
312
+ var dec = (options) => {
313
+ const opt = options?.[0] ?? optionsRequired("dec");
314
+ if (!validateNumberString(opt)) {
315
+ optionMustBeNumber("dec");
316
+ }
317
+ return (value) => {
318
+ if (typeof value !== "number") {
319
+ valueMustBeNumber("dec");
320
+ }
321
+ return value - Number(opt);
322
+ };
323
+ };
324
+ var mul = (options) => {
325
+ const opt = options?.[0] ?? optionsRequired("mul");
326
+ if (!validateNumberString(opt)) {
327
+ optionMustBeNumber("mul");
328
+ }
329
+ return (value) => {
330
+ if (typeof value !== "number") {
331
+ valueMustBeNumber("mul");
332
+ }
333
+ return value * Number(opt);
334
+ };
335
+ };
336
+ var div = (options) => {
337
+ const opt = options?.[0] ?? optionsRequired("div");
338
+ if (!validateNumberString(opt)) {
339
+ optionMustBeNumber("div");
340
+ }
341
+ return (value) => {
342
+ if (typeof value !== "number") {
343
+ valueMustBeNumber("div");
344
+ }
345
+ return value / Number(opt);
346
+ };
347
+ };
348
+ var mod = (options) => {
349
+ const opt = options?.[0] ?? optionsRequired("mod");
350
+ if (!validateNumberString(opt)) {
351
+ optionMustBeNumber("mod");
352
+ }
353
+ return (value) => {
354
+ if (typeof value !== "number") {
355
+ valueMustBeNumber("mod");
356
+ }
357
+ return value % Number(opt);
358
+ };
359
+ };
360
+ var abs = (_options) => {
361
+ return (value) => {
362
+ if (typeof value !== "number") {
363
+ valueMustBeNumber("abs");
364
+ }
365
+ return Math.abs(value);
366
+ };
367
+ };
368
+ var clamp = (options) => {
369
+ const opt1 = options?.[0] ?? optionsRequired("clamp");
370
+ if (!validateNumberString(opt1)) {
371
+ optionMustBeNumber("clamp");
372
+ }
373
+ const opt2 = options?.[1] ?? optionsRequired("clamp");
374
+ if (!validateNumberString(opt2)) {
375
+ optionMustBeNumber("clamp");
376
+ }
377
+ const min = Number(opt1);
378
+ const max = Number(opt2);
379
+ return (value) => {
380
+ if (typeof value !== "number") {
381
+ valueMustBeNumber("clamp");
382
+ }
383
+ return Math.min(Math.max(value, min), max);
384
+ };
385
+ };
386
+ var fix = (options) => {
387
+ const opt = options?.[0] ?? "0";
388
+ if (!validateNumberString(opt)) {
389
+ optionMustBeNumber("fix");
390
+ }
391
+ return (value) => {
392
+ if (typeof value !== "number") {
393
+ valueMustBeNumber("fix");
394
+ }
395
+ return value.toFixed(Number(opt));
396
+ };
397
+ };
398
+ var locale = (options) => {
399
+ const explicit = options?.[0];
400
+ return (value) => {
401
+ if (typeof value !== "number") {
402
+ valueMustBeNumber("locale");
403
+ }
404
+ return value.toLocaleString(explicit ?? config.locale);
405
+ };
406
+ };
407
+ var uc = (_options) => {
408
+ return (value) => {
409
+ return String(value).toUpperCase();
410
+ };
411
+ };
412
+ var lc = (_options) => {
413
+ return (value) => {
414
+ return String(value).toLowerCase();
415
+ };
416
+ };
417
+ var cap = (_options) => {
418
+ return (value) => {
419
+ const v = String(value);
420
+ if (v.length === 0) {
421
+ return v;
422
+ }
423
+ if (v.length === 1) {
424
+ return v.toUpperCase();
425
+ }
426
+ return v.charAt(0).toUpperCase() + v.slice(1);
427
+ };
428
+ };
429
+ var trim = (_options) => {
430
+ return (value) => {
431
+ return String(value).trim();
432
+ };
433
+ };
434
+ var slice = (options) => {
435
+ const numberedOpts = [];
436
+ const opt1 = options?.[0] ?? optionsRequired("slice");
437
+ if (!validateNumberString(opt1)) {
438
+ optionMustBeNumber("slice");
439
+ }
440
+ numberedOpts.push(Number(opt1));
441
+ const opt2 = options?.[1];
442
+ if (typeof opt2 !== "undefined") {
443
+ if (!validateNumberString(opt2)) {
444
+ optionMustBeNumber("slice");
445
+ }
446
+ numberedOpts.push(Number(opt2));
447
+ }
448
+ return (value) => {
449
+ return String(value).slice(...numberedOpts);
450
+ };
451
+ };
452
+ var substr = (options) => {
453
+ const opt1 = options?.[0] ?? optionsRequired("substr");
454
+ if (!validateNumberString(opt1)) {
455
+ optionMustBeNumber("substr");
456
+ }
457
+ const opt2 = options?.[1] ?? optionsRequired("substr");
458
+ if (!validateNumberString(opt2)) {
459
+ optionMustBeNumber("substr");
460
+ }
461
+ return (value) => {
462
+ return String(value).substr(Number(opt1), Number(opt2));
463
+ };
464
+ };
465
+ var pad = (options) => {
466
+ const opt1 = options?.[0] ?? optionsRequired("pad");
467
+ if (!validateNumberString(opt1)) {
468
+ optionMustBeNumber("pad");
469
+ }
470
+ const opt2 = options?.[1] ?? "0";
471
+ return (value) => {
472
+ return String(value).padStart(Number(opt1), opt2);
473
+ };
474
+ };
475
+ var rep = (options) => {
476
+ const opt = options?.[0] ?? optionsRequired("rep");
477
+ if (!validateNumberString(opt)) {
478
+ optionMustBeNumber("rep");
479
+ }
480
+ return (value) => {
481
+ return String(value).repeat(Number(opt));
482
+ };
483
+ };
484
+ var rev = (_options) => {
485
+ return (value) => {
486
+ return String(value).split("").reverse().join("");
487
+ };
488
+ };
489
+ var int = (_options) => {
490
+ return (value) => {
491
+ return parseInt(String(value), 10);
492
+ };
493
+ };
494
+ var float = (_options) => {
495
+ return (value) => {
496
+ return parseFloat(String(value));
497
+ };
498
+ };
499
+ var round = (options) => {
500
+ const opt = options?.[0] ?? "0";
501
+ if (!validateNumberString(opt)) {
502
+ optionMustBeNumber("round");
503
+ }
504
+ return (value) => {
505
+ if (typeof value !== "number") {
506
+ valueMustBeNumber("round");
507
+ }
508
+ const optValue = Math.pow(10, Number(opt));
509
+ return Math.round(value * optValue) / optValue;
510
+ };
511
+ };
512
+ var floor = (options) => {
513
+ const opt = options?.[0] ?? "0";
514
+ if (!validateNumberString(opt)) {
515
+ optionMustBeNumber("floor");
516
+ }
517
+ return (value) => {
518
+ if (typeof value !== "number") {
519
+ valueMustBeNumber("floor");
520
+ }
521
+ const optValue = Math.pow(10, Number(opt));
522
+ return Math.floor(value * optValue) / optValue;
523
+ };
524
+ };
525
+ var ceil = (options) => {
526
+ const opt = options?.[0] ?? "0";
527
+ if (!validateNumberString(opt)) {
528
+ optionMustBeNumber("ceil");
529
+ }
530
+ return (value) => {
531
+ if (typeof value !== "number") {
532
+ valueMustBeNumber("ceil");
533
+ }
534
+ const optValue = Math.pow(10, Number(opt));
535
+ return Math.ceil(value * optValue) / optValue;
536
+ };
537
+ };
538
+ var percent = (options) => {
539
+ const opt = options?.[0] ?? "0";
540
+ if (!validateNumberString(opt)) {
541
+ optionMustBeNumber("percent");
542
+ }
543
+ return (value) => {
544
+ if (typeof value !== "number") {
545
+ valueMustBeNumber("percent");
546
+ }
547
+ return `${(value * 100).toFixed(Number(opt))}%`;
548
+ };
549
+ };
550
+ var unit = (options) => {
551
+ const opt = options?.[0] ?? optionsRequired("unit");
552
+ return (value) => {
553
+ if (value === null || typeof value === "undefined") {
554
+ return value;
555
+ }
556
+ return String(value) + opt;
557
+ };
558
+ };
559
+ var join = (options) => {
560
+ const opt = options?.[0] ?? ", ";
561
+ return (value) => {
562
+ if (!Array.isArray(value)) {
563
+ valueMustBeArray("join");
564
+ }
565
+ return value.join(opt);
566
+ };
567
+ };
568
+ var truncate = (options) => {
569
+ const opt1 = options?.[0] ?? optionsRequired("truncate");
570
+ if (!validateNumberString(opt1)) {
571
+ optionMustBeNumber("truncate");
572
+ }
573
+ const maxLength = Number(opt1);
574
+ const suffix = options?.[1] ?? "\u2026";
575
+ return (value) => {
576
+ const v = String(value);
577
+ if (v.length <= maxLength) {
578
+ return v;
579
+ }
580
+ return v.slice(0, maxLength) + suffix;
581
+ };
582
+ };
583
+ var date = (options) => {
584
+ const explicit = options?.[0];
585
+ return (value) => {
586
+ if (!(value instanceof Date)) {
587
+ valueMustBeDate("date");
588
+ }
589
+ return value.toLocaleDateString(explicit ?? config.locale);
590
+ };
591
+ };
592
+ var time = (options) => {
593
+ const explicit = options?.[0];
594
+ return (value) => {
595
+ if (!(value instanceof Date)) {
596
+ valueMustBeDate("time");
597
+ }
598
+ return value.toLocaleTimeString(explicit ?? config.locale);
599
+ };
600
+ };
601
+ var datetime = (options) => {
602
+ const explicit = options?.[0];
603
+ return (value) => {
604
+ if (!(value instanceof Date)) {
605
+ valueMustBeDate("datetime");
606
+ }
607
+ return value.toLocaleString(explicit ?? config.locale);
608
+ };
609
+ };
610
+ var ymd = (options) => {
611
+ const opt = options?.[0] ?? "-";
612
+ return (value) => {
613
+ if (!(value instanceof Date)) {
614
+ valueMustBeDate("ymd");
615
+ }
616
+ const year = value.getFullYear().toString();
617
+ const month = (value.getMonth() + 1).toString().padStart(2, "0");
618
+ const day = value.getDate().toString().padStart(2, "0");
619
+ return `${year}${opt}${month}${opt}${day}`;
620
+ };
621
+ };
622
+ var hms = (options) => {
623
+ const opt = options?.[0] ?? ":";
624
+ return (value) => {
625
+ if (!(value instanceof Date)) {
626
+ valueMustBeDate("hms");
627
+ }
628
+ const hours = value.getHours().toString().padStart(2, "0");
629
+ const minutes = value.getMinutes().toString().padStart(2, "0");
630
+ const seconds = value.getSeconds().toString().padStart(2, "0");
631
+ return `${hours}${opt}${minutes}${opt}${seconds}`;
632
+ };
633
+ };
634
+ var falsy = (_options) => {
635
+ return (value) => value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value);
636
+ };
637
+ var truthy = (_options) => {
638
+ return (value) => value !== false && value !== null && value !== void 0 && value !== 0 && value !== "" && !Number.isNaN(value);
639
+ };
640
+ var defaults = (options) => {
641
+ const opt = options?.[0] ?? optionsRequired("defaults");
642
+ return (value) => {
643
+ if (value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value)) {
644
+ return opt;
645
+ }
646
+ return value;
647
+ };
648
+ };
649
+ var boolean = (_options) => {
650
+ return (value) => {
651
+ return Boolean(value);
652
+ };
653
+ };
654
+ var number = (_options) => {
655
+ return (value) => {
656
+ return Number(value);
657
+ };
658
+ };
659
+ var string = (_options) => {
660
+ return (value) => {
661
+ return String(value);
662
+ };
663
+ };
664
+ var _null = (_options) => {
665
+ return (value) => {
666
+ return value === "" ? null : value;
667
+ };
668
+ };
669
+ var builtinFilters = {
670
+ "eq": eq,
671
+ "ne": ne,
672
+ "not": not,
673
+ "lt": lt,
674
+ "le": le,
675
+ "gt": gt,
676
+ "ge": ge,
677
+ "inc": inc,
678
+ "dec": dec,
679
+ "mul": mul,
680
+ "div": div,
681
+ "mod": mod,
682
+ "abs": abs,
683
+ "clamp": clamp,
684
+ "fix": fix,
685
+ "locale": locale,
686
+ "uc": uc,
687
+ "lc": lc,
688
+ "cap": cap,
689
+ "trim": trim,
690
+ "slice": slice,
691
+ "substr": substr,
692
+ "pad": pad,
693
+ "rep": rep,
694
+ "rev": rev,
695
+ "truncate": truncate,
696
+ "join": join,
697
+ "int": int,
698
+ "float": float,
699
+ "round": round,
700
+ "floor": floor,
701
+ "ceil": ceil,
702
+ "percent": percent,
703
+ "unit": unit,
704
+ "date": date,
705
+ "time": time,
706
+ "datetime": datetime,
707
+ "ymd": ymd,
708
+ "hms": hms,
709
+ "falsy": falsy,
710
+ "truthy": truthy,
711
+ "defaults": defaults,
712
+ "boolean": boolean,
713
+ "number": number,
714
+ "string": string,
715
+ "null": _null
716
+ };
717
+ var outputBuiltinFilters = builtinFilters;
134
718
  var builtinFilterMeta = {
135
719
  // 比較・論理
136
720
  eq: { description: "\u7B49\u3057\u3044\u304B\u6BD4\u8F03", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
@@ -194,7 +778,14 @@ var STRUCTURAL_BINDING_TYPE_SET = /* @__PURE__ */ new Set([
194
778
  "else",
195
779
  "for"
196
780
  ]);
781
+ var DELIMITER = ".";
782
+ var WILDCARD = "*";
197
783
  var MAX_WILDCARD_DEPTH = 128;
784
+ var BINDING_SEPARATOR = ";";
785
+ var PROP_VALUE_SEPARATOR = ":";
786
+ var MODIFIER_SEPARATOR = "#";
787
+ var STATE_NAME_SEPARATOR = "@";
788
+ var FILTER_SEPARATOR = "|";
198
789
  var MODIFIER_PREVENT = "prevent";
199
790
  var MODIFIER_STOP = "stop";
200
791
  var MODIFIER_READONLY = "ro";
@@ -209,12 +800,99 @@ var MODIFIER_KEYS = Object.freeze([
209
800
  MODIFIER_KEY_INIT,
210
801
  MODIFIER_KEY_SYNC
211
802
  ]);
803
+ var ELSE_KEYWORD = "else";
804
+ var SPREAD_PROP = "...";
805
+ var EVENT_PROP_PREFIX = "on";
806
+ var EVENT_TOKEN_NAMESPACE = "eventToken";
807
+ var COMMAND_NAMESPACE = "command";
808
+ var CLASS_NAMESPACE = "class";
809
+ var ATTR_NAMESPACE = "attr";
810
+ var STYLE_NAMESPACE = "style";
212
811
  var INDEX_PARAM_PREFIX = "$";
213
812
  var tmpIndexByIndexName = {};
214
813
  for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
215
814
  tmpIndexByIndexName[`${INDEX_PARAM_PREFIX}${i + 1}`] = i;
216
815
  }
217
816
  Object.freeze(tmpIndexByIndexName);
817
+ var STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
818
+ var STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
819
+ var STATE_UPDATED_CALLBACK_NAME = "$updatedCallback";
820
+ var WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
821
+ var STATE_BINDABLES_NAME = "$bindables";
822
+ var STATE_COMMANDS_NAME = "$commands";
823
+ var STATE_COMMAND_TOKENS_NAME = "$commandTokens";
824
+ var STATE_COMMAND_NAMESPACE_NAME = "$command";
825
+ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
826
+ var STATE_ON_NAME = "$on";
827
+ var STATE_STREAMS_NAME = "$streams";
828
+ var STATE_WATCH_NAME = "$watch";
829
+ var STATE_LIST_KEYS_NAME = "$listKeys";
830
+ var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
831
+ var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
832
+ var WCS_MANIFEST_VERSION = 1;
833
+ function getWcsManifest() {
834
+ return {
835
+ version: WCS_MANIFEST_VERSION,
836
+ syntax: {
837
+ bindAttribute: config.bindAttributeName,
838
+ tagName: config.tagNames.state,
839
+ pathDelimiter: DELIMITER,
840
+ wildcard: WILDCARD,
841
+ delimiters: {
842
+ binding: BINDING_SEPARATOR,
843
+ propValue: PROP_VALUE_SEPARATOR,
844
+ modifier: MODIFIER_SEPARATOR,
845
+ stateName: STATE_NAME_SEPARATOR,
846
+ filter: FILTER_SEPARATOR
847
+ },
848
+ // 正本 STRUCTURAL_BINDING_TYPE_SET から導出(手書きの二重定義を排除)。
849
+ structuralDirectives: Array.from(STRUCTURAL_BINDING_TYPE_SET),
850
+ modifiers: {
851
+ flags: MODIFIER_FLAGS,
852
+ keyValue: MODIFIER_KEYS,
853
+ eventNamePrefix: EVENT_PROP_PREFIX
854
+ },
855
+ indexParam: {
856
+ prefix: INDEX_PARAM_PREFIX,
857
+ maxDepth: MAX_WILDCARD_DEPTH
858
+ },
859
+ bindingTypes: {
860
+ elseKeyword: ELSE_KEYWORD,
861
+ spread: SPREAD_PROP,
862
+ eventPropertyPrefix: EVENT_PROP_PREFIX,
863
+ propNamespaces: {
864
+ eventToken: EVENT_TOKEN_NAMESPACE,
865
+ command: COMMAND_NAMESPACE,
866
+ class: CLASS_NAMESPACE,
867
+ attr: ATTR_NAMESPACE,
868
+ style: STYLE_NAMESPACE
869
+ }
870
+ }
871
+ },
872
+ // 実装(Record のキー)から自動導出。手リストを持たない=ドリフトの構造的排除。
873
+ filters: Object.keys(outputBuiltinFilters),
874
+ filterMeta: builtinFilterMeta,
875
+ reservedLifecycle: [
876
+ STATE_CONNECTED_CALLBACK_NAME,
877
+ STATE_DISCONNECTED_CALLBACK_NAME,
878
+ STATE_UPDATED_CALLBACK_NAME,
879
+ WEBCOMPONENT_STATE_READY_CALLBACK_NAME
880
+ ],
881
+ reservedStateApi: [
882
+ STATE_BINDABLES_NAME,
883
+ STATE_COMMANDS_NAME,
884
+ STATE_COMMAND_TOKENS_NAME,
885
+ STATE_COMMAND_NAMESPACE_NAME,
886
+ STATE_EVENT_TOKENS_NAME,
887
+ STATE_ON_NAME,
888
+ STATE_STREAMS_NAME,
889
+ STATE_WATCH_NAME,
890
+ STATE_LIST_KEYS_NAME,
891
+ STATE_STREAM_STATUS_NAMESPACE_NAME,
892
+ STATE_STREAM_ERROR_NAMESPACE_NAME
893
+ ]
894
+ };
895
+ }
218
896
 
219
897
  // src/service/completionData.ts
220
898
  var BUILTIN_FILTERS = Object.entries(builtinFilterMeta).map(
@@ -360,7 +1038,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
360
1038
  }
361
1039
  return elements;
362
1040
  }
363
- function findScriptJsonById(html, id) {
1041
+ function findScriptJsonById(html, id2) {
364
1042
  let pos = 0;
365
1043
  const len = html.length;
366
1044
  while (pos < len) {
@@ -377,7 +1055,7 @@ function findScriptJsonById(html, id) {
377
1055
  }
378
1056
  const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
379
1057
  const idAttr = extractAttribute(scriptMatch.tagContent, "id");
380
- if (typeAttr?.toLowerCase() === "application/json" && idAttr === id) {
1058
+ if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
381
1059
  const contentStart = scriptMatch.end;
382
1060
  const scriptCloseIdx = findCloseTag(html, contentStart, "script");
383
1061
  if (scriptCloseIdx === -1) return null;
@@ -392,8 +1070,8 @@ function matchOpenTag(html, pos, tagName) {
392
1070
  const nameStart = pos + 1;
393
1071
  const nameEnd = nameStart + tagName.length;
394
1072
  if (nameEnd > html.length) return null;
395
- const slice = html.slice(nameStart, nameEnd);
396
- if (slice.toLowerCase() !== tagName.toLowerCase()) return null;
1073
+ const slice3 = html.slice(nameStart, nameEnd);
1074
+ if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
397
1075
  const charAfter = html[nameEnd];
398
1076
  if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
399
1077
  return null;
@@ -512,6 +1190,39 @@ function analyzeWatchEntries(scriptContent) {
512
1190
  }
513
1191
  return entries;
514
1192
  }
1193
+ function analyzeDeclarationSpans(scriptContent) {
1194
+ const root = locateDefaultExportObject(scriptContent);
1195
+ if (!root) return [];
1196
+ const out = [];
1197
+ for (const prop of parseTopLevelProperties(root.content)) {
1198
+ if (prop.nameStart === void 0 || prop.nameEnd === void 0) continue;
1199
+ out.push({
1200
+ name: prop.name,
1201
+ kind: prop.kind,
1202
+ start: root.start + prop.nameStart,
1203
+ end: root.start + prop.nameEnd
1204
+ });
1205
+ }
1206
+ return out;
1207
+ }
1208
+ function analyzeCallableBodies(scriptContent) {
1209
+ const root = locateDefaultExportObject(scriptContent);
1210
+ if (!root) return [];
1211
+ const out = [];
1212
+ for (const prop of parseTopLevelProperties(root.content)) {
1213
+ if (prop.kind !== "getter" && prop.kind !== "method") continue;
1214
+ if (prop.nameStart === void 0 || prop.nameEnd === void 0) continue;
1215
+ out.push({
1216
+ name: prop.name,
1217
+ kind: prop.kind,
1218
+ start: root.start + prop.nameStart,
1219
+ end: root.start + prop.nameEnd,
1220
+ body: prop.value ?? "",
1221
+ bodyStart: root.start + (prop.valueStart ?? 0)
1222
+ });
1223
+ }
1224
+ return out;
1225
+ }
515
1226
  function isNonFunctionLiteral(value) {
516
1227
  if (value === void 0) return false;
517
1228
  const trimmed = value.trim();
@@ -711,17 +1422,32 @@ function parseTopLevelProperties(objectContent) {
711
1422
  const braceStart = match.index + match[0].length - 1;
712
1423
  const body = extractBracedContent(objectContent, scan, braceStart);
713
1424
  regex.lastIndex = braceStart + body.length + 2;
1425
+ return { body, bodyStart: braceStart + 1 };
714
1426
  };
715
1427
  const accessorName = nameAt(1) ?? nameAt(2) ?? nameAt(3);
716
1428
  if (accessorName) {
717
- props.push({ name: accessorName, kind: "getter", nameStart: nameSpan[0], nameEnd: nameSpan[1] });
718
- skipBody();
1429
+ const { body, bodyStart } = skipBody();
1430
+ props.push({
1431
+ name: accessorName,
1432
+ kind: "getter",
1433
+ value: body,
1434
+ valueStart: bodyStart,
1435
+ nameStart: nameSpan[0],
1436
+ nameEnd: nameSpan[1]
1437
+ });
719
1438
  continue;
720
1439
  }
721
1440
  const methodName = nameAt(4) ?? nameAt(5) ?? nameAt(6);
722
1441
  if (methodName) {
723
- props.push({ name: methodName, kind: "method", nameStart: nameSpan[0], nameEnd: nameSpan[1] });
724
- skipBody();
1442
+ const { body, bodyStart } = skipBody();
1443
+ props.push({
1444
+ name: methodName,
1445
+ kind: "method",
1446
+ value: body,
1447
+ valueStart: bodyStart,
1448
+ nameStart: nameSpan[0],
1449
+ nameEnd: nameSpan[1]
1450
+ });
725
1451
  continue;
726
1452
  }
727
1453
  const propName = nameAt(7) ?? nameAt(8) ?? nameAt(9);
@@ -1007,6 +1733,37 @@ function getEnclosingFors(html, offset, bindAttrName = "data-wcs") {
1007
1733
  }
1008
1734
  return enclosing;
1009
1735
  }
1736
+ function countWildcardSegments(path) {
1737
+ let count = 0;
1738
+ for (const segment of path.split(".")) {
1739
+ if (segment === "*") count++;
1740
+ }
1741
+ return count;
1742
+ }
1743
+ function forPathOf(raw) {
1744
+ let path = raw.trim();
1745
+ const pipe = path.indexOf("|");
1746
+ if (pipe !== -1) path = path.slice(0, pipe).trim();
1747
+ const at = path.indexOf("@");
1748
+ if (at !== -1) path = path.slice(0, at).trim();
1749
+ return path;
1750
+ }
1751
+ function getAvailableWildcardRank(html, offset, bindAttrName = "data-wcs") {
1752
+ const chain = getEnclosingForPaths(html, offset, bindAttrName);
1753
+ if (chain.length === 0) return 0;
1754
+ let resolved = "";
1755
+ for (const raw of chain) {
1756
+ const path = forPathOf(raw);
1757
+ if (path === ".") {
1758
+ resolved = `${resolved}.*`;
1759
+ } else if (path.startsWith(".")) {
1760
+ resolved = `${resolved}.*.${path.slice(1)}`;
1761
+ } else {
1762
+ resolved = path;
1763
+ }
1764
+ }
1765
+ return countWildcardSegments(resolved) + 1;
1766
+ }
1010
1767
  function getForTemplateDepthAt(html, openPos, offset, bindAttrName) {
1011
1768
  const tagEnd = html.indexOf(">", openPos);
1012
1769
  if (tagEnd === -1 || tagEnd >= offset) return 0;
@@ -1037,8 +1794,8 @@ function getForTemplateDepthAt(html, openPos, offset, bindAttrName) {
1037
1794
  }
1038
1795
 
1039
1796
  // src/core/messages.ts
1040
- function resolveLocale(locale) {
1041
- if (locale === void 0 || locale === "" || /^ja\b|^ja[-_]/i.test(locale) || locale.toLowerCase() === "ja") return "ja";
1797
+ function resolveLocale(locale3) {
1798
+ if (locale3 === void 0 || locale3 === "" || /^ja\b|^ja[-_]/i.test(locale3) || locale3.toLowerCase() === "ja") return "ja";
1042
1799
  return "en";
1043
1800
  }
1044
1801
  var JA_EXPECTED_LABEL = {
@@ -1060,6 +1817,10 @@ var ja = {
1060
1817
  omittedPathOutsideFor: (p) => `\u7701\u7565\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
1061
1818
  loopIndexOutsideFor: (p) => `\u30EB\u30FC\u30D7\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
1062
1819
  resolvedPathInUi: (p) => `\u89E3\u6C7A\u6E08\u307F\u30D1\u30B9 "${p}" \u306F UI \u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u30D1\u30BF\u30FC\u30F3\u30D1\u30B9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044`,
1820
+ indexArity: (api, p, req, wc, actual) => `${api}("${p}") \u306E\u6DFB\u5B57\u306F${req === "exact" ? `\u3061\u3087\u3046\u3069 ${wc} \u500B` : `${wc} \u500B\u4EE5\u4E0B`}\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u30D1\u30B9\u4E2D\u306E "*" \u306F ${wc} \u500B\uFF09\u3002${actual} \u500B\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u3059`,
1821
+ wildcardRank: (subject, needed, available) => `${subject} \u306F ${needed} \u6BB5\u306E\u30EB\u30FC\u30D7\u304C\u5FC5\u8981\u3067\u3059\u304C\u3001\u73FE\u5728\u306E\u30B9\u30B3\u30FC\u30D7\u306F ${available} \u6BB5\u3067\u3059`,
1822
+ getterCycle: (cycle) => `\u30D1\u30B9 getter \u304C\u5FAA\u74B0\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${cycle}`,
1823
+ updatedCallbackUnbound: (p) => `$updatedCallback \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`,
1063
1824
  handlerFilterNotAllowed: (prop) => `\u30A4\u30D9\u30F3\u30C8\u30CF\u30F3\u30C9\u30E9 "${prop}" \u306B\u30D5\u30A3\u30EB\u30BF\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
1064
1825
  typeExpectation: (label, expected, resultType) => `"${label}" \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728\u306E\u578B: ${resultType}\uFF09`,
1065
1826
  filterUnknown: (n) => `\u30D5\u30A3\u30EB\u30BF "${n}" \u306F\u7D44\u307F\u8FBC\u307F\u30D5\u30A3\u30EB\u30BF\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
@@ -1083,6 +1844,7 @@ var ja = {
1083
1844
  tagCommandUnknown: (name, tag, declared) => `"${name}" \u306F <${tag}> \u306E command \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
1084
1845
  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`,
1085
1846
  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`,
1847
+ ariaAttrUnknown: (name) => `"${name}" \u306F WAI-ARIA \u306E\u5C5E\u6027\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002setAttribute \u306F\u305D\u306E\u307E\u307E\u66F8\u304D\u8FBC\u307F\u307E\u3059\u304C\u3001\u652F\u63F4\u6280\u8853\u306B\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059`,
1086
1848
  didYouMean: (c) => `\u3002\u3082\u3057\u304B\u3057\u3066: "${c}"`,
1087
1849
  none: () => `\u306A\u3057`,
1088
1850
  triggerSeededTruthy: (path) => `trigger \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C true \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002trigger \u306F\u30A8\u30C3\u30B8\u691C\u51FA\u306A\u3057\uFF08truthy \u66F8\u304D\u8FBC\u307F\u3067\u5373\u767A\u706B\u30FBmanual \u3082\u30D0\u30A4\u30D1\u30B9\uFF09\u306E\u305F\u3081\u3001\u30D0\u30A4\u30F3\u30C9\u6642\u306B\u5373\u767A\u706B\u3057\u307E\u3059\u3002false \u3067\u30B7\u30FC\u30C9\u3057\u3066\u304F\u3060\u3055\u3044`,
@@ -1110,6 +1872,10 @@ var en = {
1110
1872
  omittedPathOutsideFor: (p) => `Shorthand path "${p}" cannot be used outside a <template for>`,
1111
1873
  loopIndexOutsideFor: (p) => `Loop index "${p}" cannot be used outside a <template for>`,
1112
1874
  resolvedPathInUi: (p) => `Resolved path "${p}" cannot be used in a UI binding. Use a pattern path instead`,
1875
+ indexArity: (api, p, req, wc, actual) => `${api}("${p}") requires ${req === "exact" ? "exactly" : "at most"} ${wc} index(es) ("*" appears ${wc} time(s) in the path) but got ${actual}`,
1876
+ wildcardRank: (subject, needed, available) => `${subject} needs ${needed} enclosing loop level(s) but the current scope provides ${available}`,
1877
+ getterCycle: (cycle) => `Path getters form a dependency cycle: ${cycle}`,
1878
+ updatedCallbackUnbound: (p) => `$updatedCallback 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`,
1113
1879
  handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
1114
1880
  typeExpectation: (label, expected, resultType) => `"${label}" requires ${EN_EXPECTED_LABEL[expected]} (current type: ${resultType})`,
1115
1881
  filterUnknown: (n) => `Filter "${n}" is not a built-in filter`,
@@ -1133,6 +1899,7 @@ var en = {
1133
1899
  tagCommandUnknown: (name, tag, declared) => `"${name}" is not a command of <${tag}> (declared: ${declared})`,
1134
1900
  spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
1135
1901
  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})`,
1902
+ ariaAttrUnknown: (name) => `"${name}" is not a WAI-ARIA attribute. setAttribute writes it anyway, and assistive technology silently ignores it`,
1136
1903
  didYouMean: (c) => `. Did you mean "${c}"?`,
1137
1904
  none: () => `none`,
1138
1905
  triggerSeededTruthy: (path) => `The trigger-bound slot "${path}" is seeded with true. trigger has no edge detection (any truthy write fires, and it bypasses manual), so it fires immediately at bind. Seed it with false`,
@@ -1142,15 +1909,15 @@ var en = {
1142
1909
  signalsDualEntry: () => `Both @wcstack/signals and @wcstack/signals/dom are imported on this page. On a CDN each entry is a self-contained bundle, so the reactive core is duplicated and reactivity breaks at the seam \u2014 import everything from the single /dom entry`
1143
1910
  };
1144
1911
  var CATALOGS = { ja, en };
1145
- function getMessages(locale) {
1146
- return CATALOGS[resolveLocale(locale)];
1912
+ function getMessages(locale3) {
1913
+ return CATALOGS[resolveLocale(locale3)];
1147
1914
  }
1148
1915
 
1149
1916
  // src/service/bindingValidator.ts
1150
1917
  var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
1151
- function validateBindings(html, attrName, stateTagName = "wcs-state", locale, fileReader) {
1918
+ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader) {
1152
1919
  const diagnostics = [];
1153
- const msgs = getMessages(locale);
1920
+ const msgs = getMessages(locale3);
1154
1921
  const statePaths = getStatePathsFromHtml(html, stateTagName, fileReader);
1155
1922
  const pathsByState = /* @__PURE__ */ new Map();
1156
1923
  for (const p of statePaths) {
@@ -1327,6 +2094,24 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale, fi
1327
2094
  severity: "warning"
1328
2095
  });
1329
2096
  }
2097
+ if (insideFor && !pathTrimmed.startsWith(".") && !binding.includes("@")) {
2098
+ const indexMatch = /^\$(\d+)$/.exec(pathTrimmed);
2099
+ const needed = indexMatch !== null ? Number(indexMatch[1]) : pathTrimmed.includes("*") ? countWildcardSegments(pathTrimmed) : 0;
2100
+ if (needed > 0) {
2101
+ const available = getAvailableWildcardRank(html, attr.valueStart, attrName);
2102
+ if (available > 0 && needed > available) {
2103
+ const pathOffset = binding.indexOf(parsed.path);
2104
+ const pathStart = bindingStart + pathOffset;
2105
+ diagnostics.push({
2106
+ code: WcsDiagnosticCode.WildcardRank,
2107
+ start: pathStart,
2108
+ end: pathStart + pathTrimmed.length,
2109
+ message: msgs.wildcardRank(`"${pathTrimmed}"`, needed, available),
2110
+ severity: "warning"
2111
+ });
2112
+ }
2113
+ }
2114
+ }
1330
2115
  if (/\.\d+\.|\.\d+$/.test(pathTrimmed)) {
1331
2116
  const pathOffset = binding.indexOf(parsed.path);
1332
2117
  const pathStart = bindingStart + pathOffset;
@@ -1670,8 +2455,8 @@ function isLiteral(value) {
1670
2455
  }
1671
2456
 
1672
2457
  // src/service/stateTypeValidator.ts
1673
- function validateStateTypes(html, stateTagName = "wcs-state", locale) {
1674
- const msgs = getMessages(locale);
2458
+ function validateStateTypes(html, stateTagName = "wcs-state", locale3) {
2459
+ const msgs = getMessages(locale3);
1675
2460
  const blocks = parseWcsScriptBlocks(html, stateTagName);
1676
2461
  const diagnostics = [];
1677
2462
  for (const block of blocks) {
@@ -1798,8 +2583,8 @@ function isApiRoot(root) {
1798
2583
  // src/service/nestedAssignValidator.ts
1799
2584
  var NESTED_ASSIGN = new RegExp(`${ROOT_DOT}(${CHAIN_ONE_PLUS})${ASSIGN_TAIL}`, "g");
1800
2585
  var PRE_NESTED_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_DOT}(${CHAIN_ONE_PLUS})`, "g");
1801
- function validateNestedAssigns(html, stateTagName = "wcs-state", locale) {
1802
- const msgs = getMessages(locale);
2586
+ function validateNestedAssigns(html, stateTagName = "wcs-state", locale3) {
2587
+ const msgs = getMessages(locale3);
1803
2588
  const blocks = parseWcsScriptBlocks(html, stateTagName);
1804
2589
  const diagnostics = [];
1805
2590
  for (const block of blocks) {
@@ -1821,7 +2606,7 @@ function findNestedAssigns(script, baseOffset, msgs, out) {
1821
2606
  start,
1822
2607
  end: start + full.length,
1823
2608
  message: msgs.nestedAssign(suggestedPath),
1824
- severity: "warning"
2609
+ severity: "error"
1825
2610
  });
1826
2611
  }
1827
2612
  }
@@ -1850,8 +2635,8 @@ var PRE_BRACKET_INDEX = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}(${BRACKETS_ONLY
1850
2635
  function toAccessor(path) {
1851
2636
  return /^[A-Za-z_]\w*$/.test(path) ? `this.${path}` : `this["${path}"]`;
1852
2637
  }
1853
- function validateArrayMutations(html, stateTagName = "wcs-state", locale) {
1854
- const msgs = getMessages(locale);
2638
+ function validateArrayMutations(html, stateTagName = "wcs-state", locale3) {
2639
+ const msgs = getMessages(locale3);
1855
2640
  const blocks = parseWcsScriptBlocks(html, stateTagName);
1856
2641
  const diagnostics = [];
1857
2642
  for (const block of blocks) {
@@ -1874,7 +2659,7 @@ function findDestructiveCalls(script, baseOffset, msgs, out) {
1874
2659
  start,
1875
2660
  end: start + full.length,
1876
2661
  message: msgs.arrayMutation(method, ALTERNATIVES[method](toAccessor(statePath))),
1877
- severity: "warning",
2662
+ severity: "error",
1878
2663
  statePath
1879
2664
  });
1880
2665
  }
@@ -1894,7 +2679,7 @@ function findIndexAssigns(script, baseOffset, msgs, out) {
1894
2679
  start,
1895
2680
  end: start + full.length,
1896
2681
  message: msgs.arrayIndexAssign(suggestedPath),
1897
- severity: "warning",
2682
+ severity: "error",
1898
2683
  statePath: suggestedPath
1899
2684
  });
1900
2685
  }
@@ -1961,9 +2746,9 @@ function isInsideTag(html, offset, tagName) {
1961
2746
  }
1962
2747
 
1963
2748
  // src/service/templateSyntaxValidator.ts
1964
- function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale, fileReader) {
2749
+ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader) {
1965
2750
  const diagnostics = [];
1966
- const msgs = getMessages(locale);
2751
+ const msgs = getMessages(locale3);
1967
2752
  const allPaths = getStatePathsFromHtml(html, stateTagName, fileReader);
1968
2753
  if (allPaths.length === 0) return diagnostics;
1969
2754
  const defaultPaths = allPaths.filter((p) => p.stateName === "default");
@@ -2015,6 +2800,22 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
2015
2800
  severity: "warning"
2016
2801
  });
2017
2802
  }
2803
+ if (insideFor && !pathPart.startsWith(".") && !pathPart.includes("@")) {
2804
+ const indexMatch = /^\$(\d+)$/.exec(pathPart);
2805
+ const needed = indexMatch !== null ? Number(indexMatch[1]) : pathPart.includes("*") ? countWildcardSegments(pathPart) : 0;
2806
+ if (needed > 0) {
2807
+ const available = getAvailableWildcardRank(html, item.matchStart, bindAttrName);
2808
+ if (available > 0 && needed > available) {
2809
+ diagnostics.push({
2810
+ code: WcsDiagnosticCode.WildcardRank,
2811
+ start: item.exprStart,
2812
+ end: item.exprStart + pathPart.length,
2813
+ message: msgs.wildcardRank(`"${pathPart}"`, needed, available),
2814
+ severity: "warning"
2815
+ });
2816
+ }
2817
+ }
2818
+ }
2018
2819
  if (/\.\d+\.|\.\d+$/.test(pathPart)) {
2019
2820
  diagnostics.push({
2020
2821
  code: WcsDiagnosticCode.TemplateSyntax,
@@ -3212,6 +4013,35 @@ var BUILTIN_TAGS = {
3212
4013
  "abort"
3213
4014
  ]
3214
4015
  },
4016
+ "wcs-view-transition": {
4017
+ "package": "view-transition",
4018
+ "hasWcBindable": true,
4019
+ "observedAttributes": [
4020
+ "mode",
4021
+ "naming",
4022
+ "naming-limit",
4023
+ "reduced-motion",
4024
+ "types",
4025
+ "disabled",
4026
+ "for"
4027
+ ],
4028
+ "inputs": {
4029
+ "disabled": "disabled",
4030
+ "mode": "mode",
4031
+ "naming": "naming",
4032
+ "namingLimit": "naming-limit",
4033
+ "reducedMotion": "reduced-motion",
4034
+ "types": "types",
4035
+ "participants": "for"
4036
+ },
4037
+ "properties": [
4038
+ "active",
4039
+ "error"
4040
+ ],
4041
+ "commands": [
4042
+ "skip"
4043
+ ]
4044
+ },
3215
4045
  "wcs-wakelock": {
3216
4046
  "package": "wakelock",
3217
4047
  "hasWcBindable": true,
@@ -3314,9 +4144,9 @@ var DOM_COMMON_PROPERTIES = /* @__PURE__ */ new Set([
3314
4144
  ]);
3315
4145
  var STRUCTURAL_DIRECTIVES2 = /* @__PURE__ */ new Set(["for", "if", "elseif", "else"]);
3316
4146
  var EMPTYISH_SEEDS = /* @__PURE__ */ new Set(["''", '""', "``", "null", "[]", "{}"]);
3317
- function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale, fileReader) {
4147
+ function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale3, fileReader) {
3318
4148
  const diagnostics = [];
3319
- const msgs = getMessages(locale);
4149
+ const msgs = getMessages(locale3);
3320
4150
  const occurrences = findBuiltinTagOccurrences(html);
3321
4151
  if (occurrences.length === 0) return diagnostics;
3322
4152
  let statePaths = null;
@@ -3524,10 +4354,102 @@ function hasBooleanAttribute(attrsText, attrName) {
3524
4354
  return new RegExp(`(?:^|\\s)${attrName}(?:\\s|=|$)`, "i").test(attrsText);
3525
4355
  }
3526
4356
 
4357
+ // src/service/ariaValidator.ts
4358
+ var ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
4359
+ // widget attributes
4360
+ "aria-autocomplete",
4361
+ "aria-checked",
4362
+ "aria-disabled",
4363
+ "aria-errormessage",
4364
+ "aria-expanded",
4365
+ "aria-haspopup",
4366
+ "aria-hidden",
4367
+ "aria-invalid",
4368
+ "aria-label",
4369
+ "aria-level",
4370
+ "aria-modal",
4371
+ "aria-multiline",
4372
+ "aria-multiselectable",
4373
+ "aria-orientation",
4374
+ "aria-placeholder",
4375
+ "aria-pressed",
4376
+ "aria-readonly",
4377
+ "aria-required",
4378
+ "aria-selected",
4379
+ "aria-sort",
4380
+ "aria-valuemax",
4381
+ "aria-valuemin",
4382
+ "aria-valuenow",
4383
+ "aria-valuetext",
4384
+ // live region attributes
4385
+ "aria-busy",
4386
+ "aria-live",
4387
+ "aria-relevant",
4388
+ "aria-atomic",
4389
+ // drag-and-drop (deprecated in 1.1, still valid names)
4390
+ "aria-dropeffect",
4391
+ "aria-grabbed",
4392
+ // relationship attributes
4393
+ "aria-activedescendant",
4394
+ "aria-colcount",
4395
+ "aria-colindex",
4396
+ "aria-colindextext",
4397
+ "aria-colspan",
4398
+ "aria-controls",
4399
+ "aria-describedby",
4400
+ "aria-details",
4401
+ "aria-flowto",
4402
+ "aria-labelledby",
4403
+ "aria-owns",
4404
+ "aria-posinset",
4405
+ "aria-rowcount",
4406
+ "aria-rowindex",
4407
+ "aria-rowindextext",
4408
+ "aria-rowspan",
4409
+ "aria-setsize",
4410
+ // global additions
4411
+ "aria-current",
4412
+ "aria-keyshortcuts",
4413
+ "aria-roledescription",
4414
+ // 1.3 additions with broad implementation
4415
+ "aria-braillelabel",
4416
+ "aria-brailleroledescription",
4417
+ "aria-description"
4418
+ ]);
4419
+ function validateAriaAttributes(html, bindAttribute = "data-wcs", locale3) {
4420
+ const diagnostics = [];
4421
+ const msgs = getMessages(locale3);
4422
+ for (const attr of findAllBindAttributes(html, bindAttribute)) {
4423
+ let exprOffset = 0;
4424
+ for (const expr of splitBindingExpressions(attr.value)) {
4425
+ const exprStart = attr.valueStart + exprOffset;
4426
+ exprOffset += expr.length + 1;
4427
+ const property = parseBindingExpression(expr).property;
4428
+ if (!property) continue;
4429
+ const bare = property.split("#")[0];
4430
+ if (!bare.toLowerCase().startsWith("attr.aria-")) continue;
4431
+ const ariaName = bare.slice("attr.".length).toLowerCase();
4432
+ if (ARIA_ATTRIBUTES.has(ariaName)) continue;
4433
+ const propIndex = expr.indexOf(property);
4434
+ const start = propIndex === -1 ? exprStart : exprStart + propIndex;
4435
+ const end = propIndex === -1 ? exprStart + expr.length : start + property.length;
4436
+ diagnostics.push({
4437
+ code: WcsDiagnosticCode.AriaAttrUnknown,
4438
+ start,
4439
+ end,
4440
+ severity: "warning",
4441
+ member: ariaName,
4442
+ message: msgs.ariaAttrUnknown(ariaName) + suggestion(ariaName, [...ARIA_ATTRIBUTES], msgs)
4443
+ });
4444
+ }
4445
+ }
4446
+ return diagnostics;
4447
+ }
4448
+
3527
4449
  // src/service/documentEnvValidator.ts
3528
- function validateDocumentEnv(html, locale) {
4450
+ function validateDocumentEnv(html, locale3) {
3529
4451
  const diagnostics = [];
3530
- const msgs = getMessages(locale);
4452
+ const msgs = getMessages(locale3);
3531
4453
  const scanText = blankHtmlComments(html);
3532
4454
  const autos = findWcstackAutoScripts(scanText);
3533
4455
  const stateIndex = autos.findIndex((a) => a.pkg === "state");
@@ -3637,9 +4559,9 @@ function blankJsComments(code) {
3637
4559
  }
3638
4560
 
3639
4561
  // src/service/watchDeclarationValidator.ts
3640
- var STATE_NAME_SEPARATOR = "@";
3641
- function validateWatchDeclarations(html, stateTagName = "wcs-state", locale) {
3642
- const msgs = getMessages(locale);
4562
+ var STATE_NAME_SEPARATOR2 = "@";
4563
+ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
4564
+ const msgs = getMessages(locale3);
3643
4565
  const out = [];
3644
4566
  for (const block of parseWcsScriptBlocks(html, stateTagName)) {
3645
4567
  const nonObject = findNonObjectWatch(block.content);
@@ -3673,7 +4595,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale) {
3673
4595
  function validateEntry(entry, pathSet, msgs) {
3674
4596
  const { key } = entry;
3675
4597
  const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
3676
- if (key.includes(STATE_NAME_SEPARATOR)) {
4598
+ if (key.includes(STATE_NAME_SEPARATOR2)) {
3677
4599
  return invalid(msgs.watchKeyCrossState(key));
3678
4600
  }
3679
4601
  if (key.startsWith("$")) {
@@ -3695,23 +4617,1485 @@ function validateEntry(entry, pathSet, msgs) {
3695
4617
  return null;
3696
4618
  }
3697
4619
 
3698
- // src/core/validateDocument.ts
3699
- function validateDocument(text, options = {}) {
3700
- const bindAttribute = options.bindAttribute ?? "data-wcs";
3701
- const stateTagName = options.stateTagName ?? "wcs-state";
3702
- const locale = options.locale;
4620
+ // ../state/dist/parser.esm.js
4621
+ var DELIMITER2 = ".";
4622
+ var WILDCARD2 = "*";
4623
+ var MAX_WILDCARD_DEPTH2 = 128;
4624
+ var BINDING_SEPARATOR2 = ";";
4625
+ var PROP_VALUE_SEPARATOR2 = ":";
4626
+ var MODIFIER_SEPARATOR2 = "#";
4627
+ var STATE_NAME_SEPARATOR3 = "@";
4628
+ var FILTER_SEPARATOR2 = "|";
4629
+ var ELSE_KEYWORD2 = "else";
4630
+ var SPREAD_PROP2 = "...";
4631
+ var EVENT_PROP_PREFIX2 = "on";
4632
+ var EVENT_TOKEN_NAMESPACE2 = "eventToken";
4633
+ var INDEX_PARAM_PREFIX2 = "$";
4634
+ var tmpIndexByIndexName2 = {};
4635
+ for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
4636
+ tmpIndexByIndexName2[`${INDEX_PARAM_PREFIX2}${i + 1}`] = i;
4637
+ }
4638
+ Object.freeze(tmpIndexByIndexName2);
4639
+ var _cache = /* @__PURE__ */ new Map();
4640
+ function clearPathInfoCacheForTooling() {
4641
+ _cache.clear();
4642
+ }
4643
+ var id = 0;
4644
+ function getPathInfo(path) {
4645
+ let pathInfo = _cache.get(path);
4646
+ if (typeof pathInfo !== "undefined") {
4647
+ return pathInfo;
4648
+ }
4649
+ pathInfo = Object.freeze(new PathInfo(path));
4650
+ _cache.set(path, pathInfo);
4651
+ return pathInfo;
4652
+ }
4653
+ var PathInfo = class {
4654
+ id = ++id;
4655
+ path;
4656
+ segments;
4657
+ lastSegment;
4658
+ cumulativePaths;
4659
+ cumulativePathSet;
4660
+ cumulativePathInfos;
4661
+ cumulativePathInfoSet;
4662
+ parentPath;
4663
+ wildcardPaths;
4664
+ wildcardPathSet;
4665
+ indexByWildcardPath;
4666
+ wildcardPathInfos;
4667
+ wildcardPathInfoSet;
4668
+ wildcardParentPaths;
4669
+ wildcardParentPathSet;
4670
+ wildcardParentPathInfos;
4671
+ wildcardParentPathInfoSet;
4672
+ wildcardPositions;
4673
+ lastWildcardPath;
4674
+ lastWildcardInfo;
4675
+ wildcardCount;
4676
+ parentPathInfo;
4677
+ constructor(path) {
4678
+ const getPattern = (_path) => {
4679
+ return path === _path ? this : getPathInfo(_path);
4680
+ };
4681
+ const segments = path.split(".");
4682
+ const cumulativePaths = [];
4683
+ const cumulativePathInfos = [];
4684
+ const wildcardPaths = [];
4685
+ const indexByWildcardPath = {};
4686
+ const wildcardPathInfos = [];
4687
+ const wildcardParentPaths = [];
4688
+ const wildcardParentPathInfos = [];
4689
+ const wildcardPositions = [];
4690
+ let currentPatternPath = "", prevPatternPath = "";
4691
+ let wildcardCount = 0;
4692
+ for (let i = 0; i < segments.length; i++) {
4693
+ currentPatternPath += segments[i];
4694
+ if (segments[i] === WILDCARD2) {
4695
+ wildcardPaths.push(currentPatternPath);
4696
+ indexByWildcardPath[currentPatternPath] = wildcardCount;
4697
+ wildcardPathInfos.push(getPattern(currentPatternPath));
4698
+ wildcardParentPaths.push(prevPatternPath);
4699
+ wildcardParentPathInfos.push(getPattern(prevPatternPath));
4700
+ wildcardPositions.push(i);
4701
+ wildcardCount++;
4702
+ }
4703
+ cumulativePaths.push(currentPatternPath);
4704
+ cumulativePathInfos.push(getPattern(currentPatternPath));
4705
+ prevPatternPath = currentPatternPath;
4706
+ currentPatternPath += ".";
4707
+ }
4708
+ const lastWildcardPath = wildcardPaths.length > 0 ? wildcardPaths[wildcardPaths.length - 1] : null;
4709
+ const parentPath = cumulativePaths.length > 1 ? cumulativePaths[cumulativePaths.length - 2] : null;
4710
+ this.path = path;
4711
+ this.segments = segments;
4712
+ this.lastSegment = segments[segments.length - 1];
4713
+ this.cumulativePaths = cumulativePaths;
4714
+ this.cumulativePathSet = new Set(cumulativePaths);
4715
+ this.cumulativePathInfos = cumulativePathInfos;
4716
+ this.cumulativePathInfoSet = new Set(cumulativePathInfos);
4717
+ this.wildcardPaths = wildcardPaths;
4718
+ this.wildcardPathSet = new Set(wildcardPaths);
4719
+ this.indexByWildcardPath = indexByWildcardPath;
4720
+ this.wildcardPathInfos = wildcardPathInfos;
4721
+ this.wildcardPathInfoSet = new Set(wildcardPathInfos);
4722
+ this.wildcardParentPaths = wildcardParentPaths;
4723
+ this.wildcardParentPathSet = new Set(wildcardParentPaths);
4724
+ this.wildcardParentPathInfos = wildcardParentPathInfos;
4725
+ this.wildcardParentPathInfoSet = new Set(wildcardParentPathInfos);
4726
+ this.wildcardPositions = wildcardPositions;
4727
+ this.lastWildcardPath = lastWildcardPath;
4728
+ this.lastWildcardInfo = lastWildcardPath ? getPattern(lastWildcardPath) : null;
4729
+ this.parentPath = parentPath;
4730
+ this.parentPathInfo = parentPath ? getPattern(parentPath) : null;
4731
+ this.wildcardCount = wildcardCount;
4732
+ }
4733
+ };
4734
+ function editDistance2(a, b, max) {
4735
+ if (Math.abs(a.length - b.length) > max) {
4736
+ return max + 1;
4737
+ }
4738
+ const prev = new Array(b.length + 1);
4739
+ const curr = new Array(b.length + 1);
4740
+ for (let j = 0; j <= b.length; j++) {
4741
+ prev[j] = j;
4742
+ }
4743
+ for (let i = 1; i <= a.length; i++) {
4744
+ curr[0] = i;
4745
+ for (let j = 1; j <= b.length; j++) {
4746
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
4747
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
4748
+ }
4749
+ for (let j = 0; j <= b.length; j++) {
4750
+ prev[j] = curr[j];
4751
+ }
4752
+ }
4753
+ return prev[b.length];
4754
+ }
4755
+ function didYouMean(input, candidates) {
4756
+ if (input.length === 0) {
4757
+ return "";
4758
+ }
4759
+ const folded = input.toLowerCase();
4760
+ let best = null;
4761
+ let bestDistance = 3;
4762
+ for (const candidate of candidates) {
4763
+ const distance = editDistance2(folded, candidate.toLowerCase(), 2);
4764
+ if (distance < bestDistance) {
4765
+ best = candidate;
4766
+ bestDistance = distance;
4767
+ }
4768
+ }
4769
+ return best !== null ? ` Did you mean "${best}"?` : "";
4770
+ }
4771
+ var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
4772
+ function raiseError2(message) {
4773
+ throw new Error(`[@wcstack/state] ${message}`);
4774
+ }
4775
+ var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
4776
+ "if",
4777
+ "elseif",
4778
+ "else",
4779
+ "for"
4780
+ ]);
4781
+ var _config2 = {
4782
+ locale: "en"
4783
+ };
4784
+ var config2 = _config2;
4785
+ function optionsRequired2(fnName) {
4786
+ raiseError2(`filter ${fnName} requires at least one option`);
4787
+ }
4788
+ function optionMustBeNumber2(fnName) {
4789
+ raiseError2(`filter ${fnName} requires a number as option`);
4790
+ }
4791
+ function valueMustBeNumber2(fnName) {
4792
+ raiseError2(`filter ${fnName} requires a number value`);
4793
+ }
4794
+ function valueMustBeBoolean2(fnName) {
4795
+ raiseError2(`filter ${fnName} requires a boolean value`);
4796
+ }
4797
+ function valueMustBeDate2(fnName) {
4798
+ raiseError2(`filter ${fnName} requires a date value`);
4799
+ }
4800
+ function valueMustBeArray2(fnName) {
4801
+ raiseError2(`filter ${fnName} requires an array value`);
4802
+ }
4803
+ function validateNumberString2(value) {
4804
+ if (!value || isNaN(Number(value))) {
4805
+ return false;
4806
+ }
4807
+ return true;
4808
+ }
4809
+ var eq2 = (options) => {
4810
+ const opt = options?.[0] ?? optionsRequired2("eq");
4811
+ return (value) => {
4812
+ if (typeof value === "number") {
4813
+ if (!validateNumberString2(opt)) {
4814
+ optionMustBeNumber2("eq");
4815
+ }
4816
+ return value === Number(opt);
4817
+ }
4818
+ if (typeof value === "string") {
4819
+ return value === opt;
4820
+ }
4821
+ return value === opt;
4822
+ };
4823
+ };
4824
+ var ne2 = (options) => {
4825
+ const opt = options?.[0] ?? optionsRequired2("ne");
4826
+ return (value) => {
4827
+ if (typeof value === "number") {
4828
+ if (!validateNumberString2(opt)) {
4829
+ optionMustBeNumber2("ne");
4830
+ }
4831
+ return value !== Number(opt);
4832
+ }
4833
+ if (typeof value === "string") {
4834
+ return value !== opt;
4835
+ }
4836
+ return value !== opt;
4837
+ };
4838
+ };
4839
+ var not2 = (_options) => {
4840
+ return (value) => {
4841
+ if (typeof value !== "boolean") {
4842
+ valueMustBeBoolean2("not");
4843
+ }
4844
+ return !value;
4845
+ };
4846
+ };
4847
+ var lt2 = (options) => {
4848
+ const opt = options?.[0] ?? optionsRequired2("lt");
4849
+ if (!validateNumberString2(opt)) {
4850
+ optionMustBeNumber2("lt");
4851
+ }
4852
+ return (value) => {
4853
+ if (typeof value !== "number") {
4854
+ valueMustBeNumber2("lt");
4855
+ }
4856
+ return value < Number(opt);
4857
+ };
4858
+ };
4859
+ var le2 = (options) => {
4860
+ const opt = options?.[0] ?? optionsRequired2("le");
4861
+ if (!validateNumberString2(opt)) {
4862
+ optionMustBeNumber2("le");
4863
+ }
4864
+ return (value) => {
4865
+ if (typeof value !== "number") {
4866
+ valueMustBeNumber2("le");
4867
+ }
4868
+ return value <= Number(opt);
4869
+ };
4870
+ };
4871
+ var gt2 = (options) => {
4872
+ const opt = options?.[0] ?? optionsRequired2("gt");
4873
+ if (!validateNumberString2(opt)) {
4874
+ optionMustBeNumber2("gt");
4875
+ }
4876
+ return (value) => {
4877
+ if (typeof value !== "number") {
4878
+ valueMustBeNumber2("gt");
4879
+ }
4880
+ return value > Number(opt);
4881
+ };
4882
+ };
4883
+ var ge2 = (options) => {
4884
+ const opt = options?.[0] ?? optionsRequired2("ge");
4885
+ if (!validateNumberString2(opt)) {
4886
+ optionMustBeNumber2("ge");
4887
+ }
4888
+ return (value) => {
4889
+ if (typeof value !== "number") {
4890
+ valueMustBeNumber2("ge");
4891
+ }
4892
+ return value >= Number(opt);
4893
+ };
4894
+ };
4895
+ var inc2 = (options) => {
4896
+ const opt = options?.[0] ?? optionsRequired2("inc");
4897
+ if (!validateNumberString2(opt)) {
4898
+ optionMustBeNumber2("inc");
4899
+ }
4900
+ return (value) => {
4901
+ if (typeof value !== "number") {
4902
+ valueMustBeNumber2("inc");
4903
+ }
4904
+ return value + Number(opt);
4905
+ };
4906
+ };
4907
+ var dec2 = (options) => {
4908
+ const opt = options?.[0] ?? optionsRequired2("dec");
4909
+ if (!validateNumberString2(opt)) {
4910
+ optionMustBeNumber2("dec");
4911
+ }
4912
+ return (value) => {
4913
+ if (typeof value !== "number") {
4914
+ valueMustBeNumber2("dec");
4915
+ }
4916
+ return value - Number(opt);
4917
+ };
4918
+ };
4919
+ var mul2 = (options) => {
4920
+ const opt = options?.[0] ?? optionsRequired2("mul");
4921
+ if (!validateNumberString2(opt)) {
4922
+ optionMustBeNumber2("mul");
4923
+ }
4924
+ return (value) => {
4925
+ if (typeof value !== "number") {
4926
+ valueMustBeNumber2("mul");
4927
+ }
4928
+ return value * Number(opt);
4929
+ };
4930
+ };
4931
+ var div2 = (options) => {
4932
+ const opt = options?.[0] ?? optionsRequired2("div");
4933
+ if (!validateNumberString2(opt)) {
4934
+ optionMustBeNumber2("div");
4935
+ }
4936
+ return (value) => {
4937
+ if (typeof value !== "number") {
4938
+ valueMustBeNumber2("div");
4939
+ }
4940
+ return value / Number(opt);
4941
+ };
4942
+ };
4943
+ var mod2 = (options) => {
4944
+ const opt = options?.[0] ?? optionsRequired2("mod");
4945
+ if (!validateNumberString2(opt)) {
4946
+ optionMustBeNumber2("mod");
4947
+ }
4948
+ return (value) => {
4949
+ if (typeof value !== "number") {
4950
+ valueMustBeNumber2("mod");
4951
+ }
4952
+ return value % Number(opt);
4953
+ };
4954
+ };
4955
+ var abs2 = (_options) => {
4956
+ return (value) => {
4957
+ if (typeof value !== "number") {
4958
+ valueMustBeNumber2("abs");
4959
+ }
4960
+ return Math.abs(value);
4961
+ };
4962
+ };
4963
+ var clamp2 = (options) => {
4964
+ const opt1 = options?.[0] ?? optionsRequired2("clamp");
4965
+ if (!validateNumberString2(opt1)) {
4966
+ optionMustBeNumber2("clamp");
4967
+ }
4968
+ const opt2 = options?.[1] ?? optionsRequired2("clamp");
4969
+ if (!validateNumberString2(opt2)) {
4970
+ optionMustBeNumber2("clamp");
4971
+ }
4972
+ const min = Number(opt1);
4973
+ const max = Number(opt2);
4974
+ return (value) => {
4975
+ if (typeof value !== "number") {
4976
+ valueMustBeNumber2("clamp");
4977
+ }
4978
+ return Math.min(Math.max(value, min), max);
4979
+ };
4980
+ };
4981
+ var fix2 = (options) => {
4982
+ const opt = options?.[0] ?? "0";
4983
+ if (!validateNumberString2(opt)) {
4984
+ optionMustBeNumber2("fix");
4985
+ }
4986
+ return (value) => {
4987
+ if (typeof value !== "number") {
4988
+ valueMustBeNumber2("fix");
4989
+ }
4990
+ return value.toFixed(Number(opt));
4991
+ };
4992
+ };
4993
+ var locale2 = (options) => {
4994
+ const explicit = options?.[0];
4995
+ return (value) => {
4996
+ if (typeof value !== "number") {
4997
+ valueMustBeNumber2("locale");
4998
+ }
4999
+ return value.toLocaleString(explicit ?? config2.locale);
5000
+ };
5001
+ };
5002
+ var uc2 = (_options) => {
5003
+ return (value) => {
5004
+ return String(value).toUpperCase();
5005
+ };
5006
+ };
5007
+ var lc2 = (_options) => {
5008
+ return (value) => {
5009
+ return String(value).toLowerCase();
5010
+ };
5011
+ };
5012
+ var cap2 = (_options) => {
5013
+ return (value) => {
5014
+ const v = String(value);
5015
+ if (v.length === 0) {
5016
+ return v;
5017
+ }
5018
+ if (v.length === 1) {
5019
+ return v.toUpperCase();
5020
+ }
5021
+ return v.charAt(0).toUpperCase() + v.slice(1);
5022
+ };
5023
+ };
5024
+ var trim2 = (_options) => {
5025
+ return (value) => {
5026
+ return String(value).trim();
5027
+ };
5028
+ };
5029
+ var slice2 = (options) => {
5030
+ const numberedOpts = [];
5031
+ const opt1 = options?.[0] ?? optionsRequired2("slice");
5032
+ if (!validateNumberString2(opt1)) {
5033
+ optionMustBeNumber2("slice");
5034
+ }
5035
+ numberedOpts.push(Number(opt1));
5036
+ const opt2 = options?.[1];
5037
+ if (typeof opt2 !== "undefined") {
5038
+ if (!validateNumberString2(opt2)) {
5039
+ optionMustBeNumber2("slice");
5040
+ }
5041
+ numberedOpts.push(Number(opt2));
5042
+ }
5043
+ return (value) => {
5044
+ return String(value).slice(...numberedOpts);
5045
+ };
5046
+ };
5047
+ var substr2 = (options) => {
5048
+ const opt1 = options?.[0] ?? optionsRequired2("substr");
5049
+ if (!validateNumberString2(opt1)) {
5050
+ optionMustBeNumber2("substr");
5051
+ }
5052
+ const opt2 = options?.[1] ?? optionsRequired2("substr");
5053
+ if (!validateNumberString2(opt2)) {
5054
+ optionMustBeNumber2("substr");
5055
+ }
5056
+ return (value) => {
5057
+ return String(value).substr(Number(opt1), Number(opt2));
5058
+ };
5059
+ };
5060
+ var pad2 = (options) => {
5061
+ const opt1 = options?.[0] ?? optionsRequired2("pad");
5062
+ if (!validateNumberString2(opt1)) {
5063
+ optionMustBeNumber2("pad");
5064
+ }
5065
+ const opt2 = options?.[1] ?? "0";
5066
+ return (value) => {
5067
+ return String(value).padStart(Number(opt1), opt2);
5068
+ };
5069
+ };
5070
+ var rep2 = (options) => {
5071
+ const opt = options?.[0] ?? optionsRequired2("rep");
5072
+ if (!validateNumberString2(opt)) {
5073
+ optionMustBeNumber2("rep");
5074
+ }
5075
+ return (value) => {
5076
+ return String(value).repeat(Number(opt));
5077
+ };
5078
+ };
5079
+ var rev2 = (_options) => {
5080
+ return (value) => {
5081
+ return String(value).split("").reverse().join("");
5082
+ };
5083
+ };
5084
+ var int2 = (_options) => {
5085
+ return (value) => {
5086
+ return parseInt(String(value), 10);
5087
+ };
5088
+ };
5089
+ var float2 = (_options) => {
5090
+ return (value) => {
5091
+ return parseFloat(String(value));
5092
+ };
5093
+ };
5094
+ var round2 = (options) => {
5095
+ const opt = options?.[0] ?? "0";
5096
+ if (!validateNumberString2(opt)) {
5097
+ optionMustBeNumber2("round");
5098
+ }
5099
+ return (value) => {
5100
+ if (typeof value !== "number") {
5101
+ valueMustBeNumber2("round");
5102
+ }
5103
+ const optValue = Math.pow(10, Number(opt));
5104
+ return Math.round(value * optValue) / optValue;
5105
+ };
5106
+ };
5107
+ var floor2 = (options) => {
5108
+ const opt = options?.[0] ?? "0";
5109
+ if (!validateNumberString2(opt)) {
5110
+ optionMustBeNumber2("floor");
5111
+ }
5112
+ return (value) => {
5113
+ if (typeof value !== "number") {
5114
+ valueMustBeNumber2("floor");
5115
+ }
5116
+ const optValue = Math.pow(10, Number(opt));
5117
+ return Math.floor(value * optValue) / optValue;
5118
+ };
5119
+ };
5120
+ var ceil2 = (options) => {
5121
+ const opt = options?.[0] ?? "0";
5122
+ if (!validateNumberString2(opt)) {
5123
+ optionMustBeNumber2("ceil");
5124
+ }
5125
+ return (value) => {
5126
+ if (typeof value !== "number") {
5127
+ valueMustBeNumber2("ceil");
5128
+ }
5129
+ const optValue = Math.pow(10, Number(opt));
5130
+ return Math.ceil(value * optValue) / optValue;
5131
+ };
5132
+ };
5133
+ var percent2 = (options) => {
5134
+ const opt = options?.[0] ?? "0";
5135
+ if (!validateNumberString2(opt)) {
5136
+ optionMustBeNumber2("percent");
5137
+ }
5138
+ return (value) => {
5139
+ if (typeof value !== "number") {
5140
+ valueMustBeNumber2("percent");
5141
+ }
5142
+ return `${(value * 100).toFixed(Number(opt))}%`;
5143
+ };
5144
+ };
5145
+ var unit2 = (options) => {
5146
+ const opt = options?.[0] ?? optionsRequired2("unit");
5147
+ return (value) => {
5148
+ if (value === null || typeof value === "undefined") {
5149
+ return value;
5150
+ }
5151
+ return String(value) + opt;
5152
+ };
5153
+ };
5154
+ var join2 = (options) => {
5155
+ const opt = options?.[0] ?? ", ";
5156
+ return (value) => {
5157
+ if (!Array.isArray(value)) {
5158
+ valueMustBeArray2("join");
5159
+ }
5160
+ return value.join(opt);
5161
+ };
5162
+ };
5163
+ var truncate2 = (options) => {
5164
+ const opt1 = options?.[0] ?? optionsRequired2("truncate");
5165
+ if (!validateNumberString2(opt1)) {
5166
+ optionMustBeNumber2("truncate");
5167
+ }
5168
+ const maxLength = Number(opt1);
5169
+ const suffix = options?.[1] ?? "\u2026";
5170
+ return (value) => {
5171
+ const v = String(value);
5172
+ if (v.length <= maxLength) {
5173
+ return v;
5174
+ }
5175
+ return v.slice(0, maxLength) + suffix;
5176
+ };
5177
+ };
5178
+ var date2 = (options) => {
5179
+ const explicit = options?.[0];
5180
+ return (value) => {
5181
+ if (!(value instanceof Date)) {
5182
+ valueMustBeDate2("date");
5183
+ }
5184
+ return value.toLocaleDateString(explicit ?? config2.locale);
5185
+ };
5186
+ };
5187
+ var time2 = (options) => {
5188
+ const explicit = options?.[0];
5189
+ return (value) => {
5190
+ if (!(value instanceof Date)) {
5191
+ valueMustBeDate2("time");
5192
+ }
5193
+ return value.toLocaleTimeString(explicit ?? config2.locale);
5194
+ };
5195
+ };
5196
+ var datetime2 = (options) => {
5197
+ const explicit = options?.[0];
5198
+ return (value) => {
5199
+ if (!(value instanceof Date)) {
5200
+ valueMustBeDate2("datetime");
5201
+ }
5202
+ return value.toLocaleString(explicit ?? config2.locale);
5203
+ };
5204
+ };
5205
+ var ymd2 = (options) => {
5206
+ const opt = options?.[0] ?? "-";
5207
+ return (value) => {
5208
+ if (!(value instanceof Date)) {
5209
+ valueMustBeDate2("ymd");
5210
+ }
5211
+ const year = value.getFullYear().toString();
5212
+ const month = (value.getMonth() + 1).toString().padStart(2, "0");
5213
+ const day = value.getDate().toString().padStart(2, "0");
5214
+ return `${year}${opt}${month}${opt}${day}`;
5215
+ };
5216
+ };
5217
+ var hms2 = (options) => {
5218
+ const opt = options?.[0] ?? ":";
5219
+ return (value) => {
5220
+ if (!(value instanceof Date)) {
5221
+ valueMustBeDate2("hms");
5222
+ }
5223
+ const hours = value.getHours().toString().padStart(2, "0");
5224
+ const minutes = value.getMinutes().toString().padStart(2, "0");
5225
+ const seconds = value.getSeconds().toString().padStart(2, "0");
5226
+ return `${hours}${opt}${minutes}${opt}${seconds}`;
5227
+ };
5228
+ };
5229
+ var falsy2 = (_options) => {
5230
+ return (value) => value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value);
5231
+ };
5232
+ var truthy2 = (_options) => {
5233
+ return (value) => value !== false && value !== null && value !== void 0 && value !== 0 && value !== "" && !Number.isNaN(value);
5234
+ };
5235
+ var defaults2 = (options) => {
5236
+ const opt = options?.[0] ?? optionsRequired2("defaults");
5237
+ return (value) => {
5238
+ if (value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value)) {
5239
+ return opt;
5240
+ }
5241
+ return value;
5242
+ };
5243
+ };
5244
+ var boolean2 = (_options) => {
5245
+ return (value) => {
5246
+ return Boolean(value);
5247
+ };
5248
+ };
5249
+ var number2 = (_options) => {
5250
+ return (value) => {
5251
+ return Number(value);
5252
+ };
5253
+ };
5254
+ var string2 = (_options) => {
5255
+ return (value) => {
5256
+ return String(value);
5257
+ };
5258
+ };
5259
+ var _null2 = (_options) => {
5260
+ return (value) => {
5261
+ return value === "" ? null : value;
5262
+ };
5263
+ };
5264
+ var builtinFilters2 = {
5265
+ "eq": eq2,
5266
+ "ne": ne2,
5267
+ "not": not2,
5268
+ "lt": lt2,
5269
+ "le": le2,
5270
+ "gt": gt2,
5271
+ "ge": ge2,
5272
+ "inc": inc2,
5273
+ "dec": dec2,
5274
+ "mul": mul2,
5275
+ "div": div2,
5276
+ "mod": mod2,
5277
+ "abs": abs2,
5278
+ "clamp": clamp2,
5279
+ "fix": fix2,
5280
+ "locale": locale2,
5281
+ "uc": uc2,
5282
+ "lc": lc2,
5283
+ "cap": cap2,
5284
+ "trim": trim2,
5285
+ "slice": slice2,
5286
+ "substr": substr2,
5287
+ "pad": pad2,
5288
+ "rep": rep2,
5289
+ "rev": rev2,
5290
+ "truncate": truncate2,
5291
+ "join": join2,
5292
+ "int": int2,
5293
+ "float": float2,
5294
+ "round": round2,
5295
+ "floor": floor2,
5296
+ "ceil": ceil2,
5297
+ "percent": percent2,
5298
+ "unit": unit2,
5299
+ "date": date2,
5300
+ "time": time2,
5301
+ "datetime": datetime2,
5302
+ "ymd": ymd2,
5303
+ "hms": hms2,
5304
+ "falsy": falsy2,
5305
+ "truthy": truthy2,
5306
+ "defaults": defaults2,
5307
+ "boolean": boolean2,
5308
+ "number": number2,
5309
+ "string": string2,
5310
+ "null": _null2
5311
+ };
5312
+ var outputBuiltinFilters2 = builtinFilters2;
5313
+ var inputBuiltinFilters = builtinFilters2;
5314
+ var builtinFiltersByFilterIOType = {
5315
+ "input": inputBuiltinFilters,
5316
+ "output": outputBuiltinFilters2
5317
+ };
5318
+ var builtinFilterFn = (name, options) => (filters) => {
5319
+ const filter = filters[name];
5320
+ if (!filter) {
5321
+ raiseError2(`[wcs/filter-unknown] filter not found: ${name}.${didYouMean(name, Object.keys(filters))}${LINT_HINT}`);
5322
+ }
5323
+ return filter(options);
5324
+ };
5325
+ function finalizeArg(text, firstQuoteStart, lastQuoteEnd) {
5326
+ const startLimit = firstQuoteStart === -1 ? text.length : firstQuoteStart;
5327
+ let start = 0;
5328
+ while (start < startLimit && /\s/.test(text[start])) {
5329
+ start++;
5330
+ }
5331
+ const endLimit = lastQuoteEnd === -1 ? 0 : lastQuoteEnd;
5332
+ let end = text.length;
5333
+ while (end > endLimit && /\s/.test(text[end - 1])) {
5334
+ end--;
5335
+ }
5336
+ return text.slice(start, end);
5337
+ }
5338
+ function parseFilterArgs(argsText) {
5339
+ const args = [];
5340
+ let current = "";
5341
+ let inQuote = null;
5342
+ let hasQuote = false;
5343
+ let firstQuoteStart = -1;
5344
+ let lastQuoteEnd = -1;
5345
+ const flush = () => {
5346
+ args.push(finalizeArg(current, firstQuoteStart, lastQuoteEnd));
5347
+ current = "";
5348
+ hasQuote = false;
5349
+ firstQuoteStart = -1;
5350
+ lastQuoteEnd = -1;
5351
+ };
5352
+ for (let i = 0; i < argsText.length; i++) {
5353
+ const char = argsText[i];
5354
+ if (inQuote) {
5355
+ if (char === inQuote) {
5356
+ inQuote = null;
5357
+ } else {
5358
+ if (firstQuoteStart === -1) {
5359
+ firstQuoteStart = current.length;
5360
+ }
5361
+ current += char;
5362
+ lastQuoteEnd = current.length;
5363
+ }
5364
+ } else if (char === '"' || char === "'") {
5365
+ inQuote = char;
5366
+ hasQuote = true;
5367
+ } else if (char === ",") {
5368
+ flush();
5369
+ } else {
5370
+ current += char;
5371
+ }
5372
+ }
5373
+ const last = finalizeArg(current, firstQuoteStart, lastQuoteEnd);
5374
+ if (last || hasQuote) {
5375
+ args.push(last);
5376
+ }
5377
+ return args;
5378
+ }
5379
+ var filterFnByKey = /* @__PURE__ */ new Map();
5380
+ function clearFilterFnCacheForTooling() {
5381
+ filterFnByKey.clear();
5382
+ }
5383
+ function parseFilters(filterTextList, filterIOType) {
5384
+ const builtinFilters3 = builtinFiltersByFilterIOType[filterIOType];
5385
+ const filters = filterTextList.map((filterText) => {
5386
+ const openParenIndex = filterText.indexOf("(");
5387
+ const closeParenIndex = filterText.lastIndexOf(")");
5388
+ if (openParenIndex !== -1 && closeParenIndex === -1) {
5389
+ raiseError2(`Invalid filter format: missing closing parenthesis in "${filterText}"`);
5390
+ }
5391
+ if (closeParenIndex !== -1 && openParenIndex === -1) {
5392
+ raiseError2(`Invalid filter format: missing opening parenthesis in "${filterText}"`);
5393
+ }
5394
+ if (openParenIndex === -1) {
5395
+ const filterName = filterText.trim();
5396
+ const filterKey = `${filterName}():${filterIOType}`;
5397
+ let filterFn = filterFnByKey.get(filterKey);
5398
+ if (typeof filterFn === "undefined") {
5399
+ filterFn = builtinFilterFn(filterName, [])(builtinFilters3);
5400
+ filterFnByKey.set(filterKey, filterFn);
5401
+ }
5402
+ return {
5403
+ filterName,
5404
+ args: [],
5405
+ filterFn
5406
+ };
5407
+ } else {
5408
+ const argsText = filterText.substring(openParenIndex + 1, closeParenIndex);
5409
+ const filterName = filterText.substring(0, openParenIndex).trim();
5410
+ const args = parseFilterArgs(argsText);
5411
+ const filterKey = `${filterName}(${args.join(",")}):${filterIOType}`;
5412
+ let filterFn = filterFnByKey.get(filterKey);
5413
+ if (typeof filterFn === "undefined") {
5414
+ filterFn = builtinFilterFn(filterName, args)(builtinFilters3);
5415
+ filterFnByKey.set(filterKey, filterFn);
5416
+ }
5417
+ return {
5418
+ filterName,
5419
+ args,
5420
+ filterFn
5421
+ };
5422
+ }
5423
+ });
5424
+ return filters;
5425
+ }
5426
+ var trimFn = (s) => s.trim();
5427
+ var cacheFilterInfos$1 = /* @__PURE__ */ new Map();
5428
+ function clearPropPartCacheForTooling() {
5429
+ cacheFilterInfos$1.clear();
5430
+ }
5431
+ function parsePropPart(propPart) {
5432
+ const pos = propPart.indexOf(FILTER_SEPARATOR2);
5433
+ let propText = "";
5434
+ let filterTexts = [];
5435
+ let filtersText = "";
5436
+ let filters = [];
5437
+ if (pos !== -1) {
5438
+ propText = propPart.slice(0, pos).trim();
5439
+ filtersText = propPart.slice(pos + 1).trim();
5440
+ if (cacheFilterInfos$1.has(filtersText)) {
5441
+ filters = cacheFilterInfos$1.get(filtersText);
5442
+ } else {
5443
+ filterTexts = filtersText.split(FILTER_SEPARATOR2).map(trimFn);
5444
+ filters = parseFilters(filterTexts, "input");
5445
+ cacheFilterInfos$1.set(filtersText, filters);
5446
+ }
5447
+ } else {
5448
+ propText = propPart.trim();
5449
+ }
5450
+ const [propName, propModifiersText] = propText.split(MODIFIER_SEPARATOR2).map(trimFn);
5451
+ const propSegments = propName.split(DELIMITER2).map(trimFn);
5452
+ const propModifiers = propModifiersText ? propModifiersText.split(",").map(trimFn) : [];
5453
+ return {
5454
+ propName,
5455
+ propSegments,
5456
+ propModifiers,
5457
+ inFilters: filters
5458
+ };
5459
+ }
5460
+ var cacheFilterInfos = /* @__PURE__ */ new Map();
5461
+ function clearStatePartCacheForTooling() {
5462
+ cacheFilterInfos.clear();
5463
+ }
5464
+ function parseStatePart(statePart) {
5465
+ const pos = statePart.indexOf(FILTER_SEPARATOR2);
5466
+ let stateAndPath = "";
5467
+ let filterTexts = [];
5468
+ let filtersText = "";
5469
+ let filters = [];
5470
+ if (pos !== -1) {
5471
+ stateAndPath = statePart.slice(0, pos).trim();
5472
+ filtersText = statePart.slice(pos + 1).trim();
5473
+ if (cacheFilterInfos.has(filtersText)) {
5474
+ filters = cacheFilterInfos.get(filtersText);
5475
+ } else {
5476
+ filterTexts = filtersText.split(FILTER_SEPARATOR2).map(trimFn);
5477
+ filters = parseFilters(filterTexts, "output");
5478
+ cacheFilterInfos.set(filtersText, filters);
5479
+ }
5480
+ } else {
5481
+ stateAndPath = statePart.trim();
5482
+ }
5483
+ const [statePathName, stateName = "default"] = stateAndPath.split(STATE_NAME_SEPARATOR3).map(trimFn);
5484
+ const pathInfo = getPathInfo(statePathName);
5485
+ return {
5486
+ stateName,
5487
+ statePathName,
5488
+ statePathInfo: pathInfo,
5489
+ outFilters: filters
5490
+ };
5491
+ }
5492
+ function parseBindTextsForElement(bindText) {
5493
+ const [...bindTexts] = bindText.split(BINDING_SEPARATOR2).map(trimFn).filter((s) => s.length > 0);
5494
+ const results = bindTexts.map((bindText2) => {
5495
+ const separatorIndex = bindText2.indexOf(PROP_VALUE_SEPARATOR2);
5496
+ if (separatorIndex === -1) {
5497
+ raiseError2(`Invalid bindText: "${bindText2}". Missing ':' separator between propPart and statePart.`);
5498
+ }
5499
+ const propPart = bindText2.slice(0, separatorIndex).trim();
5500
+ const statePart = bindText2.slice(separatorIndex + 1).trim();
5501
+ if (propPart === ELSE_KEYWORD2) {
5502
+ const pathInfo = getPathInfo("#else");
5503
+ return {
5504
+ propName: ELSE_KEYWORD2,
5505
+ propSegments: [ELSE_KEYWORD2],
5506
+ propModifiers: [],
5507
+ statePathName: "#else",
5508
+ statePathInfo: pathInfo,
5509
+ stateName: "",
5510
+ inFilters: [],
5511
+ outFilters: [],
5512
+ bindingType: "else"
5513
+ };
5514
+ } else if (propPart === SPREAD_PROP2) {
5515
+ const stateResult = parseStatePart(statePart);
5516
+ if (stateResult.outFilters.length > 0) {
5517
+ raiseError2(`Invalid spread binding "${bindText2}": filters are not allowed on spread targets.`);
5518
+ }
5519
+ if (stateResult.statePathName.length === 0) {
5520
+ raiseError2(`Invalid spread binding "${bindText2}": spread target path is required.`);
5521
+ }
5522
+ return {
5523
+ propName: SPREAD_PROP2,
5524
+ propSegments: [SPREAD_PROP2],
5525
+ propModifiers: [],
5526
+ inFilters: [],
5527
+ ...stateResult,
5528
+ bindingType: "spread"
5529
+ };
5530
+ } else if (propPart === "if" || propPart === "elseif" || propPart === "for" || propPart === "radio" || propPart === "checkbox") {
5531
+ const stateResult = parseStatePart(statePart);
5532
+ return {
5533
+ propName: propPart,
5534
+ propSegments: [propPart],
5535
+ propModifiers: [],
5536
+ inFilters: [],
5537
+ ...stateResult,
5538
+ bindingType: propPart
5539
+ };
5540
+ } else {
5541
+ const stateResult = parseStatePart(statePart);
5542
+ const propResult = parsePropPart(propPart);
5543
+ if (propResult.propSegments[0] === EVENT_TOKEN_NAMESPACE2) {
5544
+ return {
5545
+ ...propResult,
5546
+ ...stateResult,
5547
+ bindingType: "event"
5548
+ };
5549
+ }
5550
+ if (propResult.propSegments[0].startsWith(EVENT_PROP_PREFIX2)) {
5551
+ return {
5552
+ ...propResult,
5553
+ ...stateResult,
5554
+ bindingType: "event"
5555
+ };
5556
+ } else {
5557
+ return {
5558
+ ...propResult,
5559
+ ...stateResult,
5560
+ bindingType: "prop"
5561
+ };
5562
+ }
5563
+ }
5564
+ });
5565
+ if (results.length > 1) {
5566
+ const isIncludeSingleBinding = results.some((r) => STRUCTURAL_BINDING_TYPE_SET2.has(r.bindingType));
5567
+ if (isIncludeSingleBinding) {
5568
+ raiseError2(`[wcs/template-syntax] Invalid bindText: "${bindText}". 'if', 'elseif', 'else', and 'for' bindings must be single binding. Put the structural binding alone in its own data-wcs (e.g. <template data-wcs="for: items">).${LINT_HINT}`);
5569
+ }
5570
+ }
5571
+ return results;
5572
+ }
5573
+ function parseBindTextForEmbeddedNode(bindText) {
5574
+ const stateResult = parseStatePart(bindText);
5575
+ return {
5576
+ propName: "textContent",
5577
+ propSegments: ["textContent"],
5578
+ propModifiers: [],
5579
+ inFilters: [],
5580
+ ...stateResult,
5581
+ bindingType: "text"
5582
+ };
5583
+ }
5584
+ function clearParserCaches() {
5585
+ clearPathInfoCacheForTooling();
5586
+ clearPropPartCacheForTooling();
5587
+ clearStatePartCacheForTooling();
5588
+ clearFilterFnCacheForTooling();
5589
+ }
5590
+
5591
+ // src/core/parser/positionalParser.ts
5592
+ var { delimiters } = getWcsManifest().syntax;
5593
+ function locate(haystack, needle, from, to) {
5594
+ if (needle.length === 0) return null;
5595
+ const index = haystack.indexOf(needle, from);
5596
+ if (index === -1 || index + needle.length > to) return null;
5597
+ return { start: index, end: index + needle.length };
5598
+ }
5599
+ function parseEmbeddedTextWithPositions(expression) {
5600
+ const exprRange = { start: 0, end: expression.length };
5601
+ let parsed = null;
5602
+ let error = null;
5603
+ try {
5604
+ parsed = parseBindTextForEmbeddedNode(expression);
5605
+ } catch (e) {
5606
+ error = e.message;
5607
+ }
5608
+ if (parsed === null) {
5609
+ return { exprRange, exprText: expression, parsed, error, propRange: null, pathRange: null, stateNameRange: null };
5610
+ }
5611
+ const firstPipe = expression.indexOf(delimiters.filter);
5612
+ const pathScopeEnd = firstPipe === -1 ? expression.length : firstPipe;
5613
+ const pathLocal = locate(expression, parsed.statePathName, 0, pathScopeEnd);
5614
+ let stateNameLocal = null;
5615
+ const at = expression.indexOf(delimiters.stateName);
5616
+ if (at !== -1 && at < pathScopeEnd) {
5617
+ stateNameLocal = locate(expression, parsed.stateName, at + 1, pathScopeEnd);
5618
+ }
5619
+ return {
5620
+ exprRange,
5621
+ exprText: expression,
5622
+ parsed,
5623
+ error,
5624
+ propRange: null,
5625
+ pathRange: pathLocal,
5626
+ stateNameRange: stateNameLocal
5627
+ };
5628
+ }
5629
+ function parseBindTextWithPositions(bindText) {
5630
+ const results = [];
5631
+ const segments = bindText.split(delimiters.binding);
5632
+ let segmentStart = 0;
5633
+ for (const segment of segments) {
5634
+ const leading = segment.length - segment.trimStart().length;
5635
+ const expr = segment.trim();
5636
+ const exprStart = segmentStart + leading;
5637
+ segmentStart += segment.length + delimiters.binding.length;
5638
+ if (expr.length === 0) continue;
5639
+ const exprRange = { start: exprStart, end: exprStart + expr.length };
5640
+ let parsed = null;
5641
+ let error = null;
5642
+ try {
5643
+ parsed = parseBindTextsForElement(expr)[0] ?? null;
5644
+ } catch (e) {
5645
+ error = e.message;
5646
+ }
5647
+ if (parsed === null) {
5648
+ results.push({ exprRange, exprText: expr, parsed, error, propRange: null, pathRange: null, stateNameRange: null });
5649
+ continue;
5650
+ }
5651
+ const colon = expr.indexOf(delimiters.propValue);
5652
+ const propEndLimit = colon === -1 ? expr.length : colon;
5653
+ const propLocal = locate(expr, parsed.propName, 0, propEndLimit);
5654
+ let pathLocal = null;
5655
+ let stateNameLocal = null;
5656
+ if (colon !== -1) {
5657
+ const stateBase = colon + 1;
5658
+ const firstPipe = expr.indexOf(delimiters.filter, stateBase);
5659
+ const pathScopeEnd = firstPipe === -1 ? expr.length : firstPipe;
5660
+ pathLocal = locate(expr, parsed.statePathName, stateBase, pathScopeEnd);
5661
+ const at = expr.indexOf(delimiters.stateName, stateBase);
5662
+ if (at !== -1 && at < pathScopeEnd) {
5663
+ stateNameLocal = locate(expr, parsed.stateName, at + 1, pathScopeEnd);
5664
+ }
5665
+ }
5666
+ const lift = (range) => range === null ? null : { start: exprStart + range.start, end: exprStart + range.end };
5667
+ results.push({
5668
+ exprRange,
5669
+ exprText: expr,
5670
+ parsed,
5671
+ error,
5672
+ propRange: lift(propLocal),
5673
+ pathRange: lift(pathLocal),
5674
+ stateNameRange: lift(stateNameLocal)
5675
+ });
5676
+ }
5677
+ return results;
5678
+ }
5679
+
5680
+ // src/core/index/referenceIndex.ts
5681
+ function keyOf(stateName, path) {
5682
+ return `${stateName}\0${path}`;
5683
+ }
5684
+ function buildReferenceIndex(html, options = {}) {
5685
+ clearParserCaches();
5686
+ const bindAttribute = options.bindAttribute ?? "data-wcs";
5687
+ const stateTagName = options.stateTagName ?? "wcs-state";
5688
+ const occurrences = [];
5689
+ const problems = [];
5690
+ for (const attr of findAllBindAttributes(html, bindAttribute)) {
5691
+ for (const binding of parseBindTextWithPositions(attr.value)) {
5692
+ const lift = (range) => ({ start: attr.valueStart + range.start, end: attr.valueStart + range.end });
5693
+ if (binding.parsed === null) {
5694
+ problems.push({ message: binding.error ?? "parse error", range: lift(binding.exprRange) });
5695
+ continue;
5696
+ }
5697
+ if (binding.pathRange === null) continue;
5698
+ occurrences.push({
5699
+ source: "attribute",
5700
+ kind: binding.parsed.propSegments[0] === "eventToken" ? "eventToken" : "path",
5701
+ stateName: binding.parsed.stateName,
5702
+ path: binding.parsed.statePathName,
5703
+ pathRange: lift(binding.pathRange),
5704
+ exprRange: lift(binding.exprRange),
5705
+ propName: binding.parsed.propName,
5706
+ propRange: binding.propRange === null ? null : lift(binding.propRange),
5707
+ stateNameRange: binding.stateNameRange === null ? null : lift(binding.stateNameRange),
5708
+ bindingType: binding.parsed.bindingType
5709
+ });
5710
+ }
5711
+ }
5712
+ const textMatches = [
5713
+ ...findAllMustacheSyntax(html),
5714
+ ...findAllCommentBindings(html)
5715
+ ];
5716
+ for (const match of textMatches) {
5717
+ const binding = parseEmbeddedTextWithPositions(match.expression);
5718
+ const shift = (range) => ({
5719
+ start: match.exprStart + range.start,
5720
+ end: match.exprStart + range.end
5721
+ });
5722
+ if (binding.parsed === null) {
5723
+ problems.push({ message: binding.error ?? "parse error", range: shift(binding.exprRange) });
5724
+ continue;
5725
+ }
5726
+ if (binding.pathRange === null) continue;
5727
+ occurrences.push({
5728
+ source: match.kind,
5729
+ kind: "path",
5730
+ stateName: binding.parsed.stateName,
5731
+ path: binding.parsed.statePathName,
5732
+ pathRange: shift(binding.pathRange),
5733
+ exprRange: { start: match.exprStart, end: match.exprEnd },
5734
+ propName: null,
5735
+ propRange: null,
5736
+ stateNameRange: binding.stateNameRange === null ? null : shift(binding.stateNameRange),
5737
+ bindingType: "text"
5738
+ });
5739
+ }
5740
+ const declarations = [];
5741
+ for (const block of parseWcsScriptBlocks(html, stateTagName)) {
5742
+ for (const span of analyzeDeclarationSpans(block.content)) {
5743
+ declarations.push({
5744
+ stateName: block.stateName,
5745
+ name: span.name,
5746
+ kind: span.kind,
5747
+ range: { start: block.contentStart + span.start, end: block.contentStart + span.end }
5748
+ });
5749
+ }
5750
+ }
5751
+ const byPath = /* @__PURE__ */ new Map();
5752
+ for (const occurrence of occurrences) {
5753
+ if (occurrence.kind !== "path") continue;
5754
+ const key = keyOf(occurrence.stateName, occurrence.path);
5755
+ const list = byPath.get(key);
5756
+ if (list === void 0) {
5757
+ byPath.set(key, [occurrence]);
5758
+ } else {
5759
+ list.push(occurrence);
5760
+ }
5761
+ }
5762
+ const declarationByName = /* @__PURE__ */ new Map();
5763
+ for (const declaration of declarations) {
5764
+ const key = keyOf(declaration.stateName, declaration.name);
5765
+ if (!declarationByName.has(key)) declarationByName.set(key, declaration);
5766
+ }
5767
+ return {
5768
+ occurrences,
5769
+ declarations,
5770
+ problems,
5771
+ referencesOf(stateName, path) {
5772
+ return (byPath.get(keyOf(stateName, path)) ?? []).slice();
5773
+ },
5774
+ declarationOf(stateName, path) {
5775
+ const exact = declarationByName.get(keyOf(stateName, path));
5776
+ if (exact !== void 0) return exact;
5777
+ const firstSegment = path.split(".")[0];
5778
+ if (firstSegment === path) return null;
5779
+ return declarationByName.get(keyOf(stateName, firstSegment)) ?? null;
5780
+ },
5781
+ occurrenceAt(offset) {
5782
+ for (const occurrence of occurrences) {
5783
+ if (offset >= occurrence.pathRange.start && offset < occurrence.pathRange.end) {
5784
+ return occurrence;
5785
+ }
5786
+ }
5787
+ return null;
5788
+ }
5789
+ };
5790
+ }
5791
+
5792
+ // src/service/semanticValidator.ts
5793
+ var STATE_UPDATED_CALLBACK = "$updatedCallback";
5794
+ var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
5795
+ var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
5796
+ function splitCallArgs(source, open) {
5797
+ const args = [];
5798
+ const starts = [];
5799
+ let depth = 0;
5800
+ let argStart = open;
5801
+ let i = open;
5802
+ while (i < source.length) {
5803
+ const ch = source[i];
5804
+ if (ch === '"' || ch === "'" || ch === "`") {
5805
+ const quote = ch;
5806
+ i++;
5807
+ while (i < source.length) {
5808
+ if (source[i] === "\\") {
5809
+ i += 2;
5810
+ continue;
5811
+ }
5812
+ if (source[i] === quote) {
5813
+ i++;
5814
+ break;
5815
+ }
5816
+ i++;
5817
+ }
5818
+ continue;
5819
+ }
5820
+ if (ch === "(" || ch === "[" || ch === "{") {
5821
+ depth++;
5822
+ i++;
5823
+ continue;
5824
+ }
5825
+ if (ch === ")" && depth === 0) {
5826
+ args.push(source.slice(argStart, i));
5827
+ starts.push(argStart);
5828
+ return { args, starts, end: i + 1 };
5829
+ }
5830
+ if (ch === ")" || ch === "]" || ch === "}") {
5831
+ depth--;
5832
+ i++;
5833
+ continue;
5834
+ }
5835
+ if (ch === "," && depth === 0) {
5836
+ args.push(source.slice(argStart, i));
5837
+ starts.push(argStart);
5838
+ argStart = i + 1;
5839
+ i++;
5840
+ continue;
5841
+ }
5842
+ i++;
5843
+ }
5844
+ return null;
5845
+ }
5846
+ function literalString(arg) {
5847
+ const match = STRING_LITERAL.exec(arg);
5848
+ return match === null ? null : match[2];
5849
+ }
5850
+ function literalArrayLength(arg) {
5851
+ const trimmed = arg.trim();
5852
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
5853
+ const inner = trimmed.slice(1, -1);
5854
+ if (inner.trim().length === 0) return 0;
5855
+ if (/(^|[^.])\.\.\./.test(inner)) return null;
5856
+ const parts = splitCallArgs(`${inner})`, 0);
5857
+ if (parts === null) return null;
5858
+ return parts.args.filter((part) => part.trim().length > 0).length;
5859
+ }
5860
+ function validateIndexArity(script, scriptStart, locale3) {
5861
+ const msgs = getMessages(locale3);
5862
+ const out = [];
5863
+ API_CALL.lastIndex = 0;
5864
+ let match;
5865
+ while ((match = API_CALL.exec(script)) !== null) {
5866
+ const api = `$${match[1]}`;
5867
+ const parsed = splitCallArgs(script, match.index + match[0].length);
5868
+ if (parsed === null) continue;
5869
+ API_CALL.lastIndex = parsed.end;
5870
+ if (parsed.args.length < 2) continue;
5871
+ const path = literalString(parsed.args[0]);
5872
+ if (path === null) continue;
5873
+ const actual = literalArrayLength(parsed.args[1]);
5874
+ if (actual === null) continue;
5875
+ const wildcardCount = countWildcardSegments(path);
5876
+ const requirement = api === "$resolve" ? "exact" : "atMost";
5877
+ const mismatched = requirement === "exact" ? actual !== wildcardCount : actual > wildcardCount;
5878
+ if (!mismatched) continue;
5879
+ const argText = parsed.args[1];
5880
+ const leading = argText.length - argText.trimStart().length;
5881
+ out.push({
5882
+ code: WcsDiagnosticCode.IndexArity,
5883
+ start: scriptStart + parsed.starts[1] + leading,
5884
+ end: scriptStart + parsed.starts[1] + argText.trimEnd().length,
5885
+ message: msgs.indexArity(api, path, requirement, wildcardCount, actual),
5886
+ severity: "warning"
5887
+ });
5888
+ }
5889
+ return out;
5890
+ }
5891
+ var READ_BRACKET = /\bthis\s*\??\.\s*\[\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*\]|\bthis\s*\??\[\s*(["'])((?:\\.|(?!\3)[^\\])*)\3\s*\]/g;
5892
+ var READ_DOT = /\bthis\s*\??\.\s*([A-Za-z_]\w*)/g;
5893
+ var READ_API = /\bthis\s*\??\.\s*\$(?:getAll|resolve)\s*\(\s*(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
5894
+ function collectReadPaths(body) {
5895
+ const paths = /* @__PURE__ */ new Set();
5896
+ for (const [regex, groups] of [
5897
+ [READ_BRACKET, [2, 4]],
5898
+ [READ_API, [2]],
5899
+ [READ_DOT, [1]]
5900
+ ]) {
5901
+ regex.lastIndex = 0;
5902
+ let match;
5903
+ while ((match = regex.exec(body)) !== null) {
5904
+ for (const group of groups) {
5905
+ const value = match[group];
5906
+ if (value !== void 0 && value.length > 0 && !value.startsWith("$")) {
5907
+ paths.add(value);
5908
+ }
5909
+ }
5910
+ }
5911
+ }
5912
+ return paths;
5913
+ }
5914
+ function validateGetterCycles(script, scriptStart, locale3) {
5915
+ const msgs = getMessages(locale3);
5916
+ const getters = analyzeCallableBodies(script).filter((entry) => entry.kind === "getter");
5917
+ if (getters.length === 0) return [];
5918
+ const declared = new Set(getters.map((getter) => getter.name));
5919
+ const edges = /* @__PURE__ */ new Map();
5920
+ for (const getter of getters) {
5921
+ const targets = [];
5922
+ for (const read of collectReadPaths(getter.body)) {
5923
+ if (declared.has(read)) targets.push(read);
5924
+ }
5925
+ edges.set(getter.name, targets);
5926
+ }
5927
+ const gray = /* @__PURE__ */ new Set();
5928
+ const black = /* @__PURE__ */ new Set();
5929
+ const stack = [];
5930
+ const cyclesByEntry = /* @__PURE__ */ new Map();
5931
+ const visit = (name) => {
5932
+ if (black.has(name)) return;
5933
+ if (gray.has(name)) {
5934
+ const from = stack.indexOf(name);
5935
+ const cycle = stack.slice(from).concat(name).join(" -> ");
5936
+ for (const member of stack.slice(from)) {
5937
+ if (!cyclesByEntry.has(member)) cyclesByEntry.set(member, cycle);
5938
+ }
5939
+ return;
5940
+ }
5941
+ gray.add(name);
5942
+ stack.push(name);
5943
+ for (const next of edges.get(name) ?? []) {
5944
+ visit(next);
5945
+ }
5946
+ stack.pop();
5947
+ gray.delete(name);
5948
+ black.add(name);
5949
+ };
5950
+ for (const getter of getters) {
5951
+ visit(getter.name);
5952
+ }
5953
+ if (cyclesByEntry.size === 0) return [];
5954
+ const out = [];
5955
+ for (const getter of getters) {
5956
+ const cycle = cyclesByEntry.get(getter.name);
5957
+ if (cycle === void 0) continue;
5958
+ out.push({
5959
+ code: WcsDiagnosticCode.GetterCycle,
5960
+ start: scriptStart + getter.start,
5961
+ end: scriptStart + getter.end,
5962
+ message: msgs.getterCycle(cycle),
5963
+ severity: "warning"
5964
+ });
5965
+ }
5966
+ return out;
5967
+ }
5968
+ function blankComments(source) {
5969
+ const out = source.split("");
5970
+ let i = 0;
5971
+ while (i < source.length) {
5972
+ const ch = source[i];
5973
+ if (ch === '"' || ch === "'" || ch === "`") {
5974
+ const quote = ch;
5975
+ i++;
5976
+ while (i < source.length) {
5977
+ if (source[i] === "\\") {
5978
+ i += 2;
5979
+ continue;
5980
+ }
5981
+ if (source[i] === quote) {
5982
+ i++;
5983
+ break;
5984
+ }
5985
+ i++;
5986
+ }
5987
+ continue;
5988
+ }
5989
+ if (ch === "/" && source[i + 1] === "/") {
5990
+ while (i < source.length && source[i] !== "\n") {
5991
+ out[i] = " ";
5992
+ i++;
5993
+ }
5994
+ continue;
5995
+ }
5996
+ if (ch === "/" && source[i + 1] === "*") {
5997
+ while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
5998
+ out[i] = " ";
5999
+ i++;
6000
+ }
6001
+ if (i < source.length) {
6002
+ out[i] = " ";
6003
+ out[i + 1] = " ";
6004
+ i += 2;
6005
+ }
6006
+ continue;
6007
+ }
6008
+ i++;
6009
+ }
6010
+ return out.join("");
6011
+ }
6012
+ var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
6013
+ function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3) {
6014
+ const blocks = parseWcsScriptBlocks(html, stateTagName);
6015
+ if (blocks.length === 0) return [];
6016
+ const hasCallback = blocks.some((block) => block.content.includes(STATE_UPDATED_CALLBACK));
6017
+ if (!hasCallback) return [];
6018
+ const msgs = getMessages(locale3);
6019
+ const boundPaths = collectBoundPaths(html, stateTagName, bindAttrName);
6020
+ const out = [];
6021
+ for (const block of blocks) {
6022
+ const callback = analyzeCallableBodies(block.content).find((entry) => entry.name === STATE_UPDATED_CALLBACK && entry.kind === "method");
6023
+ if (callback === void 0) continue;
6024
+ const declared = new Set(analyzeStatePaths(block.content, block.stateName).map((p) => p.path));
6025
+ const bound = boundPaths.get(block.stateName) ?? /* @__PURE__ */ new Set();
6026
+ const body = blankComments(callback.body);
6027
+ PATH_TEST_LITERAL.lastIndex = 0;
6028
+ let match;
6029
+ const reported = /* @__PURE__ */ new Set();
6030
+ while ((match = PATH_TEST_LITERAL.exec(body)) !== null) {
6031
+ const path = match[2];
6032
+ if (path.length === 0 || !declared.has(path) || bound.has(path)) continue;
6033
+ if (reported.has(path)) continue;
6034
+ reported.add(path);
6035
+ const quoteAt = match.index + match[0].length - path.length - 1;
6036
+ out.push({
6037
+ code: WcsDiagnosticCode.UpdatedCallbackUnbound,
6038
+ start: block.contentStart + callback.bodyStart + quoteAt,
6039
+ end: block.contentStart + callback.bodyStart + quoteAt + path.length,
6040
+ message: msgs.updatedCallbackUnbound(path),
6041
+ severity: "warning"
6042
+ });
6043
+ }
6044
+ }
6045
+ return out;
6046
+ }
6047
+ function collectBoundPaths(html, stateTagName, bindAttrName) {
6048
+ const byState = /* @__PURE__ */ new Map();
6049
+ const add = (stateName, path) => {
6050
+ let set = byState.get(stateName);
6051
+ if (set === void 0) {
6052
+ set = /* @__PURE__ */ new Set();
6053
+ byState.set(stateName, set);
6054
+ }
6055
+ set.add(path);
6056
+ };
6057
+ const index = buildReferenceIndex(html, { bindAttribute: bindAttrName, stateTagName });
6058
+ for (const occurrence of index.occurrences) {
6059
+ add(occurrence.stateName, occurrence.path);
6060
+ if (!occurrence.path.startsWith(".")) continue;
6061
+ const forPath = getInnermostForPath(html, occurrence.pathRange.start, bindAttrName);
6062
+ if (forPath === null || forPath.startsWith(".")) continue;
6063
+ add(
6064
+ occurrence.stateName,
6065
+ occurrence.path === "." ? `${forPath}.*` : `${forPath}.*.${occurrence.path.slice(1)}`
6066
+ );
6067
+ }
6068
+ return byState;
6069
+ }
6070
+ function validateSemantics(html, stateTagName = "wcs-state", locale3, bindAttrName = "data-wcs") {
6071
+ const out = [];
6072
+ for (const block of parseWcsScriptBlocks(html, stateTagName)) {
6073
+ out.push(...validateIndexArity(block.content, block.contentStart, locale3));
6074
+ out.push(...validateGetterCycles(block.content, block.contentStart, locale3));
6075
+ }
6076
+ out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3));
6077
+ return out;
6078
+ }
6079
+
6080
+ // src/core/validateDocument.ts
6081
+ function validateDocument(text, options = {}) {
6082
+ const bindAttribute = options.bindAttribute ?? "data-wcs";
6083
+ const stateTagName = options.stateTagName ?? "wcs-state";
6084
+ const locale3 = options.locale;
3703
6085
  const fileReader = options.fileReader;
3704
6086
  const out = [];
3705
- out.push(...validateBindings(text, bindAttribute, stateTagName, locale, fileReader));
3706
- out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale, fileReader));
3707
- out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale, fileReader));
3708
- out.push(...validateDocumentEnv(text, locale));
3709
- out.push(...validateArrayMutations(text, stateTagName, locale));
3710
- out.push(...validateWatchDeclarations(text, stateTagName, locale));
3711
- for (const d of validateStateTypes(text, stateTagName, locale)) {
6087
+ out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader));
6088
+ out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader));
6089
+ out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
6090
+ out.push(...validateAriaAttributes(text, bindAttribute, locale3));
6091
+ out.push(...validateDocumentEnv(text, locale3));
6092
+ out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
6093
+ out.push(...validateArrayMutations(text, stateTagName, locale3));
6094
+ out.push(...validateWatchDeclarations(text, stateTagName, locale3));
6095
+ for (const d of validateStateTypes(text, stateTagName, locale3)) {
3712
6096
  out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
3713
6097
  }
3714
- for (const d of validateNestedAssigns(text, stateTagName, locale)) {
6098
+ for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
3715
6099
  out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
3716
6100
  }
3717
6101
  return sortDiagnostics(out);
@@ -4336,29 +6720,6 @@ function runValidation(inputs, options = {}) {
4336
6720
  function classify(path) {
4337
6721
  return path.endsWith(".manifest.json") ? "manifest" : "html";
4338
6722
  }
4339
- function createFileReader(htmlPath, read = (p) => (0, import_node_fs.readFileSync)(p, "utf8")) {
4340
- const base = (0, import_node_path.dirname)(htmlPath);
4341
- const cache = /* @__PURE__ */ new Map();
4342
- return (relativePath) => {
4343
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(relativePath) || relativePath.startsWith("/")) {
4344
- return void 0;
4345
- }
4346
- if (cache.has(relativePath)) {
4347
- return cache.get(relativePath);
4348
- }
4349
- let content;
4350
- try {
4351
- content = read((0, import_node_path.resolve)(base, relativePath));
4352
- if (content.charCodeAt(0) === 65279) {
4353
- content = content.slice(1);
4354
- }
4355
- } catch {
4356
- content = void 0;
4357
- }
4358
- cache.set(relativePath, content);
4359
- return content;
4360
- };
4361
- }
4362
6723
  function parseArgs(argv) {
4363
6724
  const options = {};
4364
6725
  const files = [];
@@ -4383,7 +6744,7 @@ function resolveCliLocale(explicit, env = process.env) {
4383
6744
  }
4384
6745
  function main(argv) {
4385
6746
  const { options, files } = parseArgs(argv);
4386
- const locale = resolveCliLocale(options.locale);
6747
+ const locale3 = resolveCliLocale(options.locale);
4387
6748
  if (files.length === 0) {
4388
6749
  process.stderr.write("usage: wcs-validate [--attr=data-wcs] [--state-tag=wcs-state] [--lang=ja|en] <file> [<file> ...]\n");
4389
6750
  return 2;
@@ -4392,7 +6753,7 @@ function main(argv) {
4392
6753
  for (const path of files) {
4393
6754
  let text;
4394
6755
  try {
4395
- text = (0, import_node_fs.readFileSync)(path, "utf8");
6756
+ text = (0, import_node_fs2.readFileSync)(path, "utf8");
4396
6757
  } catch (e) {
4397
6758
  process.stderr.write(`cannot read ${path}: ${e.message}
4398
6759
  `);
@@ -4401,7 +6762,7 @@ function main(argv) {
4401
6762
  const kind = classify(path);
4402
6763
  inputs.push({ source: path, text, kind, fileReader: kind === "html" ? createFileReader(path) : void 0 });
4403
6764
  }
4404
- const result = runValidation(inputs, { ...options, locale });
6765
+ const result = runValidation(inputs, { ...options, locale: locale3 });
4405
6766
  for (const line of result.lines) {
4406
6767
  process.stdout.write(line + "\n");
4407
6768
  }