@wcstack/lint 1.30.0 → 1.31.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 +2327 -95
  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` / `$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
  // 越境 `@` / `$` 始まり / 空キー・空セグメント / 明らかな非関数ハンドラ。
@@ -131,6 +171,545 @@ function sortDiagnostics(diagnostics) {
131
171
  }
132
172
 
133
173
  // ../state/dist/manifest.esm.js
174
+ var _config = {
175
+ bindAttributeName: "data-wcs",
176
+ tagNames: {
177
+ state: "wcs-state"
178
+ },
179
+ locale: "en"
180
+ };
181
+ var config = _config;
182
+ function raiseError(message) {
183
+ throw new Error(`[@wcstack/state] ${message}`);
184
+ }
185
+ function optionsRequired(fnName) {
186
+ raiseError(`filter ${fnName} requires at least one option`);
187
+ }
188
+ function optionMustBeNumber(fnName) {
189
+ raiseError(`filter ${fnName} requires a number as option`);
190
+ }
191
+ function valueMustBeNumber(fnName) {
192
+ raiseError(`filter ${fnName} requires a number value`);
193
+ }
194
+ function valueMustBeBoolean(fnName) {
195
+ raiseError(`filter ${fnName} requires a boolean value`);
196
+ }
197
+ function valueMustBeDate(fnName) {
198
+ raiseError(`filter ${fnName} requires a date value`);
199
+ }
200
+ function valueMustBeArray(fnName) {
201
+ raiseError(`filter ${fnName} requires an array value`);
202
+ }
203
+ function validateNumberString(value) {
204
+ if (!value || isNaN(Number(value))) {
205
+ return false;
206
+ }
207
+ return true;
208
+ }
209
+ var eq = (options) => {
210
+ const opt = options?.[0] ?? optionsRequired("eq");
211
+ return (value) => {
212
+ if (typeof value === "number") {
213
+ if (!validateNumberString(opt)) {
214
+ optionMustBeNumber("eq");
215
+ }
216
+ return value === Number(opt);
217
+ }
218
+ if (typeof value === "string") {
219
+ return value === opt;
220
+ }
221
+ return value === opt;
222
+ };
223
+ };
224
+ var ne = (options) => {
225
+ const opt = options?.[0] ?? optionsRequired("ne");
226
+ return (value) => {
227
+ if (typeof value === "number") {
228
+ if (!validateNumberString(opt)) {
229
+ optionMustBeNumber("ne");
230
+ }
231
+ return value !== Number(opt);
232
+ }
233
+ if (typeof value === "string") {
234
+ return value !== opt;
235
+ }
236
+ return value !== opt;
237
+ };
238
+ };
239
+ var not = (_options) => {
240
+ return (value) => {
241
+ if (typeof value !== "boolean") {
242
+ valueMustBeBoolean("not");
243
+ }
244
+ return !value;
245
+ };
246
+ };
247
+ var lt = (options) => {
248
+ const opt = options?.[0] ?? optionsRequired("lt");
249
+ if (!validateNumberString(opt)) {
250
+ optionMustBeNumber("lt");
251
+ }
252
+ return (value) => {
253
+ if (typeof value !== "number") {
254
+ valueMustBeNumber("lt");
255
+ }
256
+ return value < Number(opt);
257
+ };
258
+ };
259
+ var le = (options) => {
260
+ const opt = options?.[0] ?? optionsRequired("le");
261
+ if (!validateNumberString(opt)) {
262
+ optionMustBeNumber("le");
263
+ }
264
+ return (value) => {
265
+ if (typeof value !== "number") {
266
+ valueMustBeNumber("le");
267
+ }
268
+ return value <= Number(opt);
269
+ };
270
+ };
271
+ var gt = (options) => {
272
+ const opt = options?.[0] ?? optionsRequired("gt");
273
+ if (!validateNumberString(opt)) {
274
+ optionMustBeNumber("gt");
275
+ }
276
+ return (value) => {
277
+ if (typeof value !== "number") {
278
+ valueMustBeNumber("gt");
279
+ }
280
+ return value > Number(opt);
281
+ };
282
+ };
283
+ var ge = (options) => {
284
+ const opt = options?.[0] ?? optionsRequired("ge");
285
+ if (!validateNumberString(opt)) {
286
+ optionMustBeNumber("ge");
287
+ }
288
+ return (value) => {
289
+ if (typeof value !== "number") {
290
+ valueMustBeNumber("ge");
291
+ }
292
+ return value >= Number(opt);
293
+ };
294
+ };
295
+ var inc = (options) => {
296
+ const opt = options?.[0] ?? optionsRequired("inc");
297
+ if (!validateNumberString(opt)) {
298
+ optionMustBeNumber("inc");
299
+ }
300
+ return (value) => {
301
+ if (typeof value !== "number") {
302
+ valueMustBeNumber("inc");
303
+ }
304
+ return value + Number(opt);
305
+ };
306
+ };
307
+ var dec = (options) => {
308
+ const opt = options?.[0] ?? optionsRequired("dec");
309
+ if (!validateNumberString(opt)) {
310
+ optionMustBeNumber("dec");
311
+ }
312
+ return (value) => {
313
+ if (typeof value !== "number") {
314
+ valueMustBeNumber("dec");
315
+ }
316
+ return value - Number(opt);
317
+ };
318
+ };
319
+ var mul = (options) => {
320
+ const opt = options?.[0] ?? optionsRequired("mul");
321
+ if (!validateNumberString(opt)) {
322
+ optionMustBeNumber("mul");
323
+ }
324
+ return (value) => {
325
+ if (typeof value !== "number") {
326
+ valueMustBeNumber("mul");
327
+ }
328
+ return value * Number(opt);
329
+ };
330
+ };
331
+ var div = (options) => {
332
+ const opt = options?.[0] ?? optionsRequired("div");
333
+ if (!validateNumberString(opt)) {
334
+ optionMustBeNumber("div");
335
+ }
336
+ return (value) => {
337
+ if (typeof value !== "number") {
338
+ valueMustBeNumber("div");
339
+ }
340
+ return value / Number(opt);
341
+ };
342
+ };
343
+ var mod = (options) => {
344
+ const opt = options?.[0] ?? optionsRequired("mod");
345
+ if (!validateNumberString(opt)) {
346
+ optionMustBeNumber("mod");
347
+ }
348
+ return (value) => {
349
+ if (typeof value !== "number") {
350
+ valueMustBeNumber("mod");
351
+ }
352
+ return value % Number(opt);
353
+ };
354
+ };
355
+ var abs = (_options) => {
356
+ return (value) => {
357
+ if (typeof value !== "number") {
358
+ valueMustBeNumber("abs");
359
+ }
360
+ return Math.abs(value);
361
+ };
362
+ };
363
+ var clamp = (options) => {
364
+ const opt1 = options?.[0] ?? optionsRequired("clamp");
365
+ if (!validateNumberString(opt1)) {
366
+ optionMustBeNumber("clamp");
367
+ }
368
+ const opt2 = options?.[1] ?? optionsRequired("clamp");
369
+ if (!validateNumberString(opt2)) {
370
+ optionMustBeNumber("clamp");
371
+ }
372
+ const min = Number(opt1);
373
+ const max = Number(opt2);
374
+ return (value) => {
375
+ if (typeof value !== "number") {
376
+ valueMustBeNumber("clamp");
377
+ }
378
+ return Math.min(Math.max(value, min), max);
379
+ };
380
+ };
381
+ var fix = (options) => {
382
+ const opt = options?.[0] ?? "0";
383
+ if (!validateNumberString(opt)) {
384
+ optionMustBeNumber("fix");
385
+ }
386
+ return (value) => {
387
+ if (typeof value !== "number") {
388
+ valueMustBeNumber("fix");
389
+ }
390
+ return value.toFixed(Number(opt));
391
+ };
392
+ };
393
+ var locale = (options) => {
394
+ const opt = options?.[0] ?? config.locale;
395
+ return (value) => {
396
+ if (typeof value !== "number") {
397
+ valueMustBeNumber("locale");
398
+ }
399
+ return value.toLocaleString(opt);
400
+ };
401
+ };
402
+ var uc = (_options) => {
403
+ return (value) => {
404
+ return String(value).toUpperCase();
405
+ };
406
+ };
407
+ var lc = (_options) => {
408
+ return (value) => {
409
+ return String(value).toLowerCase();
410
+ };
411
+ };
412
+ var cap = (_options) => {
413
+ return (value) => {
414
+ const v = String(value);
415
+ if (v.length === 0) {
416
+ return v;
417
+ }
418
+ if (v.length === 1) {
419
+ return v.toUpperCase();
420
+ }
421
+ return v.charAt(0).toUpperCase() + v.slice(1);
422
+ };
423
+ };
424
+ var trim = (_options) => {
425
+ return (value) => {
426
+ return String(value).trim();
427
+ };
428
+ };
429
+ var slice = (options) => {
430
+ const numberedOpts = [];
431
+ const opt1 = options?.[0] ?? optionsRequired("slice");
432
+ if (!validateNumberString(opt1)) {
433
+ optionMustBeNumber("slice");
434
+ }
435
+ numberedOpts.push(Number(opt1));
436
+ const opt2 = options?.[1];
437
+ if (typeof opt2 !== "undefined") {
438
+ if (!validateNumberString(opt2)) {
439
+ optionMustBeNumber("slice");
440
+ }
441
+ numberedOpts.push(Number(opt2));
442
+ }
443
+ return (value) => {
444
+ return String(value).slice(...numberedOpts);
445
+ };
446
+ };
447
+ var substr = (options) => {
448
+ const opt1 = options?.[0] ?? optionsRequired("substr");
449
+ if (!validateNumberString(opt1)) {
450
+ optionMustBeNumber("substr");
451
+ }
452
+ const opt2 = options?.[1] ?? optionsRequired("substr");
453
+ if (!validateNumberString(opt2)) {
454
+ optionMustBeNumber("substr");
455
+ }
456
+ return (value) => {
457
+ return String(value).substr(Number(opt1), Number(opt2));
458
+ };
459
+ };
460
+ var pad = (options) => {
461
+ const opt1 = options?.[0] ?? optionsRequired("pad");
462
+ if (!validateNumberString(opt1)) {
463
+ optionMustBeNumber("pad");
464
+ }
465
+ const opt2 = options?.[1] ?? "0";
466
+ return (value) => {
467
+ return String(value).padStart(Number(opt1), opt2);
468
+ };
469
+ };
470
+ var rep = (options) => {
471
+ const opt = options?.[0] ?? optionsRequired("rep");
472
+ if (!validateNumberString(opt)) {
473
+ optionMustBeNumber("rep");
474
+ }
475
+ return (value) => {
476
+ return String(value).repeat(Number(opt));
477
+ };
478
+ };
479
+ var rev = (_options) => {
480
+ return (value) => {
481
+ return String(value).split("").reverse().join("");
482
+ };
483
+ };
484
+ var int = (_options) => {
485
+ return (value) => {
486
+ return parseInt(String(value), 10);
487
+ };
488
+ };
489
+ var float = (_options) => {
490
+ return (value) => {
491
+ return parseFloat(String(value));
492
+ };
493
+ };
494
+ var round = (options) => {
495
+ const opt = options?.[0] ?? "0";
496
+ if (!validateNumberString(opt)) {
497
+ optionMustBeNumber("round");
498
+ }
499
+ return (value) => {
500
+ if (typeof value !== "number") {
501
+ valueMustBeNumber("round");
502
+ }
503
+ const optValue = Math.pow(10, Number(opt));
504
+ return Math.round(value * optValue) / optValue;
505
+ };
506
+ };
507
+ var floor = (options) => {
508
+ const opt = options?.[0] ?? "0";
509
+ if (!validateNumberString(opt)) {
510
+ optionMustBeNumber("floor");
511
+ }
512
+ return (value) => {
513
+ if (typeof value !== "number") {
514
+ valueMustBeNumber("floor");
515
+ }
516
+ const optValue = Math.pow(10, Number(opt));
517
+ return Math.floor(value * optValue) / optValue;
518
+ };
519
+ };
520
+ var ceil = (options) => {
521
+ const opt = options?.[0] ?? "0";
522
+ if (!validateNumberString(opt)) {
523
+ optionMustBeNumber("ceil");
524
+ }
525
+ return (value) => {
526
+ if (typeof value !== "number") {
527
+ valueMustBeNumber("ceil");
528
+ }
529
+ const optValue = Math.pow(10, Number(opt));
530
+ return Math.ceil(value * optValue) / optValue;
531
+ };
532
+ };
533
+ var percent = (options) => {
534
+ const opt = options?.[0] ?? "0";
535
+ if (!validateNumberString(opt)) {
536
+ optionMustBeNumber("percent");
537
+ }
538
+ return (value) => {
539
+ if (typeof value !== "number") {
540
+ valueMustBeNumber("percent");
541
+ }
542
+ return `${(value * 100).toFixed(Number(opt))}%`;
543
+ };
544
+ };
545
+ var unit = (options) => {
546
+ const opt = options?.[0] ?? optionsRequired("unit");
547
+ return (value) => {
548
+ if (value === null || typeof value === "undefined") {
549
+ return value;
550
+ }
551
+ return String(value) + opt;
552
+ };
553
+ };
554
+ var join = (options) => {
555
+ const opt = options?.[0] ?? ", ";
556
+ return (value) => {
557
+ if (!Array.isArray(value)) {
558
+ valueMustBeArray("join");
559
+ }
560
+ return value.join(opt);
561
+ };
562
+ };
563
+ var truncate = (options) => {
564
+ const opt1 = options?.[0] ?? optionsRequired("truncate");
565
+ if (!validateNumberString(opt1)) {
566
+ optionMustBeNumber("truncate");
567
+ }
568
+ const maxLength = Number(opt1);
569
+ const suffix = options?.[1] ?? "\u2026";
570
+ return (value) => {
571
+ const v = String(value);
572
+ if (v.length <= maxLength) {
573
+ return v;
574
+ }
575
+ return v.slice(0, maxLength) + suffix;
576
+ };
577
+ };
578
+ var date = (options) => {
579
+ const opt = options?.[0] ?? config.locale;
580
+ return (value) => {
581
+ if (!(value instanceof Date)) {
582
+ valueMustBeDate("date");
583
+ }
584
+ return value.toLocaleDateString(opt);
585
+ };
586
+ };
587
+ var time = (options) => {
588
+ const opt = options?.[0] ?? config.locale;
589
+ return (value) => {
590
+ if (!(value instanceof Date)) {
591
+ valueMustBeDate("time");
592
+ }
593
+ return value.toLocaleTimeString(opt);
594
+ };
595
+ };
596
+ var datetime = (options) => {
597
+ const opt = options?.[0] ?? config.locale;
598
+ return (value) => {
599
+ if (!(value instanceof Date)) {
600
+ valueMustBeDate("datetime");
601
+ }
602
+ return value.toLocaleString(opt);
603
+ };
604
+ };
605
+ var ymd = (options) => {
606
+ const opt = options?.[0] ?? "-";
607
+ return (value) => {
608
+ if (!(value instanceof Date)) {
609
+ valueMustBeDate("ymd");
610
+ }
611
+ const year = value.getFullYear().toString();
612
+ const month = (value.getMonth() + 1).toString().padStart(2, "0");
613
+ const day = value.getDate().toString().padStart(2, "0");
614
+ return `${year}${opt}${month}${opt}${day}`;
615
+ };
616
+ };
617
+ var hms = (options) => {
618
+ const opt = options?.[0] ?? ":";
619
+ return (value) => {
620
+ if (!(value instanceof Date)) {
621
+ valueMustBeDate("hms");
622
+ }
623
+ const hours = value.getHours().toString().padStart(2, "0");
624
+ const minutes = value.getMinutes().toString().padStart(2, "0");
625
+ const seconds = value.getSeconds().toString().padStart(2, "0");
626
+ return `${hours}${opt}${minutes}${opt}${seconds}`;
627
+ };
628
+ };
629
+ var falsy = (_options) => {
630
+ return (value) => value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value);
631
+ };
632
+ var truthy = (_options) => {
633
+ return (value) => value !== false && value !== null && value !== void 0 && value !== 0 && value !== "" && !Number.isNaN(value);
634
+ };
635
+ var defaults = (options) => {
636
+ const opt = options?.[0] ?? optionsRequired("defaults");
637
+ return (value) => {
638
+ if (value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value)) {
639
+ return opt;
640
+ }
641
+ return value;
642
+ };
643
+ };
644
+ var boolean = (_options) => {
645
+ return (value) => {
646
+ return Boolean(value);
647
+ };
648
+ };
649
+ var number = (_options) => {
650
+ return (value) => {
651
+ return Number(value);
652
+ };
653
+ };
654
+ var string = (_options) => {
655
+ return (value) => {
656
+ return String(value);
657
+ };
658
+ };
659
+ var _null = (_options) => {
660
+ return (value) => {
661
+ return value === "" ? null : value;
662
+ };
663
+ };
664
+ var builtinFilters = {
665
+ "eq": eq,
666
+ "ne": ne,
667
+ "not": not,
668
+ "lt": lt,
669
+ "le": le,
670
+ "gt": gt,
671
+ "ge": ge,
672
+ "inc": inc,
673
+ "dec": dec,
674
+ "mul": mul,
675
+ "div": div,
676
+ "mod": mod,
677
+ "abs": abs,
678
+ "clamp": clamp,
679
+ "fix": fix,
680
+ "locale": locale,
681
+ "uc": uc,
682
+ "lc": lc,
683
+ "cap": cap,
684
+ "trim": trim,
685
+ "slice": slice,
686
+ "substr": substr,
687
+ "pad": pad,
688
+ "rep": rep,
689
+ "rev": rev,
690
+ "truncate": truncate,
691
+ "join": join,
692
+ "int": int,
693
+ "float": float,
694
+ "round": round,
695
+ "floor": floor,
696
+ "ceil": ceil,
697
+ "percent": percent,
698
+ "unit": unit,
699
+ "date": date,
700
+ "time": time,
701
+ "datetime": datetime,
702
+ "ymd": ymd,
703
+ "hms": hms,
704
+ "falsy": falsy,
705
+ "truthy": truthy,
706
+ "defaults": defaults,
707
+ "boolean": boolean,
708
+ "number": number,
709
+ "string": string,
710
+ "null": _null
711
+ };
712
+ var outputBuiltinFilters = builtinFilters;
134
713
  var builtinFilterMeta = {
135
714
  // 比較・論理
136
715
  eq: { description: "\u7B49\u3057\u3044\u304B\u6BD4\u8F03", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
@@ -194,7 +773,14 @@ var STRUCTURAL_BINDING_TYPE_SET = /* @__PURE__ */ new Set([
194
773
  "else",
195
774
  "for"
196
775
  ]);
776
+ var DELIMITER = ".";
777
+ var WILDCARD = "*";
197
778
  var MAX_WILDCARD_DEPTH = 128;
779
+ var BINDING_SEPARATOR = ";";
780
+ var PROP_VALUE_SEPARATOR = ":";
781
+ var MODIFIER_SEPARATOR = "#";
782
+ var STATE_NAME_SEPARATOR = "@";
783
+ var FILTER_SEPARATOR = "|";
198
784
  var MODIFIER_PREVENT = "prevent";
199
785
  var MODIFIER_STOP = "stop";
200
786
  var MODIFIER_READONLY = "ro";
@@ -209,12 +795,99 @@ var MODIFIER_KEYS = Object.freeze([
209
795
  MODIFIER_KEY_INIT,
210
796
  MODIFIER_KEY_SYNC
211
797
  ]);
798
+ var ELSE_KEYWORD = "else";
799
+ var SPREAD_PROP = "...";
800
+ var EVENT_PROP_PREFIX = "on";
801
+ var EVENT_TOKEN_NAMESPACE = "eventToken";
802
+ var COMMAND_NAMESPACE = "command";
803
+ var CLASS_NAMESPACE = "class";
804
+ var ATTR_NAMESPACE = "attr";
805
+ var STYLE_NAMESPACE = "style";
212
806
  var INDEX_PARAM_PREFIX = "$";
213
807
  var tmpIndexByIndexName = {};
214
808
  for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
215
809
  tmpIndexByIndexName[`${INDEX_PARAM_PREFIX}${i + 1}`] = i;
216
810
  }
217
811
  Object.freeze(tmpIndexByIndexName);
812
+ var STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
813
+ var STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
814
+ var STATE_UPDATED_CALLBACK_NAME = "$updatedCallback";
815
+ var WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
816
+ var STATE_BINDABLES_NAME = "$bindables";
817
+ var STATE_COMMANDS_NAME = "$commands";
818
+ var STATE_COMMAND_TOKENS_NAME = "$commandTokens";
819
+ var STATE_COMMAND_NAMESPACE_NAME = "$command";
820
+ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
821
+ var STATE_ON_NAME = "$on";
822
+ var STATE_STREAMS_NAME = "$streams";
823
+ var STATE_WATCH_NAME = "$watch";
824
+ var STATE_LIST_KEYS_NAME = "$listKeys";
825
+ var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
826
+ var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
827
+ var WCS_MANIFEST_VERSION = 1;
828
+ function getWcsManifest() {
829
+ return {
830
+ version: WCS_MANIFEST_VERSION,
831
+ syntax: {
832
+ bindAttribute: config.bindAttributeName,
833
+ tagName: config.tagNames.state,
834
+ pathDelimiter: DELIMITER,
835
+ wildcard: WILDCARD,
836
+ delimiters: {
837
+ binding: BINDING_SEPARATOR,
838
+ propValue: PROP_VALUE_SEPARATOR,
839
+ modifier: MODIFIER_SEPARATOR,
840
+ stateName: STATE_NAME_SEPARATOR,
841
+ filter: FILTER_SEPARATOR
842
+ },
843
+ // 正本 STRUCTURAL_BINDING_TYPE_SET から導出(手書きの二重定義を排除)。
844
+ structuralDirectives: Array.from(STRUCTURAL_BINDING_TYPE_SET),
845
+ modifiers: {
846
+ flags: MODIFIER_FLAGS,
847
+ keyValue: MODIFIER_KEYS,
848
+ eventNamePrefix: EVENT_PROP_PREFIX
849
+ },
850
+ indexParam: {
851
+ prefix: INDEX_PARAM_PREFIX,
852
+ maxDepth: MAX_WILDCARD_DEPTH
853
+ },
854
+ bindingTypes: {
855
+ elseKeyword: ELSE_KEYWORD,
856
+ spread: SPREAD_PROP,
857
+ eventPropertyPrefix: EVENT_PROP_PREFIX,
858
+ propNamespaces: {
859
+ eventToken: EVENT_TOKEN_NAMESPACE,
860
+ command: COMMAND_NAMESPACE,
861
+ class: CLASS_NAMESPACE,
862
+ attr: ATTR_NAMESPACE,
863
+ style: STYLE_NAMESPACE
864
+ }
865
+ }
866
+ },
867
+ // 実装(Record のキー)から自動導出。手リストを持たない=ドリフトの構造的排除。
868
+ filters: Object.keys(outputBuiltinFilters),
869
+ filterMeta: builtinFilterMeta,
870
+ reservedLifecycle: [
871
+ STATE_CONNECTED_CALLBACK_NAME,
872
+ STATE_DISCONNECTED_CALLBACK_NAME,
873
+ STATE_UPDATED_CALLBACK_NAME,
874
+ WEBCOMPONENT_STATE_READY_CALLBACK_NAME
875
+ ],
876
+ reservedStateApi: [
877
+ STATE_BINDABLES_NAME,
878
+ STATE_COMMANDS_NAME,
879
+ STATE_COMMAND_TOKENS_NAME,
880
+ STATE_COMMAND_NAMESPACE_NAME,
881
+ STATE_EVENT_TOKENS_NAME,
882
+ STATE_ON_NAME,
883
+ STATE_STREAMS_NAME,
884
+ STATE_WATCH_NAME,
885
+ STATE_LIST_KEYS_NAME,
886
+ STATE_STREAM_STATUS_NAMESPACE_NAME,
887
+ STATE_STREAM_ERROR_NAMESPACE_NAME
888
+ ]
889
+ };
890
+ }
218
891
 
219
892
  // src/service/completionData.ts
220
893
  var BUILTIN_FILTERS = Object.entries(builtinFilterMeta).map(
@@ -360,7 +1033,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
360
1033
  }
361
1034
  return elements;
362
1035
  }
363
- function findScriptJsonById(html, id) {
1036
+ function findScriptJsonById(html, id2) {
364
1037
  let pos = 0;
365
1038
  const len = html.length;
366
1039
  while (pos < len) {
@@ -377,7 +1050,7 @@ function findScriptJsonById(html, id) {
377
1050
  }
378
1051
  const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
379
1052
  const idAttr = extractAttribute(scriptMatch.tagContent, "id");
380
- if (typeAttr?.toLowerCase() === "application/json" && idAttr === id) {
1053
+ if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
381
1054
  const contentStart = scriptMatch.end;
382
1055
  const scriptCloseIdx = findCloseTag(html, contentStart, "script");
383
1056
  if (scriptCloseIdx === -1) return null;
@@ -392,8 +1065,8 @@ function matchOpenTag(html, pos, tagName) {
392
1065
  const nameStart = pos + 1;
393
1066
  const nameEnd = nameStart + tagName.length;
394
1067
  if (nameEnd > html.length) return null;
395
- const slice = html.slice(nameStart, nameEnd);
396
- if (slice.toLowerCase() !== tagName.toLowerCase()) return null;
1068
+ const slice3 = html.slice(nameStart, nameEnd);
1069
+ if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
397
1070
  const charAfter = html[nameEnd];
398
1071
  if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
399
1072
  return null;
@@ -512,6 +1185,39 @@ function analyzeWatchEntries(scriptContent) {
512
1185
  }
513
1186
  return entries;
514
1187
  }
1188
+ function analyzeDeclarationSpans(scriptContent) {
1189
+ const root = locateDefaultExportObject(scriptContent);
1190
+ if (!root) return [];
1191
+ const out = [];
1192
+ for (const prop of parseTopLevelProperties(root.content)) {
1193
+ if (prop.nameStart === void 0 || prop.nameEnd === void 0) continue;
1194
+ out.push({
1195
+ name: prop.name,
1196
+ kind: prop.kind,
1197
+ start: root.start + prop.nameStart,
1198
+ end: root.start + prop.nameEnd
1199
+ });
1200
+ }
1201
+ return out;
1202
+ }
1203
+ function analyzeCallableBodies(scriptContent) {
1204
+ const root = locateDefaultExportObject(scriptContent);
1205
+ if (!root) return [];
1206
+ const out = [];
1207
+ for (const prop of parseTopLevelProperties(root.content)) {
1208
+ if (prop.kind !== "getter" && prop.kind !== "method") continue;
1209
+ if (prop.nameStart === void 0 || prop.nameEnd === void 0) continue;
1210
+ out.push({
1211
+ name: prop.name,
1212
+ kind: prop.kind,
1213
+ start: root.start + prop.nameStart,
1214
+ end: root.start + prop.nameEnd,
1215
+ body: prop.value ?? "",
1216
+ bodyStart: root.start + (prop.valueStart ?? 0)
1217
+ });
1218
+ }
1219
+ return out;
1220
+ }
515
1221
  function isNonFunctionLiteral(value) {
516
1222
  if (value === void 0) return false;
517
1223
  const trimmed = value.trim();
@@ -711,17 +1417,32 @@ function parseTopLevelProperties(objectContent) {
711
1417
  const braceStart = match.index + match[0].length - 1;
712
1418
  const body = extractBracedContent(objectContent, scan, braceStart);
713
1419
  regex.lastIndex = braceStart + body.length + 2;
1420
+ return { body, bodyStart: braceStart + 1 };
714
1421
  };
715
1422
  const accessorName = nameAt(1) ?? nameAt(2) ?? nameAt(3);
716
1423
  if (accessorName) {
717
- props.push({ name: accessorName, kind: "getter", nameStart: nameSpan[0], nameEnd: nameSpan[1] });
718
- skipBody();
1424
+ const { body, bodyStart } = skipBody();
1425
+ props.push({
1426
+ name: accessorName,
1427
+ kind: "getter",
1428
+ value: body,
1429
+ valueStart: bodyStart,
1430
+ nameStart: nameSpan[0],
1431
+ nameEnd: nameSpan[1]
1432
+ });
719
1433
  continue;
720
1434
  }
721
1435
  const methodName = nameAt(4) ?? nameAt(5) ?? nameAt(6);
722
1436
  if (methodName) {
723
- props.push({ name: methodName, kind: "method", nameStart: nameSpan[0], nameEnd: nameSpan[1] });
724
- skipBody();
1437
+ const { body, bodyStart } = skipBody();
1438
+ props.push({
1439
+ name: methodName,
1440
+ kind: "method",
1441
+ value: body,
1442
+ valueStart: bodyStart,
1443
+ nameStart: nameSpan[0],
1444
+ nameEnd: nameSpan[1]
1445
+ });
725
1446
  continue;
726
1447
  }
727
1448
  const propName = nameAt(7) ?? nameAt(8) ?? nameAt(9);
@@ -1007,6 +1728,37 @@ function getEnclosingFors(html, offset, bindAttrName = "data-wcs") {
1007
1728
  }
1008
1729
  return enclosing;
1009
1730
  }
1731
+ function countWildcardSegments(path) {
1732
+ let count = 0;
1733
+ for (const segment of path.split(".")) {
1734
+ if (segment === "*") count++;
1735
+ }
1736
+ return count;
1737
+ }
1738
+ function forPathOf(raw) {
1739
+ let path = raw.trim();
1740
+ const pipe = path.indexOf("|");
1741
+ if (pipe !== -1) path = path.slice(0, pipe).trim();
1742
+ const at = path.indexOf("@");
1743
+ if (at !== -1) path = path.slice(0, at).trim();
1744
+ return path;
1745
+ }
1746
+ function getAvailableWildcardRank(html, offset, bindAttrName = "data-wcs") {
1747
+ const chain = getEnclosingForPaths(html, offset, bindAttrName);
1748
+ if (chain.length === 0) return 0;
1749
+ let resolved = "";
1750
+ for (const raw of chain) {
1751
+ const path = forPathOf(raw);
1752
+ if (path === ".") {
1753
+ resolved = `${resolved}.*`;
1754
+ } else if (path.startsWith(".")) {
1755
+ resolved = `${resolved}.*.${path.slice(1)}`;
1756
+ } else {
1757
+ resolved = path;
1758
+ }
1759
+ }
1760
+ return countWildcardSegments(resolved) + 1;
1761
+ }
1010
1762
  function getForTemplateDepthAt(html, openPos, offset, bindAttrName) {
1011
1763
  const tagEnd = html.indexOf(">", openPos);
1012
1764
  if (tagEnd === -1 || tagEnd >= offset) return 0;
@@ -1037,8 +1789,8 @@ function getForTemplateDepthAt(html, openPos, offset, bindAttrName) {
1037
1789
  }
1038
1790
 
1039
1791
  // 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";
1792
+ function resolveLocale(locale3) {
1793
+ if (locale3 === void 0 || locale3 === "" || /^ja\b|^ja[-_]/i.test(locale3) || locale3.toLowerCase() === "ja") return "ja";
1042
1794
  return "en";
1043
1795
  }
1044
1796
  var JA_EXPECTED_LABEL = {
@@ -1060,6 +1812,10 @@ var ja = {
1060
1812
  omittedPathOutsideFor: (p) => `\u7701\u7565\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
1061
1813
  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
1814
  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`,
1815
+ 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`,
1816
+ 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`,
1817
+ getterCycle: (cycle) => `\u30D1\u30B9 getter \u304C\u5FAA\u74B0\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${cycle}`,
1818
+ 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
1819
  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
1820
  typeExpectation: (label, expected, resultType) => `"${label}" \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728\u306E\u578B: ${resultType}\uFF09`,
1065
1821
  filterUnknown: (n) => `\u30D5\u30A3\u30EB\u30BF "${n}" \u306F\u7D44\u307F\u8FBC\u307F\u30D5\u30A3\u30EB\u30BF\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
@@ -1110,6 +1866,10 @@ var en = {
1110
1866
  omittedPathOutsideFor: (p) => `Shorthand path "${p}" cannot be used outside a <template for>`,
1111
1867
  loopIndexOutsideFor: (p) => `Loop index "${p}" cannot be used outside a <template for>`,
1112
1868
  resolvedPathInUi: (p) => `Resolved path "${p}" cannot be used in a UI binding. Use a pattern path instead`,
1869
+ 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}`,
1870
+ wildcardRank: (subject, needed, available) => `${subject} needs ${needed} enclosing loop level(s) but the current scope provides ${available}`,
1871
+ getterCycle: (cycle) => `Path getters form a dependency cycle: ${cycle}`,
1872
+ 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
1873
  handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
1114
1874
  typeExpectation: (label, expected, resultType) => `"${label}" requires ${EN_EXPECTED_LABEL[expected]} (current type: ${resultType})`,
1115
1875
  filterUnknown: (n) => `Filter "${n}" is not a built-in filter`,
@@ -1142,15 +1902,15 @@ var en = {
1142
1902
  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
1903
  };
1144
1904
  var CATALOGS = { ja, en };
1145
- function getMessages(locale) {
1146
- return CATALOGS[resolveLocale(locale)];
1905
+ function getMessages(locale3) {
1906
+ return CATALOGS[resolveLocale(locale3)];
1147
1907
  }
1148
1908
 
1149
1909
  // src/service/bindingValidator.ts
1150
1910
  var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
1151
- function validateBindings(html, attrName, stateTagName = "wcs-state", locale, fileReader) {
1911
+ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader) {
1152
1912
  const diagnostics = [];
1153
- const msgs = getMessages(locale);
1913
+ const msgs = getMessages(locale3);
1154
1914
  const statePaths = getStatePathsFromHtml(html, stateTagName, fileReader);
1155
1915
  const pathsByState = /* @__PURE__ */ new Map();
1156
1916
  for (const p of statePaths) {
@@ -1327,6 +2087,24 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale, fi
1327
2087
  severity: "warning"
1328
2088
  });
1329
2089
  }
2090
+ if (insideFor && !pathTrimmed.startsWith(".") && !binding.includes("@")) {
2091
+ const indexMatch = /^\$(\d+)$/.exec(pathTrimmed);
2092
+ const needed = indexMatch !== null ? Number(indexMatch[1]) : pathTrimmed.includes("*") ? countWildcardSegments(pathTrimmed) : 0;
2093
+ if (needed > 0) {
2094
+ const available = getAvailableWildcardRank(html, attr.valueStart, attrName);
2095
+ if (available > 0 && needed > available) {
2096
+ const pathOffset = binding.indexOf(parsed.path);
2097
+ const pathStart = bindingStart + pathOffset;
2098
+ diagnostics.push({
2099
+ code: WcsDiagnosticCode.WildcardRank,
2100
+ start: pathStart,
2101
+ end: pathStart + pathTrimmed.length,
2102
+ message: msgs.wildcardRank(`"${pathTrimmed}"`, needed, available),
2103
+ severity: "warning"
2104
+ });
2105
+ }
2106
+ }
2107
+ }
1330
2108
  if (/\.\d+\.|\.\d+$/.test(pathTrimmed)) {
1331
2109
  const pathOffset = binding.indexOf(parsed.path);
1332
2110
  const pathStart = bindingStart + pathOffset;
@@ -1670,8 +2448,8 @@ function isLiteral(value) {
1670
2448
  }
1671
2449
 
1672
2450
  // src/service/stateTypeValidator.ts
1673
- function validateStateTypes(html, stateTagName = "wcs-state", locale) {
1674
- const msgs = getMessages(locale);
2451
+ function validateStateTypes(html, stateTagName = "wcs-state", locale3) {
2452
+ const msgs = getMessages(locale3);
1675
2453
  const blocks = parseWcsScriptBlocks(html, stateTagName);
1676
2454
  const diagnostics = [];
1677
2455
  for (const block of blocks) {
@@ -1798,8 +2576,8 @@ function isApiRoot(root) {
1798
2576
  // src/service/nestedAssignValidator.ts
1799
2577
  var NESTED_ASSIGN = new RegExp(`${ROOT_DOT}(${CHAIN_ONE_PLUS})${ASSIGN_TAIL}`, "g");
1800
2578
  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);
2579
+ function validateNestedAssigns(html, stateTagName = "wcs-state", locale3) {
2580
+ const msgs = getMessages(locale3);
1803
2581
  const blocks = parseWcsScriptBlocks(html, stateTagName);
1804
2582
  const diagnostics = [];
1805
2583
  for (const block of blocks) {
@@ -1821,7 +2599,7 @@ function findNestedAssigns(script, baseOffset, msgs, out) {
1821
2599
  start,
1822
2600
  end: start + full.length,
1823
2601
  message: msgs.nestedAssign(suggestedPath),
1824
- severity: "warning"
2602
+ severity: "error"
1825
2603
  });
1826
2604
  }
1827
2605
  }
@@ -1850,8 +2628,8 @@ var PRE_BRACKET_INDEX = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}(${BRACKETS_ONLY
1850
2628
  function toAccessor(path) {
1851
2629
  return /^[A-Za-z_]\w*$/.test(path) ? `this.${path}` : `this["${path}"]`;
1852
2630
  }
1853
- function validateArrayMutations(html, stateTagName = "wcs-state", locale) {
1854
- const msgs = getMessages(locale);
2631
+ function validateArrayMutations(html, stateTagName = "wcs-state", locale3) {
2632
+ const msgs = getMessages(locale3);
1855
2633
  const blocks = parseWcsScriptBlocks(html, stateTagName);
1856
2634
  const diagnostics = [];
1857
2635
  for (const block of blocks) {
@@ -1874,7 +2652,7 @@ function findDestructiveCalls(script, baseOffset, msgs, out) {
1874
2652
  start,
1875
2653
  end: start + full.length,
1876
2654
  message: msgs.arrayMutation(method, ALTERNATIVES[method](toAccessor(statePath))),
1877
- severity: "warning",
2655
+ severity: "error",
1878
2656
  statePath
1879
2657
  });
1880
2658
  }
@@ -1894,7 +2672,7 @@ function findIndexAssigns(script, baseOffset, msgs, out) {
1894
2672
  start,
1895
2673
  end: start + full.length,
1896
2674
  message: msgs.arrayIndexAssign(suggestedPath),
1897
- severity: "warning",
2675
+ severity: "error",
1898
2676
  statePath: suggestedPath
1899
2677
  });
1900
2678
  }
@@ -1961,9 +2739,9 @@ function isInsideTag(html, offset, tagName) {
1961
2739
  }
1962
2740
 
1963
2741
  // src/service/templateSyntaxValidator.ts
1964
- function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale, fileReader) {
2742
+ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader) {
1965
2743
  const diagnostics = [];
1966
- const msgs = getMessages(locale);
2744
+ const msgs = getMessages(locale3);
1967
2745
  const allPaths = getStatePathsFromHtml(html, stateTagName, fileReader);
1968
2746
  if (allPaths.length === 0) return diagnostics;
1969
2747
  const defaultPaths = allPaths.filter((p) => p.stateName === "default");
@@ -2015,6 +2793,22 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
2015
2793
  severity: "warning"
2016
2794
  });
2017
2795
  }
2796
+ if (insideFor && !pathPart.startsWith(".") && !pathPart.includes("@")) {
2797
+ const indexMatch = /^\$(\d+)$/.exec(pathPart);
2798
+ const needed = indexMatch !== null ? Number(indexMatch[1]) : pathPart.includes("*") ? countWildcardSegments(pathPart) : 0;
2799
+ if (needed > 0) {
2800
+ const available = getAvailableWildcardRank(html, item.matchStart, bindAttrName);
2801
+ if (available > 0 && needed > available) {
2802
+ diagnostics.push({
2803
+ code: WcsDiagnosticCode.WildcardRank,
2804
+ start: item.exprStart,
2805
+ end: item.exprStart + pathPart.length,
2806
+ message: msgs.wildcardRank(`"${pathPart}"`, needed, available),
2807
+ severity: "warning"
2808
+ });
2809
+ }
2810
+ }
2811
+ }
2018
2812
  if (/\.\d+\.|\.\d+$/.test(pathPart)) {
2019
2813
  diagnostics.push({
2020
2814
  code: WcsDiagnosticCode.TemplateSyntax,
@@ -3314,9 +4108,9 @@ var DOM_COMMON_PROPERTIES = /* @__PURE__ */ new Set([
3314
4108
  ]);
3315
4109
  var STRUCTURAL_DIRECTIVES2 = /* @__PURE__ */ new Set(["for", "if", "elseif", "else"]);
3316
4110
  var EMPTYISH_SEEDS = /* @__PURE__ */ new Set(["''", '""', "``", "null", "[]", "{}"]);
3317
- function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale, fileReader) {
4111
+ function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale3, fileReader) {
3318
4112
  const diagnostics = [];
3319
- const msgs = getMessages(locale);
4113
+ const msgs = getMessages(locale3);
3320
4114
  const occurrences = findBuiltinTagOccurrences(html);
3321
4115
  if (occurrences.length === 0) return diagnostics;
3322
4116
  let statePaths = null;
@@ -3525,9 +4319,9 @@ function hasBooleanAttribute(attrsText, attrName) {
3525
4319
  }
3526
4320
 
3527
4321
  // src/service/documentEnvValidator.ts
3528
- function validateDocumentEnv(html, locale) {
4322
+ function validateDocumentEnv(html, locale3) {
3529
4323
  const diagnostics = [];
3530
- const msgs = getMessages(locale);
4324
+ const msgs = getMessages(locale3);
3531
4325
  const scanText = blankHtmlComments(html);
3532
4326
  const autos = findWcstackAutoScripts(scanText);
3533
4327
  const stateIndex = autos.findIndex((a) => a.pkg === "state");
@@ -3637,9 +4431,9 @@ function blankJsComments(code) {
3637
4431
  }
3638
4432
 
3639
4433
  // src/service/watchDeclarationValidator.ts
3640
- var STATE_NAME_SEPARATOR = "@";
3641
- function validateWatchDeclarations(html, stateTagName = "wcs-state", locale) {
3642
- const msgs = getMessages(locale);
4434
+ var STATE_NAME_SEPARATOR2 = "@";
4435
+ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
4436
+ const msgs = getMessages(locale3);
3643
4437
  const out = [];
3644
4438
  for (const block of parseWcsScriptBlocks(html, stateTagName)) {
3645
4439
  const nonObject = findNonObjectWatch(block.content);
@@ -3673,7 +4467,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale) {
3673
4467
  function validateEntry(entry, pathSet, msgs) {
3674
4468
  const { key } = entry;
3675
4469
  const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
3676
- if (key.includes(STATE_NAME_SEPARATOR)) {
4470
+ if (key.includes(STATE_NAME_SEPARATOR2)) {
3677
4471
  return invalid(msgs.watchKeyCrossState(key));
3678
4472
  }
3679
4473
  if (key.startsWith("$")) {
@@ -3695,43 +4489,1504 @@ function validateEntry(entry, pathSet, msgs) {
3695
4489
  return null;
3696
4490
  }
3697
4491
 
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;
3703
- const fileReader = options.fileReader;
3704
- 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)) {
3712
- out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
4492
+ // ../state/dist/parser.esm.js
4493
+ var DELIMITER2 = ".";
4494
+ var WILDCARD2 = "*";
4495
+ var MAX_WILDCARD_DEPTH2 = 128;
4496
+ var BINDING_SEPARATOR2 = ";";
4497
+ var PROP_VALUE_SEPARATOR2 = ":";
4498
+ var MODIFIER_SEPARATOR2 = "#";
4499
+ var STATE_NAME_SEPARATOR3 = "@";
4500
+ var FILTER_SEPARATOR2 = "|";
4501
+ var ELSE_KEYWORD2 = "else";
4502
+ var SPREAD_PROP2 = "...";
4503
+ var EVENT_PROP_PREFIX2 = "on";
4504
+ var EVENT_TOKEN_NAMESPACE2 = "eventToken";
4505
+ var INDEX_PARAM_PREFIX2 = "$";
4506
+ var tmpIndexByIndexName2 = {};
4507
+ for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
4508
+ tmpIndexByIndexName2[`${INDEX_PARAM_PREFIX2}${i + 1}`] = i;
4509
+ }
4510
+ Object.freeze(tmpIndexByIndexName2);
4511
+ var _cache = /* @__PURE__ */ new Map();
4512
+ function clearPathInfoCacheForTooling() {
4513
+ _cache.clear();
4514
+ }
4515
+ var id = 0;
4516
+ function getPathInfo(path) {
4517
+ let pathInfo = _cache.get(path);
4518
+ if (typeof pathInfo !== "undefined") {
4519
+ return pathInfo;
4520
+ }
4521
+ pathInfo = Object.freeze(new PathInfo(path));
4522
+ _cache.set(path, pathInfo);
4523
+ return pathInfo;
4524
+ }
4525
+ var PathInfo = class {
4526
+ id = ++id;
4527
+ path;
4528
+ segments;
4529
+ lastSegment;
4530
+ cumulativePaths;
4531
+ cumulativePathSet;
4532
+ cumulativePathInfos;
4533
+ cumulativePathInfoSet;
4534
+ parentPath;
4535
+ wildcardPaths;
4536
+ wildcardPathSet;
4537
+ indexByWildcardPath;
4538
+ wildcardPathInfos;
4539
+ wildcardPathInfoSet;
4540
+ wildcardParentPaths;
4541
+ wildcardParentPathSet;
4542
+ wildcardParentPathInfos;
4543
+ wildcardParentPathInfoSet;
4544
+ wildcardPositions;
4545
+ lastWildcardPath;
4546
+ lastWildcardInfo;
4547
+ wildcardCount;
4548
+ parentPathInfo;
4549
+ constructor(path) {
4550
+ const getPattern = (_path) => {
4551
+ return path === _path ? this : getPathInfo(_path);
4552
+ };
4553
+ const segments = path.split(".");
4554
+ const cumulativePaths = [];
4555
+ const cumulativePathInfos = [];
4556
+ const wildcardPaths = [];
4557
+ const indexByWildcardPath = {};
4558
+ const wildcardPathInfos = [];
4559
+ const wildcardParentPaths = [];
4560
+ const wildcardParentPathInfos = [];
4561
+ const wildcardPositions = [];
4562
+ let currentPatternPath = "", prevPatternPath = "";
4563
+ let wildcardCount = 0;
4564
+ for (let i = 0; i < segments.length; i++) {
4565
+ currentPatternPath += segments[i];
4566
+ if (segments[i] === WILDCARD2) {
4567
+ wildcardPaths.push(currentPatternPath);
4568
+ indexByWildcardPath[currentPatternPath] = wildcardCount;
4569
+ wildcardPathInfos.push(getPattern(currentPatternPath));
4570
+ wildcardParentPaths.push(prevPatternPath);
4571
+ wildcardParentPathInfos.push(getPattern(prevPatternPath));
4572
+ wildcardPositions.push(i);
4573
+ wildcardCount++;
4574
+ }
4575
+ cumulativePaths.push(currentPatternPath);
4576
+ cumulativePathInfos.push(getPattern(currentPatternPath));
4577
+ prevPatternPath = currentPatternPath;
4578
+ currentPatternPath += ".";
4579
+ }
4580
+ const lastWildcardPath = wildcardPaths.length > 0 ? wildcardPaths[wildcardPaths.length - 1] : null;
4581
+ const parentPath = cumulativePaths.length > 1 ? cumulativePaths[cumulativePaths.length - 2] : null;
4582
+ this.path = path;
4583
+ this.segments = segments;
4584
+ this.lastSegment = segments[segments.length - 1];
4585
+ this.cumulativePaths = cumulativePaths;
4586
+ this.cumulativePathSet = new Set(cumulativePaths);
4587
+ this.cumulativePathInfos = cumulativePathInfos;
4588
+ this.cumulativePathInfoSet = new Set(cumulativePathInfos);
4589
+ this.wildcardPaths = wildcardPaths;
4590
+ this.wildcardPathSet = new Set(wildcardPaths);
4591
+ this.indexByWildcardPath = indexByWildcardPath;
4592
+ this.wildcardPathInfos = wildcardPathInfos;
4593
+ this.wildcardPathInfoSet = new Set(wildcardPathInfos);
4594
+ this.wildcardParentPaths = wildcardParentPaths;
4595
+ this.wildcardParentPathSet = new Set(wildcardParentPaths);
4596
+ this.wildcardParentPathInfos = wildcardParentPathInfos;
4597
+ this.wildcardParentPathInfoSet = new Set(wildcardParentPathInfos);
4598
+ this.wildcardPositions = wildcardPositions;
4599
+ this.lastWildcardPath = lastWildcardPath;
4600
+ this.lastWildcardInfo = lastWildcardPath ? getPattern(lastWildcardPath) : null;
4601
+ this.parentPath = parentPath;
4602
+ this.parentPathInfo = parentPath ? getPattern(parentPath) : null;
4603
+ this.wildcardCount = wildcardCount;
3713
4604
  }
3714
- for (const d of validateNestedAssigns(text, stateTagName, locale)) {
3715
- out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
4605
+ };
4606
+ function editDistance2(a, b, max) {
4607
+ if (Math.abs(a.length - b.length) > max) {
4608
+ return max + 1;
3716
4609
  }
3717
- return sortDiagnostics(out);
3718
- }
3719
-
3720
- // src/core/sidecar/schemaSubset.ts
3721
- var ALLOWED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
3722
- "type",
3723
- "properties",
3724
- "required",
3725
- "items",
3726
- "enum",
3727
- "const",
3728
- "anyOf",
3729
- "$defs",
3730
- "$ref"
3731
- ]);
3732
- var DiagnosticContext = class {
3733
- constructor(spans) {
3734
- this.spans = spans;
4610
+ const prev = new Array(b.length + 1);
4611
+ const curr = new Array(b.length + 1);
4612
+ for (let j = 0; j <= b.length; j++) {
4613
+ prev[j] = j;
4614
+ }
4615
+ for (let i = 1; i <= a.length; i++) {
4616
+ curr[0] = i;
4617
+ for (let j = 1; j <= b.length; j++) {
4618
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
4619
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
4620
+ }
4621
+ for (let j = 0; j <= b.length; j++) {
4622
+ prev[j] = curr[j];
4623
+ }
4624
+ }
4625
+ return prev[b.length];
4626
+ }
4627
+ function didYouMean(input, candidates) {
4628
+ if (input.length === 0) {
4629
+ return "";
4630
+ }
4631
+ const folded = input.toLowerCase();
4632
+ let best = null;
4633
+ let bestDistance = 3;
4634
+ for (const candidate of candidates) {
4635
+ const distance = editDistance2(folded, candidate.toLowerCase(), 2);
4636
+ if (distance < bestDistance) {
4637
+ best = candidate;
4638
+ bestDistance = distance;
4639
+ }
4640
+ }
4641
+ return best !== null ? ` Did you mean "${best}"?` : "";
4642
+ }
4643
+ var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
4644
+ function raiseError2(message) {
4645
+ throw new Error(`[@wcstack/state] ${message}`);
4646
+ }
4647
+ var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
4648
+ "if",
4649
+ "elseif",
4650
+ "else",
4651
+ "for"
4652
+ ]);
4653
+ var _config2 = {
4654
+ locale: "en"
4655
+ };
4656
+ var config2 = _config2;
4657
+ function optionsRequired2(fnName) {
4658
+ raiseError2(`filter ${fnName} requires at least one option`);
4659
+ }
4660
+ function optionMustBeNumber2(fnName) {
4661
+ raiseError2(`filter ${fnName} requires a number as option`);
4662
+ }
4663
+ function valueMustBeNumber2(fnName) {
4664
+ raiseError2(`filter ${fnName} requires a number value`);
4665
+ }
4666
+ function valueMustBeBoolean2(fnName) {
4667
+ raiseError2(`filter ${fnName} requires a boolean value`);
4668
+ }
4669
+ function valueMustBeDate2(fnName) {
4670
+ raiseError2(`filter ${fnName} requires a date value`);
4671
+ }
4672
+ function valueMustBeArray2(fnName) {
4673
+ raiseError2(`filter ${fnName} requires an array value`);
4674
+ }
4675
+ function validateNumberString2(value) {
4676
+ if (!value || isNaN(Number(value))) {
4677
+ return false;
4678
+ }
4679
+ return true;
4680
+ }
4681
+ var eq2 = (options) => {
4682
+ const opt = options?.[0] ?? optionsRequired2("eq");
4683
+ return (value) => {
4684
+ if (typeof value === "number") {
4685
+ if (!validateNumberString2(opt)) {
4686
+ optionMustBeNumber2("eq");
4687
+ }
4688
+ return value === Number(opt);
4689
+ }
4690
+ if (typeof value === "string") {
4691
+ return value === opt;
4692
+ }
4693
+ return value === opt;
4694
+ };
4695
+ };
4696
+ var ne2 = (options) => {
4697
+ const opt = options?.[0] ?? optionsRequired2("ne");
4698
+ return (value) => {
4699
+ if (typeof value === "number") {
4700
+ if (!validateNumberString2(opt)) {
4701
+ optionMustBeNumber2("ne");
4702
+ }
4703
+ return value !== Number(opt);
4704
+ }
4705
+ if (typeof value === "string") {
4706
+ return value !== opt;
4707
+ }
4708
+ return value !== opt;
4709
+ };
4710
+ };
4711
+ var not2 = (_options) => {
4712
+ return (value) => {
4713
+ if (typeof value !== "boolean") {
4714
+ valueMustBeBoolean2("not");
4715
+ }
4716
+ return !value;
4717
+ };
4718
+ };
4719
+ var lt2 = (options) => {
4720
+ const opt = options?.[0] ?? optionsRequired2("lt");
4721
+ if (!validateNumberString2(opt)) {
4722
+ optionMustBeNumber2("lt");
4723
+ }
4724
+ return (value) => {
4725
+ if (typeof value !== "number") {
4726
+ valueMustBeNumber2("lt");
4727
+ }
4728
+ return value < Number(opt);
4729
+ };
4730
+ };
4731
+ var le2 = (options) => {
4732
+ const opt = options?.[0] ?? optionsRequired2("le");
4733
+ if (!validateNumberString2(opt)) {
4734
+ optionMustBeNumber2("le");
4735
+ }
4736
+ return (value) => {
4737
+ if (typeof value !== "number") {
4738
+ valueMustBeNumber2("le");
4739
+ }
4740
+ return value <= Number(opt);
4741
+ };
4742
+ };
4743
+ var gt2 = (options) => {
4744
+ const opt = options?.[0] ?? optionsRequired2("gt");
4745
+ if (!validateNumberString2(opt)) {
4746
+ optionMustBeNumber2("gt");
4747
+ }
4748
+ return (value) => {
4749
+ if (typeof value !== "number") {
4750
+ valueMustBeNumber2("gt");
4751
+ }
4752
+ return value > Number(opt);
4753
+ };
4754
+ };
4755
+ var ge2 = (options) => {
4756
+ const opt = options?.[0] ?? optionsRequired2("ge");
4757
+ if (!validateNumberString2(opt)) {
4758
+ optionMustBeNumber2("ge");
4759
+ }
4760
+ return (value) => {
4761
+ if (typeof value !== "number") {
4762
+ valueMustBeNumber2("ge");
4763
+ }
4764
+ return value >= Number(opt);
4765
+ };
4766
+ };
4767
+ var inc2 = (options) => {
4768
+ const opt = options?.[0] ?? optionsRequired2("inc");
4769
+ if (!validateNumberString2(opt)) {
4770
+ optionMustBeNumber2("inc");
4771
+ }
4772
+ return (value) => {
4773
+ if (typeof value !== "number") {
4774
+ valueMustBeNumber2("inc");
4775
+ }
4776
+ return value + Number(opt);
4777
+ };
4778
+ };
4779
+ var dec2 = (options) => {
4780
+ const opt = options?.[0] ?? optionsRequired2("dec");
4781
+ if (!validateNumberString2(opt)) {
4782
+ optionMustBeNumber2("dec");
4783
+ }
4784
+ return (value) => {
4785
+ if (typeof value !== "number") {
4786
+ valueMustBeNumber2("dec");
4787
+ }
4788
+ return value - Number(opt);
4789
+ };
4790
+ };
4791
+ var mul2 = (options) => {
4792
+ const opt = options?.[0] ?? optionsRequired2("mul");
4793
+ if (!validateNumberString2(opt)) {
4794
+ optionMustBeNumber2("mul");
4795
+ }
4796
+ return (value) => {
4797
+ if (typeof value !== "number") {
4798
+ valueMustBeNumber2("mul");
4799
+ }
4800
+ return value * Number(opt);
4801
+ };
4802
+ };
4803
+ var div2 = (options) => {
4804
+ const opt = options?.[0] ?? optionsRequired2("div");
4805
+ if (!validateNumberString2(opt)) {
4806
+ optionMustBeNumber2("div");
4807
+ }
4808
+ return (value) => {
4809
+ if (typeof value !== "number") {
4810
+ valueMustBeNumber2("div");
4811
+ }
4812
+ return value / Number(opt);
4813
+ };
4814
+ };
4815
+ var mod2 = (options) => {
4816
+ const opt = options?.[0] ?? optionsRequired2("mod");
4817
+ if (!validateNumberString2(opt)) {
4818
+ optionMustBeNumber2("mod");
4819
+ }
4820
+ return (value) => {
4821
+ if (typeof value !== "number") {
4822
+ valueMustBeNumber2("mod");
4823
+ }
4824
+ return value % Number(opt);
4825
+ };
4826
+ };
4827
+ var abs2 = (_options) => {
4828
+ return (value) => {
4829
+ if (typeof value !== "number") {
4830
+ valueMustBeNumber2("abs");
4831
+ }
4832
+ return Math.abs(value);
4833
+ };
4834
+ };
4835
+ var clamp2 = (options) => {
4836
+ const opt1 = options?.[0] ?? optionsRequired2("clamp");
4837
+ if (!validateNumberString2(opt1)) {
4838
+ optionMustBeNumber2("clamp");
4839
+ }
4840
+ const opt2 = options?.[1] ?? optionsRequired2("clamp");
4841
+ if (!validateNumberString2(opt2)) {
4842
+ optionMustBeNumber2("clamp");
4843
+ }
4844
+ const min = Number(opt1);
4845
+ const max = Number(opt2);
4846
+ return (value) => {
4847
+ if (typeof value !== "number") {
4848
+ valueMustBeNumber2("clamp");
4849
+ }
4850
+ return Math.min(Math.max(value, min), max);
4851
+ };
4852
+ };
4853
+ var fix2 = (options) => {
4854
+ const opt = options?.[0] ?? "0";
4855
+ if (!validateNumberString2(opt)) {
4856
+ optionMustBeNumber2("fix");
4857
+ }
4858
+ return (value) => {
4859
+ if (typeof value !== "number") {
4860
+ valueMustBeNumber2("fix");
4861
+ }
4862
+ return value.toFixed(Number(opt));
4863
+ };
4864
+ };
4865
+ var locale2 = (options) => {
4866
+ const opt = options?.[0] ?? config2.locale;
4867
+ return (value) => {
4868
+ if (typeof value !== "number") {
4869
+ valueMustBeNumber2("locale");
4870
+ }
4871
+ return value.toLocaleString(opt);
4872
+ };
4873
+ };
4874
+ var uc2 = (_options) => {
4875
+ return (value) => {
4876
+ return String(value).toUpperCase();
4877
+ };
4878
+ };
4879
+ var lc2 = (_options) => {
4880
+ return (value) => {
4881
+ return String(value).toLowerCase();
4882
+ };
4883
+ };
4884
+ var cap2 = (_options) => {
4885
+ return (value) => {
4886
+ const v = String(value);
4887
+ if (v.length === 0) {
4888
+ return v;
4889
+ }
4890
+ if (v.length === 1) {
4891
+ return v.toUpperCase();
4892
+ }
4893
+ return v.charAt(0).toUpperCase() + v.slice(1);
4894
+ };
4895
+ };
4896
+ var trim2 = (_options) => {
4897
+ return (value) => {
4898
+ return String(value).trim();
4899
+ };
4900
+ };
4901
+ var slice2 = (options) => {
4902
+ const numberedOpts = [];
4903
+ const opt1 = options?.[0] ?? optionsRequired2("slice");
4904
+ if (!validateNumberString2(opt1)) {
4905
+ optionMustBeNumber2("slice");
4906
+ }
4907
+ numberedOpts.push(Number(opt1));
4908
+ const opt2 = options?.[1];
4909
+ if (typeof opt2 !== "undefined") {
4910
+ if (!validateNumberString2(opt2)) {
4911
+ optionMustBeNumber2("slice");
4912
+ }
4913
+ numberedOpts.push(Number(opt2));
4914
+ }
4915
+ return (value) => {
4916
+ return String(value).slice(...numberedOpts);
4917
+ };
4918
+ };
4919
+ var substr2 = (options) => {
4920
+ const opt1 = options?.[0] ?? optionsRequired2("substr");
4921
+ if (!validateNumberString2(opt1)) {
4922
+ optionMustBeNumber2("substr");
4923
+ }
4924
+ const opt2 = options?.[1] ?? optionsRequired2("substr");
4925
+ if (!validateNumberString2(opt2)) {
4926
+ optionMustBeNumber2("substr");
4927
+ }
4928
+ return (value) => {
4929
+ return String(value).substr(Number(opt1), Number(opt2));
4930
+ };
4931
+ };
4932
+ var pad2 = (options) => {
4933
+ const opt1 = options?.[0] ?? optionsRequired2("pad");
4934
+ if (!validateNumberString2(opt1)) {
4935
+ optionMustBeNumber2("pad");
4936
+ }
4937
+ const opt2 = options?.[1] ?? "0";
4938
+ return (value) => {
4939
+ return String(value).padStart(Number(opt1), opt2);
4940
+ };
4941
+ };
4942
+ var rep2 = (options) => {
4943
+ const opt = options?.[0] ?? optionsRequired2("rep");
4944
+ if (!validateNumberString2(opt)) {
4945
+ optionMustBeNumber2("rep");
4946
+ }
4947
+ return (value) => {
4948
+ return String(value).repeat(Number(opt));
4949
+ };
4950
+ };
4951
+ var rev2 = (_options) => {
4952
+ return (value) => {
4953
+ return String(value).split("").reverse().join("");
4954
+ };
4955
+ };
4956
+ var int2 = (_options) => {
4957
+ return (value) => {
4958
+ return parseInt(String(value), 10);
4959
+ };
4960
+ };
4961
+ var float2 = (_options) => {
4962
+ return (value) => {
4963
+ return parseFloat(String(value));
4964
+ };
4965
+ };
4966
+ var round2 = (options) => {
4967
+ const opt = options?.[0] ?? "0";
4968
+ if (!validateNumberString2(opt)) {
4969
+ optionMustBeNumber2("round");
4970
+ }
4971
+ return (value) => {
4972
+ if (typeof value !== "number") {
4973
+ valueMustBeNumber2("round");
4974
+ }
4975
+ const optValue = Math.pow(10, Number(opt));
4976
+ return Math.round(value * optValue) / optValue;
4977
+ };
4978
+ };
4979
+ var floor2 = (options) => {
4980
+ const opt = options?.[0] ?? "0";
4981
+ if (!validateNumberString2(opt)) {
4982
+ optionMustBeNumber2("floor");
4983
+ }
4984
+ return (value) => {
4985
+ if (typeof value !== "number") {
4986
+ valueMustBeNumber2("floor");
4987
+ }
4988
+ const optValue = Math.pow(10, Number(opt));
4989
+ return Math.floor(value * optValue) / optValue;
4990
+ };
4991
+ };
4992
+ var ceil2 = (options) => {
4993
+ const opt = options?.[0] ?? "0";
4994
+ if (!validateNumberString2(opt)) {
4995
+ optionMustBeNumber2("ceil");
4996
+ }
4997
+ return (value) => {
4998
+ if (typeof value !== "number") {
4999
+ valueMustBeNumber2("ceil");
5000
+ }
5001
+ const optValue = Math.pow(10, Number(opt));
5002
+ return Math.ceil(value * optValue) / optValue;
5003
+ };
5004
+ };
5005
+ var percent2 = (options) => {
5006
+ const opt = options?.[0] ?? "0";
5007
+ if (!validateNumberString2(opt)) {
5008
+ optionMustBeNumber2("percent");
5009
+ }
5010
+ return (value) => {
5011
+ if (typeof value !== "number") {
5012
+ valueMustBeNumber2("percent");
5013
+ }
5014
+ return `${(value * 100).toFixed(Number(opt))}%`;
5015
+ };
5016
+ };
5017
+ var unit2 = (options) => {
5018
+ const opt = options?.[0] ?? optionsRequired2("unit");
5019
+ return (value) => {
5020
+ if (value === null || typeof value === "undefined") {
5021
+ return value;
5022
+ }
5023
+ return String(value) + opt;
5024
+ };
5025
+ };
5026
+ var join2 = (options) => {
5027
+ const opt = options?.[0] ?? ", ";
5028
+ return (value) => {
5029
+ if (!Array.isArray(value)) {
5030
+ valueMustBeArray2("join");
5031
+ }
5032
+ return value.join(opt);
5033
+ };
5034
+ };
5035
+ var truncate2 = (options) => {
5036
+ const opt1 = options?.[0] ?? optionsRequired2("truncate");
5037
+ if (!validateNumberString2(opt1)) {
5038
+ optionMustBeNumber2("truncate");
5039
+ }
5040
+ const maxLength = Number(opt1);
5041
+ const suffix = options?.[1] ?? "\u2026";
5042
+ return (value) => {
5043
+ const v = String(value);
5044
+ if (v.length <= maxLength) {
5045
+ return v;
5046
+ }
5047
+ return v.slice(0, maxLength) + suffix;
5048
+ };
5049
+ };
5050
+ var date2 = (options) => {
5051
+ const opt = options?.[0] ?? config2.locale;
5052
+ return (value) => {
5053
+ if (!(value instanceof Date)) {
5054
+ valueMustBeDate2("date");
5055
+ }
5056
+ return value.toLocaleDateString(opt);
5057
+ };
5058
+ };
5059
+ var time2 = (options) => {
5060
+ const opt = options?.[0] ?? config2.locale;
5061
+ return (value) => {
5062
+ if (!(value instanceof Date)) {
5063
+ valueMustBeDate2("time");
5064
+ }
5065
+ return value.toLocaleTimeString(opt);
5066
+ };
5067
+ };
5068
+ var datetime2 = (options) => {
5069
+ const opt = options?.[0] ?? config2.locale;
5070
+ return (value) => {
5071
+ if (!(value instanceof Date)) {
5072
+ valueMustBeDate2("datetime");
5073
+ }
5074
+ return value.toLocaleString(opt);
5075
+ };
5076
+ };
5077
+ var ymd2 = (options) => {
5078
+ const opt = options?.[0] ?? "-";
5079
+ return (value) => {
5080
+ if (!(value instanceof Date)) {
5081
+ valueMustBeDate2("ymd");
5082
+ }
5083
+ const year = value.getFullYear().toString();
5084
+ const month = (value.getMonth() + 1).toString().padStart(2, "0");
5085
+ const day = value.getDate().toString().padStart(2, "0");
5086
+ return `${year}${opt}${month}${opt}${day}`;
5087
+ };
5088
+ };
5089
+ var hms2 = (options) => {
5090
+ const opt = options?.[0] ?? ":";
5091
+ return (value) => {
5092
+ if (!(value instanceof Date)) {
5093
+ valueMustBeDate2("hms");
5094
+ }
5095
+ const hours = value.getHours().toString().padStart(2, "0");
5096
+ const minutes = value.getMinutes().toString().padStart(2, "0");
5097
+ const seconds = value.getSeconds().toString().padStart(2, "0");
5098
+ return `${hours}${opt}${minutes}${opt}${seconds}`;
5099
+ };
5100
+ };
5101
+ var falsy2 = (_options) => {
5102
+ return (value) => value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value);
5103
+ };
5104
+ var truthy2 = (_options) => {
5105
+ return (value) => value !== false && value !== null && value !== void 0 && value !== 0 && value !== "" && !Number.isNaN(value);
5106
+ };
5107
+ var defaults2 = (options) => {
5108
+ const opt = options?.[0] ?? optionsRequired2("defaults");
5109
+ return (value) => {
5110
+ if (value === false || value === null || value === void 0 || value === 0 || value === "" || Number.isNaN(value)) {
5111
+ return opt;
5112
+ }
5113
+ return value;
5114
+ };
5115
+ };
5116
+ var boolean2 = (_options) => {
5117
+ return (value) => {
5118
+ return Boolean(value);
5119
+ };
5120
+ };
5121
+ var number2 = (_options) => {
5122
+ return (value) => {
5123
+ return Number(value);
5124
+ };
5125
+ };
5126
+ var string2 = (_options) => {
5127
+ return (value) => {
5128
+ return String(value);
5129
+ };
5130
+ };
5131
+ var _null2 = (_options) => {
5132
+ return (value) => {
5133
+ return value === "" ? null : value;
5134
+ };
5135
+ };
5136
+ var builtinFilters2 = {
5137
+ "eq": eq2,
5138
+ "ne": ne2,
5139
+ "not": not2,
5140
+ "lt": lt2,
5141
+ "le": le2,
5142
+ "gt": gt2,
5143
+ "ge": ge2,
5144
+ "inc": inc2,
5145
+ "dec": dec2,
5146
+ "mul": mul2,
5147
+ "div": div2,
5148
+ "mod": mod2,
5149
+ "abs": abs2,
5150
+ "clamp": clamp2,
5151
+ "fix": fix2,
5152
+ "locale": locale2,
5153
+ "uc": uc2,
5154
+ "lc": lc2,
5155
+ "cap": cap2,
5156
+ "trim": trim2,
5157
+ "slice": slice2,
5158
+ "substr": substr2,
5159
+ "pad": pad2,
5160
+ "rep": rep2,
5161
+ "rev": rev2,
5162
+ "truncate": truncate2,
5163
+ "join": join2,
5164
+ "int": int2,
5165
+ "float": float2,
5166
+ "round": round2,
5167
+ "floor": floor2,
5168
+ "ceil": ceil2,
5169
+ "percent": percent2,
5170
+ "unit": unit2,
5171
+ "date": date2,
5172
+ "time": time2,
5173
+ "datetime": datetime2,
5174
+ "ymd": ymd2,
5175
+ "hms": hms2,
5176
+ "falsy": falsy2,
5177
+ "truthy": truthy2,
5178
+ "defaults": defaults2,
5179
+ "boolean": boolean2,
5180
+ "number": number2,
5181
+ "string": string2,
5182
+ "null": _null2
5183
+ };
5184
+ var outputBuiltinFilters2 = builtinFilters2;
5185
+ var inputBuiltinFilters = builtinFilters2;
5186
+ var builtinFiltersByFilterIOType = {
5187
+ "input": inputBuiltinFilters,
5188
+ "output": outputBuiltinFilters2
5189
+ };
5190
+ var builtinFilterFn = (name, options) => (filters) => {
5191
+ const filter = filters[name];
5192
+ if (!filter) {
5193
+ raiseError2(`[wcs/filter-unknown] filter not found: ${name}.${didYouMean(name, Object.keys(filters))}${LINT_HINT}`);
5194
+ }
5195
+ return filter(options);
5196
+ };
5197
+ function finalizeArg(text, firstQuoteStart, lastQuoteEnd) {
5198
+ const startLimit = firstQuoteStart === -1 ? text.length : firstQuoteStart;
5199
+ let start = 0;
5200
+ while (start < startLimit && /\s/.test(text[start])) {
5201
+ start++;
5202
+ }
5203
+ const endLimit = lastQuoteEnd === -1 ? 0 : lastQuoteEnd;
5204
+ let end = text.length;
5205
+ while (end > endLimit && /\s/.test(text[end - 1])) {
5206
+ end--;
5207
+ }
5208
+ return text.slice(start, end);
5209
+ }
5210
+ function parseFilterArgs(argsText) {
5211
+ const args = [];
5212
+ let current = "";
5213
+ let inQuote = null;
5214
+ let hasQuote = false;
5215
+ let firstQuoteStart = -1;
5216
+ let lastQuoteEnd = -1;
5217
+ const flush = () => {
5218
+ args.push(finalizeArg(current, firstQuoteStart, lastQuoteEnd));
5219
+ current = "";
5220
+ hasQuote = false;
5221
+ firstQuoteStart = -1;
5222
+ lastQuoteEnd = -1;
5223
+ };
5224
+ for (let i = 0; i < argsText.length; i++) {
5225
+ const char = argsText[i];
5226
+ if (inQuote) {
5227
+ if (char === inQuote) {
5228
+ inQuote = null;
5229
+ } else {
5230
+ if (firstQuoteStart === -1) {
5231
+ firstQuoteStart = current.length;
5232
+ }
5233
+ current += char;
5234
+ lastQuoteEnd = current.length;
5235
+ }
5236
+ } else if (char === '"' || char === "'") {
5237
+ inQuote = char;
5238
+ hasQuote = true;
5239
+ } else if (char === ",") {
5240
+ flush();
5241
+ } else {
5242
+ current += char;
5243
+ }
5244
+ }
5245
+ const last = finalizeArg(current, firstQuoteStart, lastQuoteEnd);
5246
+ if (last || hasQuote) {
5247
+ args.push(last);
5248
+ }
5249
+ return args;
5250
+ }
5251
+ var filterFnByKey = /* @__PURE__ */ new Map();
5252
+ function clearFilterFnCacheForTooling() {
5253
+ filterFnByKey.clear();
5254
+ }
5255
+ function parseFilters(filterTextList, filterIOType) {
5256
+ const builtinFilters3 = builtinFiltersByFilterIOType[filterIOType];
5257
+ const filters = filterTextList.map((filterText) => {
5258
+ const openParenIndex = filterText.indexOf("(");
5259
+ const closeParenIndex = filterText.lastIndexOf(")");
5260
+ if (openParenIndex !== -1 && closeParenIndex === -1) {
5261
+ raiseError2(`Invalid filter format: missing closing parenthesis in "${filterText}"`);
5262
+ }
5263
+ if (closeParenIndex !== -1 && openParenIndex === -1) {
5264
+ raiseError2(`Invalid filter format: missing opening parenthesis in "${filterText}"`);
5265
+ }
5266
+ if (openParenIndex === -1) {
5267
+ const filterName = filterText.trim();
5268
+ const filterKey = `${filterName}():${filterIOType}`;
5269
+ let filterFn = filterFnByKey.get(filterKey);
5270
+ if (typeof filterFn === "undefined") {
5271
+ filterFn = builtinFilterFn(filterName, [])(builtinFilters3);
5272
+ filterFnByKey.set(filterKey, filterFn);
5273
+ }
5274
+ return {
5275
+ filterName,
5276
+ args: [],
5277
+ filterFn
5278
+ };
5279
+ } else {
5280
+ const argsText = filterText.substring(openParenIndex + 1, closeParenIndex);
5281
+ const filterName = filterText.substring(0, openParenIndex).trim();
5282
+ const args = parseFilterArgs(argsText);
5283
+ const filterKey = `${filterName}(${args.join(",")}):${filterIOType}`;
5284
+ let filterFn = filterFnByKey.get(filterKey);
5285
+ if (typeof filterFn === "undefined") {
5286
+ filterFn = builtinFilterFn(filterName, args)(builtinFilters3);
5287
+ filterFnByKey.set(filterKey, filterFn);
5288
+ }
5289
+ return {
5290
+ filterName,
5291
+ args,
5292
+ filterFn
5293
+ };
5294
+ }
5295
+ });
5296
+ return filters;
5297
+ }
5298
+ var trimFn = (s) => s.trim();
5299
+ var cacheFilterInfos$1 = /* @__PURE__ */ new Map();
5300
+ function clearPropPartCacheForTooling() {
5301
+ cacheFilterInfos$1.clear();
5302
+ }
5303
+ function parsePropPart(propPart) {
5304
+ const pos = propPart.indexOf(FILTER_SEPARATOR2);
5305
+ let propText = "";
5306
+ let filterTexts = [];
5307
+ let filtersText = "";
5308
+ let filters = [];
5309
+ if (pos !== -1) {
5310
+ propText = propPart.slice(0, pos).trim();
5311
+ filtersText = propPart.slice(pos + 1).trim();
5312
+ if (cacheFilterInfos$1.has(filtersText)) {
5313
+ filters = cacheFilterInfos$1.get(filtersText);
5314
+ } else {
5315
+ filterTexts = filtersText.split(FILTER_SEPARATOR2).map(trimFn);
5316
+ filters = parseFilters(filterTexts, "input");
5317
+ cacheFilterInfos$1.set(filtersText, filters);
5318
+ }
5319
+ } else {
5320
+ propText = propPart.trim();
5321
+ }
5322
+ const [propName, propModifiersText] = propText.split(MODIFIER_SEPARATOR2).map(trimFn);
5323
+ const propSegments = propName.split(DELIMITER2).map(trimFn);
5324
+ const propModifiers = propModifiersText ? propModifiersText.split(",").map(trimFn) : [];
5325
+ return {
5326
+ propName,
5327
+ propSegments,
5328
+ propModifiers,
5329
+ inFilters: filters
5330
+ };
5331
+ }
5332
+ var cacheFilterInfos = /* @__PURE__ */ new Map();
5333
+ function clearStatePartCacheForTooling() {
5334
+ cacheFilterInfos.clear();
5335
+ }
5336
+ function parseStatePart(statePart) {
5337
+ const pos = statePart.indexOf(FILTER_SEPARATOR2);
5338
+ let stateAndPath = "";
5339
+ let filterTexts = [];
5340
+ let filtersText = "";
5341
+ let filters = [];
5342
+ if (pos !== -1) {
5343
+ stateAndPath = statePart.slice(0, pos).trim();
5344
+ filtersText = statePart.slice(pos + 1).trim();
5345
+ if (cacheFilterInfos.has(filtersText)) {
5346
+ filters = cacheFilterInfos.get(filtersText);
5347
+ } else {
5348
+ filterTexts = filtersText.split(FILTER_SEPARATOR2).map(trimFn);
5349
+ filters = parseFilters(filterTexts, "output");
5350
+ cacheFilterInfos.set(filtersText, filters);
5351
+ }
5352
+ } else {
5353
+ stateAndPath = statePart.trim();
5354
+ }
5355
+ const [statePathName, stateName = "default"] = stateAndPath.split(STATE_NAME_SEPARATOR3).map(trimFn);
5356
+ const pathInfo = getPathInfo(statePathName);
5357
+ return {
5358
+ stateName,
5359
+ statePathName,
5360
+ statePathInfo: pathInfo,
5361
+ outFilters: filters
5362
+ };
5363
+ }
5364
+ function parseBindTextsForElement(bindText) {
5365
+ const [...bindTexts] = bindText.split(BINDING_SEPARATOR2).map(trimFn).filter((s) => s.length > 0);
5366
+ const results = bindTexts.map((bindText2) => {
5367
+ const separatorIndex = bindText2.indexOf(PROP_VALUE_SEPARATOR2);
5368
+ if (separatorIndex === -1) {
5369
+ raiseError2(`Invalid bindText: "${bindText2}". Missing ':' separator between propPart and statePart.`);
5370
+ }
5371
+ const propPart = bindText2.slice(0, separatorIndex).trim();
5372
+ const statePart = bindText2.slice(separatorIndex + 1).trim();
5373
+ if (propPart === ELSE_KEYWORD2) {
5374
+ const pathInfo = getPathInfo("#else");
5375
+ return {
5376
+ propName: ELSE_KEYWORD2,
5377
+ propSegments: [ELSE_KEYWORD2],
5378
+ propModifiers: [],
5379
+ statePathName: "#else",
5380
+ statePathInfo: pathInfo,
5381
+ stateName: "",
5382
+ inFilters: [],
5383
+ outFilters: [],
5384
+ bindingType: "else"
5385
+ };
5386
+ } else if (propPart === SPREAD_PROP2) {
5387
+ const stateResult = parseStatePart(statePart);
5388
+ if (stateResult.outFilters.length > 0) {
5389
+ raiseError2(`Invalid spread binding "${bindText2}": filters are not allowed on spread targets.`);
5390
+ }
5391
+ if (stateResult.statePathName.length === 0) {
5392
+ raiseError2(`Invalid spread binding "${bindText2}": spread target path is required.`);
5393
+ }
5394
+ return {
5395
+ propName: SPREAD_PROP2,
5396
+ propSegments: [SPREAD_PROP2],
5397
+ propModifiers: [],
5398
+ inFilters: [],
5399
+ ...stateResult,
5400
+ bindingType: "spread"
5401
+ };
5402
+ } else if (propPart === "if" || propPart === "elseif" || propPart === "for" || propPart === "radio" || propPart === "checkbox") {
5403
+ const stateResult = parseStatePart(statePart);
5404
+ return {
5405
+ propName: propPart,
5406
+ propSegments: [propPart],
5407
+ propModifiers: [],
5408
+ inFilters: [],
5409
+ ...stateResult,
5410
+ bindingType: propPart
5411
+ };
5412
+ } else {
5413
+ const stateResult = parseStatePart(statePart);
5414
+ const propResult = parsePropPart(propPart);
5415
+ if (propResult.propSegments[0] === EVENT_TOKEN_NAMESPACE2) {
5416
+ return {
5417
+ ...propResult,
5418
+ ...stateResult,
5419
+ bindingType: "event"
5420
+ };
5421
+ }
5422
+ if (propResult.propSegments[0].startsWith(EVENT_PROP_PREFIX2)) {
5423
+ return {
5424
+ ...propResult,
5425
+ ...stateResult,
5426
+ bindingType: "event"
5427
+ };
5428
+ } else {
5429
+ return {
5430
+ ...propResult,
5431
+ ...stateResult,
5432
+ bindingType: "prop"
5433
+ };
5434
+ }
5435
+ }
5436
+ });
5437
+ if (results.length > 1) {
5438
+ const isIncludeSingleBinding = results.some((r) => STRUCTURAL_BINDING_TYPE_SET2.has(r.bindingType));
5439
+ if (isIncludeSingleBinding) {
5440
+ 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}`);
5441
+ }
5442
+ }
5443
+ return results;
5444
+ }
5445
+ function parseBindTextForEmbeddedNode(bindText) {
5446
+ const stateResult = parseStatePart(bindText);
5447
+ return {
5448
+ propName: "textContent",
5449
+ propSegments: ["textContent"],
5450
+ propModifiers: [],
5451
+ inFilters: [],
5452
+ ...stateResult,
5453
+ bindingType: "text"
5454
+ };
5455
+ }
5456
+ function clearParserCaches() {
5457
+ clearPathInfoCacheForTooling();
5458
+ clearPropPartCacheForTooling();
5459
+ clearStatePartCacheForTooling();
5460
+ clearFilterFnCacheForTooling();
5461
+ }
5462
+
5463
+ // src/core/parser/positionalParser.ts
5464
+ var { delimiters } = getWcsManifest().syntax;
5465
+ function locate(haystack, needle, from, to) {
5466
+ if (needle.length === 0) return null;
5467
+ const index = haystack.indexOf(needle, from);
5468
+ if (index === -1 || index + needle.length > to) return null;
5469
+ return { start: index, end: index + needle.length };
5470
+ }
5471
+ function parseEmbeddedTextWithPositions(expression) {
5472
+ const exprRange = { start: 0, end: expression.length };
5473
+ let parsed = null;
5474
+ let error = null;
5475
+ try {
5476
+ parsed = parseBindTextForEmbeddedNode(expression);
5477
+ } catch (e) {
5478
+ error = e.message;
5479
+ }
5480
+ if (parsed === null) {
5481
+ return { exprRange, exprText: expression, parsed, error, propRange: null, pathRange: null, stateNameRange: null };
5482
+ }
5483
+ const firstPipe = expression.indexOf(delimiters.filter);
5484
+ const pathScopeEnd = firstPipe === -1 ? expression.length : firstPipe;
5485
+ const pathLocal = locate(expression, parsed.statePathName, 0, pathScopeEnd);
5486
+ let stateNameLocal = null;
5487
+ const at = expression.indexOf(delimiters.stateName);
5488
+ if (at !== -1 && at < pathScopeEnd) {
5489
+ stateNameLocal = locate(expression, parsed.stateName, at + 1, pathScopeEnd);
5490
+ }
5491
+ return {
5492
+ exprRange,
5493
+ exprText: expression,
5494
+ parsed,
5495
+ error,
5496
+ propRange: null,
5497
+ pathRange: pathLocal,
5498
+ stateNameRange: stateNameLocal
5499
+ };
5500
+ }
5501
+ function parseBindTextWithPositions(bindText) {
5502
+ const results = [];
5503
+ const segments = bindText.split(delimiters.binding);
5504
+ let segmentStart = 0;
5505
+ for (const segment of segments) {
5506
+ const leading = segment.length - segment.trimStart().length;
5507
+ const expr = segment.trim();
5508
+ const exprStart = segmentStart + leading;
5509
+ segmentStart += segment.length + delimiters.binding.length;
5510
+ if (expr.length === 0) continue;
5511
+ const exprRange = { start: exprStart, end: exprStart + expr.length };
5512
+ let parsed = null;
5513
+ let error = null;
5514
+ try {
5515
+ parsed = parseBindTextsForElement(expr)[0] ?? null;
5516
+ } catch (e) {
5517
+ error = e.message;
5518
+ }
5519
+ if (parsed === null) {
5520
+ results.push({ exprRange, exprText: expr, parsed, error, propRange: null, pathRange: null, stateNameRange: null });
5521
+ continue;
5522
+ }
5523
+ const colon = expr.indexOf(delimiters.propValue);
5524
+ const propEndLimit = colon === -1 ? expr.length : colon;
5525
+ const propLocal = locate(expr, parsed.propName, 0, propEndLimit);
5526
+ let pathLocal = null;
5527
+ let stateNameLocal = null;
5528
+ if (colon !== -1) {
5529
+ const stateBase = colon + 1;
5530
+ const firstPipe = expr.indexOf(delimiters.filter, stateBase);
5531
+ const pathScopeEnd = firstPipe === -1 ? expr.length : firstPipe;
5532
+ pathLocal = locate(expr, parsed.statePathName, stateBase, pathScopeEnd);
5533
+ const at = expr.indexOf(delimiters.stateName, stateBase);
5534
+ if (at !== -1 && at < pathScopeEnd) {
5535
+ stateNameLocal = locate(expr, parsed.stateName, at + 1, pathScopeEnd);
5536
+ }
5537
+ }
5538
+ const lift = (range) => range === null ? null : { start: exprStart + range.start, end: exprStart + range.end };
5539
+ results.push({
5540
+ exprRange,
5541
+ exprText: expr,
5542
+ parsed,
5543
+ error,
5544
+ propRange: lift(propLocal),
5545
+ pathRange: lift(pathLocal),
5546
+ stateNameRange: lift(stateNameLocal)
5547
+ });
5548
+ }
5549
+ return results;
5550
+ }
5551
+
5552
+ // src/core/index/referenceIndex.ts
5553
+ function keyOf(stateName, path) {
5554
+ return `${stateName}\0${path}`;
5555
+ }
5556
+ function buildReferenceIndex(html, options = {}) {
5557
+ clearParserCaches();
5558
+ const bindAttribute = options.bindAttribute ?? "data-wcs";
5559
+ const stateTagName = options.stateTagName ?? "wcs-state";
5560
+ const occurrences = [];
5561
+ const problems = [];
5562
+ for (const attr of findAllBindAttributes(html, bindAttribute)) {
5563
+ for (const binding of parseBindTextWithPositions(attr.value)) {
5564
+ const lift = (range) => ({ start: attr.valueStart + range.start, end: attr.valueStart + range.end });
5565
+ if (binding.parsed === null) {
5566
+ problems.push({ message: binding.error ?? "parse error", range: lift(binding.exprRange) });
5567
+ continue;
5568
+ }
5569
+ if (binding.pathRange === null) continue;
5570
+ occurrences.push({
5571
+ source: "attribute",
5572
+ kind: binding.parsed.propSegments[0] === "eventToken" ? "eventToken" : "path",
5573
+ stateName: binding.parsed.stateName,
5574
+ path: binding.parsed.statePathName,
5575
+ pathRange: lift(binding.pathRange),
5576
+ exprRange: lift(binding.exprRange),
5577
+ propName: binding.parsed.propName,
5578
+ propRange: binding.propRange === null ? null : lift(binding.propRange),
5579
+ stateNameRange: binding.stateNameRange === null ? null : lift(binding.stateNameRange),
5580
+ bindingType: binding.parsed.bindingType
5581
+ });
5582
+ }
5583
+ }
5584
+ const textMatches = [
5585
+ ...findAllMustacheSyntax(html),
5586
+ ...findAllCommentBindings(html)
5587
+ ];
5588
+ for (const match of textMatches) {
5589
+ const binding = parseEmbeddedTextWithPositions(match.expression);
5590
+ const shift = (range) => ({
5591
+ start: match.exprStart + range.start,
5592
+ end: match.exprStart + range.end
5593
+ });
5594
+ if (binding.parsed === null) {
5595
+ problems.push({ message: binding.error ?? "parse error", range: shift(binding.exprRange) });
5596
+ continue;
5597
+ }
5598
+ if (binding.pathRange === null) continue;
5599
+ occurrences.push({
5600
+ source: match.kind,
5601
+ kind: "path",
5602
+ stateName: binding.parsed.stateName,
5603
+ path: binding.parsed.statePathName,
5604
+ pathRange: shift(binding.pathRange),
5605
+ exprRange: { start: match.exprStart, end: match.exprEnd },
5606
+ propName: null,
5607
+ propRange: null,
5608
+ stateNameRange: binding.stateNameRange === null ? null : shift(binding.stateNameRange),
5609
+ bindingType: "text"
5610
+ });
5611
+ }
5612
+ const declarations = [];
5613
+ for (const block of parseWcsScriptBlocks(html, stateTagName)) {
5614
+ for (const span of analyzeDeclarationSpans(block.content)) {
5615
+ declarations.push({
5616
+ stateName: block.stateName,
5617
+ name: span.name,
5618
+ kind: span.kind,
5619
+ range: { start: block.contentStart + span.start, end: block.contentStart + span.end }
5620
+ });
5621
+ }
5622
+ }
5623
+ const byPath = /* @__PURE__ */ new Map();
5624
+ for (const occurrence of occurrences) {
5625
+ if (occurrence.kind !== "path") continue;
5626
+ const key = keyOf(occurrence.stateName, occurrence.path);
5627
+ const list = byPath.get(key);
5628
+ if (list === void 0) {
5629
+ byPath.set(key, [occurrence]);
5630
+ } else {
5631
+ list.push(occurrence);
5632
+ }
5633
+ }
5634
+ const declarationByName = /* @__PURE__ */ new Map();
5635
+ for (const declaration of declarations) {
5636
+ const key = keyOf(declaration.stateName, declaration.name);
5637
+ if (!declarationByName.has(key)) declarationByName.set(key, declaration);
5638
+ }
5639
+ return {
5640
+ occurrences,
5641
+ declarations,
5642
+ problems,
5643
+ referencesOf(stateName, path) {
5644
+ return (byPath.get(keyOf(stateName, path)) ?? []).slice();
5645
+ },
5646
+ declarationOf(stateName, path) {
5647
+ const exact = declarationByName.get(keyOf(stateName, path));
5648
+ if (exact !== void 0) return exact;
5649
+ const firstSegment = path.split(".")[0];
5650
+ if (firstSegment === path) return null;
5651
+ return declarationByName.get(keyOf(stateName, firstSegment)) ?? null;
5652
+ },
5653
+ occurrenceAt(offset) {
5654
+ for (const occurrence of occurrences) {
5655
+ if (offset >= occurrence.pathRange.start && offset < occurrence.pathRange.end) {
5656
+ return occurrence;
5657
+ }
5658
+ }
5659
+ return null;
5660
+ }
5661
+ };
5662
+ }
5663
+
5664
+ // src/service/semanticValidator.ts
5665
+ var STATE_UPDATED_CALLBACK = "$updatedCallback";
5666
+ var API_CALL = /\.\s*\$(getAll|resolve)\s*\(/g;
5667
+ var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
5668
+ function splitCallArgs(source, open) {
5669
+ const args = [];
5670
+ const starts = [];
5671
+ let depth = 0;
5672
+ let argStart = open;
5673
+ let i = open;
5674
+ while (i < source.length) {
5675
+ const ch = source[i];
5676
+ if (ch === '"' || ch === "'" || ch === "`") {
5677
+ const quote = ch;
5678
+ i++;
5679
+ while (i < source.length) {
5680
+ if (source[i] === "\\") {
5681
+ i += 2;
5682
+ continue;
5683
+ }
5684
+ if (source[i] === quote) {
5685
+ i++;
5686
+ break;
5687
+ }
5688
+ i++;
5689
+ }
5690
+ continue;
5691
+ }
5692
+ if (ch === "(" || ch === "[" || ch === "{") {
5693
+ depth++;
5694
+ i++;
5695
+ continue;
5696
+ }
5697
+ if (ch === ")" && depth === 0) {
5698
+ args.push(source.slice(argStart, i));
5699
+ starts.push(argStart);
5700
+ return { args, starts, end: i + 1 };
5701
+ }
5702
+ if (ch === ")" || ch === "]" || ch === "}") {
5703
+ depth--;
5704
+ i++;
5705
+ continue;
5706
+ }
5707
+ if (ch === "," && depth === 0) {
5708
+ args.push(source.slice(argStart, i));
5709
+ starts.push(argStart);
5710
+ argStart = i + 1;
5711
+ i++;
5712
+ continue;
5713
+ }
5714
+ i++;
5715
+ }
5716
+ return null;
5717
+ }
5718
+ function literalString(arg) {
5719
+ const match = STRING_LITERAL.exec(arg);
5720
+ return match === null ? null : match[2];
5721
+ }
5722
+ function literalArrayLength(arg) {
5723
+ const trimmed = arg.trim();
5724
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
5725
+ const inner = trimmed.slice(1, -1);
5726
+ if (inner.trim().length === 0) return 0;
5727
+ if (/(^|[^.])\.\.\./.test(inner)) return null;
5728
+ const parts = splitCallArgs(`${inner})`, 0);
5729
+ if (parts === null) return null;
5730
+ return parts.args.filter((part) => part.trim().length > 0).length;
5731
+ }
5732
+ function validateIndexArity(script, scriptStart, locale3) {
5733
+ const msgs = getMessages(locale3);
5734
+ const out = [];
5735
+ API_CALL.lastIndex = 0;
5736
+ let match;
5737
+ while ((match = API_CALL.exec(script)) !== null) {
5738
+ const api = `$${match[1]}`;
5739
+ const parsed = splitCallArgs(script, match.index + match[0].length);
5740
+ if (parsed === null) continue;
5741
+ API_CALL.lastIndex = parsed.end;
5742
+ if (parsed.args.length < 2) continue;
5743
+ const path = literalString(parsed.args[0]);
5744
+ if (path === null) continue;
5745
+ const actual = literalArrayLength(parsed.args[1]);
5746
+ if (actual === null) continue;
5747
+ const wildcardCount = countWildcardSegments(path);
5748
+ const requirement = api === "$resolve" ? "exact" : "atMost";
5749
+ const mismatched = requirement === "exact" ? actual !== wildcardCount : actual > wildcardCount;
5750
+ if (!mismatched) continue;
5751
+ const argText = parsed.args[1];
5752
+ const leading = argText.length - argText.trimStart().length;
5753
+ out.push({
5754
+ code: WcsDiagnosticCode.IndexArity,
5755
+ start: scriptStart + parsed.starts[1] + leading,
5756
+ end: scriptStart + parsed.starts[1] + argText.trimEnd().length,
5757
+ message: msgs.indexArity(api, path, requirement, wildcardCount, actual),
5758
+ severity: "warning"
5759
+ });
5760
+ }
5761
+ return out;
5762
+ }
5763
+ var READ_BRACKET = /\bthis\s*\??\.\s*\[\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*\]|\bthis\s*\??\[\s*(["'])((?:\\.|(?!\3)[^\\])*)\3\s*\]/g;
5764
+ var READ_DOT = /\bthis\s*\??\.\s*([A-Za-z_]\w*)/g;
5765
+ var READ_API = /\bthis\s*\??\.\s*\$(?:getAll|resolve)\s*\(\s*(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
5766
+ function collectReadPaths(body) {
5767
+ const paths = /* @__PURE__ */ new Set();
5768
+ for (const [regex, groups] of [
5769
+ [READ_BRACKET, [2, 4]],
5770
+ [READ_API, [2]],
5771
+ [READ_DOT, [1]]
5772
+ ]) {
5773
+ regex.lastIndex = 0;
5774
+ let match;
5775
+ while ((match = regex.exec(body)) !== null) {
5776
+ for (const group of groups) {
5777
+ const value = match[group];
5778
+ if (value !== void 0 && value.length > 0 && !value.startsWith("$")) {
5779
+ paths.add(value);
5780
+ }
5781
+ }
5782
+ }
5783
+ }
5784
+ return paths;
5785
+ }
5786
+ function validateGetterCycles(script, scriptStart, locale3) {
5787
+ const msgs = getMessages(locale3);
5788
+ const getters = analyzeCallableBodies(script).filter((entry) => entry.kind === "getter");
5789
+ if (getters.length === 0) return [];
5790
+ const declared = new Set(getters.map((getter) => getter.name));
5791
+ const edges = /* @__PURE__ */ new Map();
5792
+ for (const getter of getters) {
5793
+ const targets = [];
5794
+ for (const read of collectReadPaths(getter.body)) {
5795
+ if (declared.has(read)) targets.push(read);
5796
+ }
5797
+ edges.set(getter.name, targets);
5798
+ }
5799
+ const gray = /* @__PURE__ */ new Set();
5800
+ const black = /* @__PURE__ */ new Set();
5801
+ const stack = [];
5802
+ const cyclesByEntry = /* @__PURE__ */ new Map();
5803
+ const visit = (name) => {
5804
+ if (black.has(name)) return;
5805
+ if (gray.has(name)) {
5806
+ const from = stack.indexOf(name);
5807
+ const cycle = stack.slice(from).concat(name).join(" -> ");
5808
+ for (const member of stack.slice(from)) {
5809
+ if (!cyclesByEntry.has(member)) cyclesByEntry.set(member, cycle);
5810
+ }
5811
+ return;
5812
+ }
5813
+ gray.add(name);
5814
+ stack.push(name);
5815
+ for (const next of edges.get(name) ?? []) {
5816
+ visit(next);
5817
+ }
5818
+ stack.pop();
5819
+ gray.delete(name);
5820
+ black.add(name);
5821
+ };
5822
+ for (const getter of getters) {
5823
+ visit(getter.name);
5824
+ }
5825
+ if (cyclesByEntry.size === 0) return [];
5826
+ const out = [];
5827
+ for (const getter of getters) {
5828
+ const cycle = cyclesByEntry.get(getter.name);
5829
+ if (cycle === void 0) continue;
5830
+ out.push({
5831
+ code: WcsDiagnosticCode.GetterCycle,
5832
+ start: scriptStart + getter.start,
5833
+ end: scriptStart + getter.end,
5834
+ message: msgs.getterCycle(cycle),
5835
+ severity: "warning"
5836
+ });
5837
+ }
5838
+ return out;
5839
+ }
5840
+ function blankComments(source) {
5841
+ const out = source.split("");
5842
+ let i = 0;
5843
+ while (i < source.length) {
5844
+ const ch = source[i];
5845
+ if (ch === '"' || ch === "'" || ch === "`") {
5846
+ const quote = ch;
5847
+ i++;
5848
+ while (i < source.length) {
5849
+ if (source[i] === "\\") {
5850
+ i += 2;
5851
+ continue;
5852
+ }
5853
+ if (source[i] === quote) {
5854
+ i++;
5855
+ break;
5856
+ }
5857
+ i++;
5858
+ }
5859
+ continue;
5860
+ }
5861
+ if (ch === "/" && source[i + 1] === "/") {
5862
+ while (i < source.length && source[i] !== "\n") {
5863
+ out[i] = " ";
5864
+ i++;
5865
+ }
5866
+ continue;
5867
+ }
5868
+ if (ch === "/" && source[i + 1] === "*") {
5869
+ while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
5870
+ out[i] = " ";
5871
+ i++;
5872
+ }
5873
+ if (i < source.length) {
5874
+ out[i] = " ";
5875
+ out[i + 1] = " ";
5876
+ i += 2;
5877
+ }
5878
+ continue;
5879
+ }
5880
+ i++;
5881
+ }
5882
+ return out.join("");
5883
+ }
5884
+ var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
5885
+ function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3) {
5886
+ const blocks = parseWcsScriptBlocks(html, stateTagName);
5887
+ if (blocks.length === 0) return [];
5888
+ const hasCallback = blocks.some((block) => block.content.includes(STATE_UPDATED_CALLBACK));
5889
+ if (!hasCallback) return [];
5890
+ const msgs = getMessages(locale3);
5891
+ const boundPaths = collectBoundPaths(html, stateTagName, bindAttrName);
5892
+ const out = [];
5893
+ for (const block of blocks) {
5894
+ const callback = analyzeCallableBodies(block.content).find((entry) => entry.name === STATE_UPDATED_CALLBACK && entry.kind === "method");
5895
+ if (callback === void 0) continue;
5896
+ const declared = new Set(analyzeStatePaths(block.content, block.stateName).map((p) => p.path));
5897
+ const bound = boundPaths.get(block.stateName) ?? /* @__PURE__ */ new Set();
5898
+ const body = blankComments(callback.body);
5899
+ PATH_TEST_LITERAL.lastIndex = 0;
5900
+ let match;
5901
+ const reported = /* @__PURE__ */ new Set();
5902
+ while ((match = PATH_TEST_LITERAL.exec(body)) !== null) {
5903
+ const path = match[2];
5904
+ if (path.length === 0 || !declared.has(path) || bound.has(path)) continue;
5905
+ if (reported.has(path)) continue;
5906
+ reported.add(path);
5907
+ const quoteAt = match.index + match[0].length - path.length - 1;
5908
+ out.push({
5909
+ code: WcsDiagnosticCode.UpdatedCallbackUnbound,
5910
+ start: block.contentStart + callback.bodyStart + quoteAt,
5911
+ end: block.contentStart + callback.bodyStart + quoteAt + path.length,
5912
+ message: msgs.updatedCallbackUnbound(path),
5913
+ severity: "warning"
5914
+ });
5915
+ }
5916
+ }
5917
+ return out;
5918
+ }
5919
+ function collectBoundPaths(html, stateTagName, bindAttrName) {
5920
+ const byState = /* @__PURE__ */ new Map();
5921
+ const add = (stateName, path) => {
5922
+ let set = byState.get(stateName);
5923
+ if (set === void 0) {
5924
+ set = /* @__PURE__ */ new Set();
5925
+ byState.set(stateName, set);
5926
+ }
5927
+ set.add(path);
5928
+ };
5929
+ const index = buildReferenceIndex(html, { bindAttribute: bindAttrName, stateTagName });
5930
+ for (const occurrence of index.occurrences) {
5931
+ add(occurrence.stateName, occurrence.path);
5932
+ if (!occurrence.path.startsWith(".")) continue;
5933
+ const forPath = getInnermostForPath(html, occurrence.pathRange.start, bindAttrName);
5934
+ if (forPath === null || forPath.startsWith(".")) continue;
5935
+ add(
5936
+ occurrence.stateName,
5937
+ occurrence.path === "." ? `${forPath}.*` : `${forPath}.*.${occurrence.path.slice(1)}`
5938
+ );
5939
+ }
5940
+ return byState;
5941
+ }
5942
+ function validateSemantics(html, stateTagName = "wcs-state", locale3, bindAttrName = "data-wcs") {
5943
+ const out = [];
5944
+ for (const block of parseWcsScriptBlocks(html, stateTagName)) {
5945
+ out.push(...validateIndexArity(block.content, block.contentStart, locale3));
5946
+ out.push(...validateGetterCycles(block.content, block.contentStart, locale3));
5947
+ }
5948
+ out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3));
5949
+ return out;
5950
+ }
5951
+
5952
+ // src/core/validateDocument.ts
5953
+ function validateDocument(text, options = {}) {
5954
+ const bindAttribute = options.bindAttribute ?? "data-wcs";
5955
+ const stateTagName = options.stateTagName ?? "wcs-state";
5956
+ const locale3 = options.locale;
5957
+ const fileReader = options.fileReader;
5958
+ const out = [];
5959
+ out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader));
5960
+ out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader));
5961
+ out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
5962
+ out.push(...validateDocumentEnv(text, locale3));
5963
+ out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
5964
+ out.push(...validateArrayMutations(text, stateTagName, locale3));
5965
+ out.push(...validateWatchDeclarations(text, stateTagName, locale3));
5966
+ for (const d of validateStateTypes(text, stateTagName, locale3)) {
5967
+ out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
5968
+ }
5969
+ for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
5970
+ out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
5971
+ }
5972
+ return sortDiagnostics(out);
5973
+ }
5974
+
5975
+ // src/core/sidecar/schemaSubset.ts
5976
+ var ALLOWED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
5977
+ "type",
5978
+ "properties",
5979
+ "required",
5980
+ "items",
5981
+ "enum",
5982
+ "const",
5983
+ "anyOf",
5984
+ "$defs",
5985
+ "$ref"
5986
+ ]);
5987
+ var DiagnosticContext = class {
5988
+ constructor(spans) {
5989
+ this.spans = spans;
3735
5990
  }
3736
5991
  diagnostics = [];
3737
5992
  add(code, pointer2, message, severity, extra = {}, useKeySpan = false) {
@@ -4336,29 +6591,6 @@ function runValidation(inputs, options = {}) {
4336
6591
  function classify(path) {
4337
6592
  return path.endsWith(".manifest.json") ? "manifest" : "html";
4338
6593
  }
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
6594
  function parseArgs(argv) {
4363
6595
  const options = {};
4364
6596
  const files = [];
@@ -4383,7 +6615,7 @@ function resolveCliLocale(explicit, env = process.env) {
4383
6615
  }
4384
6616
  function main(argv) {
4385
6617
  const { options, files } = parseArgs(argv);
4386
- const locale = resolveCliLocale(options.locale);
6618
+ const locale3 = resolveCliLocale(options.locale);
4387
6619
  if (files.length === 0) {
4388
6620
  process.stderr.write("usage: wcs-validate [--attr=data-wcs] [--state-tag=wcs-state] [--lang=ja|en] <file> [<file> ...]\n");
4389
6621
  return 2;
@@ -4392,7 +6624,7 @@ function main(argv) {
4392
6624
  for (const path of files) {
4393
6625
  let text;
4394
6626
  try {
4395
- text = (0, import_node_fs.readFileSync)(path, "utf8");
6627
+ text = (0, import_node_fs2.readFileSync)(path, "utf8");
4396
6628
  } catch (e) {
4397
6629
  process.stderr.write(`cannot read ${path}: ${e.message}
4398
6630
  `);
@@ -4401,7 +6633,7 @@ function main(argv) {
4401
6633
  const kind = classify(path);
4402
6634
  inputs.push({ source: path, text, kind, fileReader: kind === "html" ? createFileReader(path) : void 0 });
4403
6635
  }
4404
- const result = runValidation(inputs, { ...options, locale });
6636
+ const result = runValidation(inputs, { ...options, locale: locale3 });
4405
6637
  for (const line of result.lines) {
4406
6638
  process.stdout.write(line + "\n");
4407
6639
  }