@wcstack/typescript 4.1.1 → 4.2.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.
package/dist/index.esm.js CHANGED
@@ -572,7 +572,7 @@ function loadSchemaCore() {
572
572
  return cached;
573
573
  }
574
574
 
575
- var version = "4.1.1";
575
+ var version = "4.2.0";
576
576
  var pkg = {
577
577
  version: version};
578
578
 
@@ -166,6 +166,10 @@ var WcsDiagnosticCode = {
166
166
  // ランタイムは何も報告しない(root は正しい値)ので lint の code。`#direct` か `event.target.closest(...)` を
167
167
  // 案内する。読みが実行されない分岐にあることもあるので warning。
168
168
  DelegatedCurrentTarget: "wcs/delegated-current-target",
169
+ // `<form data-wcs="onsubmit: login">` の `#prevent` 忘れ(ハンドラも preventDefault を呼ばない)。ブラウザが
170
+ // フォームも送る — ページが読み直されるか、router があれば GET が欄(パスワードも)を URL と履歴に載せる
171
+ // (docs/seam-apps.md、router-auth)。warning(action 付きのフォームと、読めないハンドラでは黙る)。
172
+ SubmitWithoutPrevent: "wcs/submit-without-prevent",
169
173
  // 4.0 で外れた API の旧名(`this.$trackDependency` → `$dependOn`、`$untrackDependency` → `$untracked`)。
170
174
  // ランタイムは読んだ時点で同じ code(#1701)で throw するので error。3.x(3.2〜3.3)では「動くが 4.0 で
171
175
  // 外れる」info だった。フィルタの旧名(`uc` など)は 4.0 のランタイムと同じく `wcs/filter-unknown` で、
@@ -223,6 +227,13 @@ var WcsDiagnosticCode = {
223
227
  RouterOutletMisplaced: "wcs/router-outlet-misplaced",
224
228
  // @wcstack/signals と /dom エントリの同一ページ混在(リアクティブコア二重化)。
225
229
  SignalsDualEntry: "wcs/signals-dual-entry",
230
+ // `<wcs-route>`(またはレイアウトのテンプレート)の直下のテキストの `{{ }}`。最初に着いたルートでしか束ねられず、
231
+ // 遷移で入ったルートでは書いたままの文字が出る(router の README「Where route content lives」— docs/seam-apps.md、
232
+ // router-admin)。warning。
233
+ RouteBareMustache: "wcs/route-bare-mustache",
234
+ // 既定の `<slot>` の無いレイアウトのテンプレートに、名前付きスロットの外の中身(入れ子のルート)を渡している。
235
+ // 中身は表示されない。ランタイムも同じ code で警告する(docs/seam-apps.md、router-admin)。warning。
236
+ LayoutSlotMissing: "wcs/layout-slot-missing",
226
237
  // --- deprecations ---
227
238
  // 名前付き State(`<wcs-state name>` / `path@name`)。v2 でマウント(`mount=` と接頭辞付きパス)に
228
239
  // 置き換わる(docs/state-mount-design.md D16)。1.x では warning、v2 では parse error と同時に error。
@@ -821,7 +832,8 @@ var FORMATS_FILTER_NAMES = [
821
832
  "coalesce",
822
833
  "number",
823
834
  "string",
824
- "nullIfEmpty"
835
+ "nullIfEmpty",
836
+ "currency"
825
837
  ];
826
838
  var definitions = /* @__PURE__ */ new Map();
827
839
  function hasFilter(name) {
@@ -1038,6 +1050,8 @@ var codeOf = (id) => {
1038
1050
  return c ? `[wcs/${c}] ` : "";
1039
1051
  };
1040
1052
  var MOVED = ["enableMustache", "sameValueGuard", "enableDirectionalInitialSync"];
1053
+ var was = (v) => v == null ? String(v) : v instanceof Date ? "a Date" : Array.isArray(v) ? "an array" : typeof v === "string" ? `a string (${JSON.stringify(v.length > 32 ? v.slice(0, 32) + "\u2026" : v)})` : typeof v === "object" ? "an object" : `a ${typeof v} (${String(v)})`;
1054
+ var NAMESPACES = ["", "", "class.", "attr.", "style."];
1041
1055
  var element = (tag, at2) => `<${tag}${at2.map((a, i) => i % 2 ? `="${a}"` : ` ${a}`).join("")}>`;
1042
1056
  var CSP_GUIDE = "https://github.com/wcstack/wcstack/blob/main/docs/csp.md";
1043
1057
  var SENTENCES = {
@@ -1089,10 +1103,11 @@ var SENTENCES = {
1089
1103
  41
1090
1104
  /* RenderChain */
1091
1105
  ]: () => `render chain depth limit exceeded (${MAX_RENDER_CHAIN} drains that rendering itself started); bindings for this batch were not applied.`,
1106
+ // (named as written — `textContent: iso`, `class.active: x` — when the binding has a member; else its type)
1092
1107
  [
1093
1108
  12
1094
1109
  /* BindingFailed */
1095
- ]: (type, p) => `binding "${type}: ${p}" failed to apply.`,
1110
+ ]: (type, p, name, kind) => `binding "${name ? (NAMESPACES[kind] ?? "") + name : type}: ${p}" failed to apply.`,
1096
1111
  [
1097
1112
  13
1098
1113
  /* LoadFailed */
@@ -1164,11 +1179,19 @@ var SENTENCES = {
1164
1179
  [
1165
1180
  28
1166
1181
  /* FilterValueNotNumber */
1167
- ]: (fn) => `filter ${fn} requires a number value`,
1182
+ ]: (fn, v) => `filter ${fn} requires a number value, not ${was(v)}${typeof v === "string" ? ` \u2014 put |number before |${fn}, or convert it where it is read` : ""}`,
1168
1183
  [
1169
1184
  29
1170
1185
  /* FilterValueNotDate */
1171
- ]: (fn) => `filter ${fn} requires a date value`,
1186
+ ]: (fn, v) => `filter ${fn} requires a date value (a Date), not ${was(v)}${typeof v === "string" || typeof v === "number" ? " \u2014 make the Date where it is read: a getter returning new Date(\u2026)" : ""}`,
1187
+ [
1188
+ 52
1189
+ /* FilterStyleInvalid */
1190
+ ]: (fn, v) => `filter ${fn} takes a style of full, long, medium or short, not "${v}"`,
1191
+ [
1192
+ 53
1193
+ /* FilterCurrencyInvalid */
1194
+ ]: (v) => `filter currency takes an ISO 4217 currency code (JPY, USD, EUR), not "${v}"`,
1172
1195
  [
1173
1196
  30
1174
1197
  /* FilterValueNotArray */
@@ -2108,11 +2131,17 @@ function optionsRequired(fnName) {
2108
2131
  function optionMustBeNumber(fnName) {
2109
2132
  raise2(27, [fnName]);
2110
2133
  }
2111
- function valueMustBeNumber(fnName) {
2112
- raise2(28, [fnName]);
2134
+ function styleInvalid(fnName, value) {
2135
+ raise2(52, [fnName, value]);
2136
+ }
2137
+ function currencyInvalid(value) {
2138
+ raise2(53, [value]);
2113
2139
  }
2114
- function valueMustBeDate(fnName) {
2115
- raise2(29, [fnName]);
2140
+ function valueMustBeNumber(fnName, value) {
2141
+ raise2(28, [fnName, value]);
2142
+ }
2143
+ function valueMustBeDate(fnName, value) {
2144
+ raise2(29, [fnName, value]);
2116
2145
  }
2117
2146
  function valueMustBeArray(fnName) {
2118
2147
  raise2(30, [fnName]);
@@ -2179,7 +2208,7 @@ var numeric = (name, op) => ({
2179
2208
  const opt = requiredNumberOption(options, 0, name);
2180
2209
  return (value) => {
2181
2210
  if (typeof value !== "number") {
2182
- valueMustBeNumber(name);
2211
+ valueMustBeNumber(name, value);
2183
2212
  }
2184
2213
  return op(value, opt);
2185
2214
  };
@@ -2209,7 +2238,7 @@ var toFixed = (options) => {
2209
2238
  const opt = numberOption(options?.[0] ?? "0", "toFixed");
2210
2239
  return (value) => {
2211
2240
  if (typeof value !== "number") {
2212
- valueMustBeNumber("toFixed");
2241
+ valueMustBeNumber("toFixed", value);
2213
2242
  }
2214
2243
  return value.toFixed(opt);
2215
2244
  };
@@ -2218,7 +2247,7 @@ var round = (options) => {
2218
2247
  const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "round"));
2219
2248
  return (value) => {
2220
2249
  if (typeof value !== "number") {
2221
- valueMustBeNumber("round");
2250
+ valueMustBeNumber("round", value);
2222
2251
  }
2223
2252
  return Math.round(value * optValue) / optValue;
2224
2253
  };
@@ -2227,7 +2256,7 @@ var floor = (options) => {
2227
2256
  const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "floor"));
2228
2257
  return (value) => {
2229
2258
  if (typeof value !== "number") {
2230
- valueMustBeNumber("floor");
2259
+ valueMustBeNumber("floor", value);
2231
2260
  }
2232
2261
  return Math.floor(value * optValue) / optValue;
2233
2262
  };
@@ -2236,7 +2265,7 @@ var ceil = (options) => {
2236
2265
  const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "ceil"));
2237
2266
  return (value) => {
2238
2267
  if (typeof value !== "number") {
2239
- valueMustBeNumber("ceil");
2268
+ valueMustBeNumber("ceil", value);
2240
2269
  }
2241
2270
  return Math.ceil(value * optValue) / optValue;
2242
2271
  };
@@ -2245,7 +2274,7 @@ var percent = (options) => {
2245
2274
  const opt = numberOption(options?.[0] ?? "0", "percent");
2246
2275
  return (value) => {
2247
2276
  if (typeof value !== "number") {
2248
- valueMustBeNumber("percent");
2277
+ valueMustBeNumber("percent", value);
2249
2278
  }
2250
2279
  return `${(value * 100).toFixed(opt)}%`;
2251
2280
  };
@@ -2272,11 +2301,29 @@ var locale = (options) => {
2272
2301
  const explicit = options?.[0];
2273
2302
  return (value) => {
2274
2303
  if (typeof value !== "number") {
2275
- valueMustBeNumber("locale");
2304
+ valueMustBeNumber("locale", value);
2276
2305
  }
2277
2306
  return value.toLocaleString(explicit ?? defaultLocale());
2278
2307
  };
2279
2308
  };
2309
+ var currency = (options) => {
2310
+ const code = options?.[0] ?? optionsRequired("currency");
2311
+ const explicit = options?.[1];
2312
+ const format = { style: "currency", currency: code };
2313
+ try {
2314
+ new Intl.NumberFormat("en", format);
2315
+ const known = Intl.supportedValuesOf?.("currency");
2316
+ if (known && !known.includes(code.toUpperCase())) throw 0;
2317
+ } catch {
2318
+ currencyInvalid(code);
2319
+ }
2320
+ return (value) => {
2321
+ if (typeof value !== "number") {
2322
+ valueMustBeNumber("currency", value);
2323
+ }
2324
+ return value.toLocaleString(explicit ?? defaultLocale(), format);
2325
+ };
2326
+ };
2280
2327
  var upper = () => (value) => String(value).toUpperCase();
2281
2328
  var lower = () => (value) => String(value).toLowerCase();
2282
2329
  var capitalize = () => (value) => {
@@ -2333,38 +2380,48 @@ var join = (options) => {
2333
2380
  return value.join(opt);
2334
2381
  };
2335
2382
  };
2383
+ var STYLES = ["full", "long", "medium", "short"];
2384
+ function dateOptions(options, fn) {
2385
+ let [loc, style] = options ?? [];
2386
+ if (style === void 0 && loc !== void 0 && STYLES.includes(loc)) [loc, style] = [void 0, loc];
2387
+ if (style !== void 0 && !STYLES.includes(style)) styleInvalid(fn, style);
2388
+ return [loc, style];
2389
+ }
2336
2390
  var date = (options) => {
2337
- const explicit = options?.[0];
2391
+ const [explicit, style] = dateOptions(options, "date");
2392
+ const format = style && { dateStyle: style };
2338
2393
  return (value) => {
2339
2394
  if (!(value instanceof Date)) {
2340
- valueMustBeDate("date");
2395
+ valueMustBeDate("date", value);
2341
2396
  }
2342
- return value.toLocaleDateString(explicit ?? defaultLocale());
2397
+ return value.toLocaleDateString(explicit ?? defaultLocale(), format);
2343
2398
  };
2344
2399
  };
2345
2400
  var time = (options) => {
2346
- const explicit = options?.[0];
2401
+ const [explicit, style] = dateOptions(options, "time");
2402
+ const format = style && { timeStyle: style };
2347
2403
  return (value) => {
2348
2404
  if (!(value instanceof Date)) {
2349
- valueMustBeDate("time");
2405
+ valueMustBeDate("time", value);
2350
2406
  }
2351
- return value.toLocaleTimeString(explicit ?? defaultLocale());
2407
+ return value.toLocaleTimeString(explicit ?? defaultLocale(), format);
2352
2408
  };
2353
2409
  };
2354
2410
  var datetime = (options) => {
2355
- const explicit = options?.[0];
2411
+ const [explicit, style] = dateOptions(options, "datetime");
2412
+ const format = style && { dateStyle: style, timeStyle: style };
2356
2413
  return (value) => {
2357
2414
  if (!(value instanceof Date)) {
2358
- valueMustBeDate("datetime");
2415
+ valueMustBeDate("datetime", value);
2359
2416
  }
2360
- return value.toLocaleString(explicit ?? defaultLocale());
2417
+ return value.toLocaleString(explicit ?? defaultLocale(), format);
2361
2418
  };
2362
2419
  };
2363
2420
  var ymd = (options) => {
2364
2421
  const opt = options?.[0] ?? "-";
2365
2422
  return (value) => {
2366
2423
  if (!(value instanceof Date)) {
2367
- valueMustBeDate("ymd");
2424
+ valueMustBeDate("ymd", value);
2368
2425
  }
2369
2426
  const year = value.getFullYear().toString();
2370
2427
  const month = (value.getMonth() + 1).toString().padStart(2, "0");
@@ -2376,7 +2433,7 @@ var hms = (options) => {
2376
2433
  const opt = options?.[0] ?? ":";
2377
2434
  return (value) => {
2378
2435
  if (!(value instanceof Date)) {
2379
- valueMustBeDate("hms");
2436
+ valueMustBeDate("hms", value);
2380
2437
  }
2381
2438
  const hours = value.getHours().toString().padStart(2, "0");
2382
2439
  const minutes = value.getMinutes().toString().padStart(2, "0");
@@ -2386,7 +2443,7 @@ var hms = (options) => {
2386
2443
  };
2387
2444
  var abs = () => (value) => {
2388
2445
  if (typeof value !== "number") {
2389
- valueMustBeNumber("abs");
2446
+ valueMustBeNumber("abs", value);
2390
2447
  }
2391
2448
  return Math.abs(value);
2392
2449
  };
@@ -2395,7 +2452,7 @@ var clamp = (options) => {
2395
2452
  const max = requiredNumberOption(options, 1, "clamp");
2396
2453
  return (value) => {
2397
2454
  if (typeof value !== "number") {
2398
- valueMustBeNumber("clamp");
2455
+ valueMustBeNumber("clamp", value);
2399
2456
  }
2400
2457
  return Math.min(Math.max(value, min), max);
2401
2458
  };
@@ -2442,10 +2499,10 @@ var registered = {
2442
2499
  ceil: { factory: ceil, arity: [0, 1] },
2443
2500
  percent: { factory: percent, arity: [0, 1] },
2444
2501
  unit: { factory: nullishPassthrough(unit), arity: [1, 1] },
2445
- // The locale-dependent three take a locale like `locale` does (`date(ja-JP)`)
2446
- date: { factory: date, arity: [0, 1] },
2447
- time: { factory: time, arity: [0, 1] },
2448
- datetime: { factory: datetime, arity: [0, 1] },
2502
+ // The locale-dependent three take a locale like `locale` does (`date(ja-JP)`), and a style (`date(ja-JP,long)`)
2503
+ date: { factory: date, arity: [0, 2] },
2504
+ time: { factory: time, arity: [0, 2] },
2505
+ datetime: { factory: datetime, arity: [0, 2] },
2449
2506
  ymd: { factory: ymd, arity: [0, 1] },
2450
2507
  hms: { factory: hms, arity: [0, 1] },
2451
2508
  // the core set of 4.0.0-rc.3 (the core's own: eq ne not lt le gt ge falsy truthy boolean)
@@ -2472,7 +2529,9 @@ var registered = {
2472
2529
  boolean: boolean2,
2473
2530
  number: { factory: number, arity: [0, 0] },
2474
2531
  string: { factory: string, arity: [0, 0] },
2475
- nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] }
2532
+ nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] },
2533
+ // (added in 4.2: last, so the order of the others is unchanged)
2534
+ currency: { factory: currency, arity: [1, 2] }
2476
2535
  };
2477
2536
  var formatFilters = /* @__PURE__ */ Object.fromEntries(
2478
2537
  /* @__PURE__ */ Object.entries(registered).filter(([name]) => !(name in coreFilters))
@@ -2543,10 +2602,11 @@ var builtinFilterMeta = {
2543
2602
  unit: { description: "\u5358\u4F4D\uFF08\u63A5\u5C3E\u8F9E\uFF09\u3092\u4ED8\u52A0", hasArgs: true, resultType: "string", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["string"] },
2544
2603
  // 日付・時刻
2545
2604
  // `locale` と同じくロケールを 1 つ受ける(`date(ja-JP)`)。実装は最初からこれを読んでいたが、
2546
- // メタデータ側が maxArgs: 0 だったため lint と補完が正しい書き方を誤りとして報告していた
2547
- date: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u4ED8 ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
2548
- time: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u6642\u523B ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
2549
- datetime: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u6642 ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
2605
+ // メタデータ側が maxArgs: 0 だったため lint と補完が正しい書き方を誤りとして報告していた。
2606
+ // 4.2: 2 つ目は書式(full / long / medium / short — Intl の dateStyle / timeStyle)。書式だけなら既定のロケール
2607
+ date: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u4ED8 ([locale][, style]\u3002style \u306F full / long / medium / short\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 2, argTypes: ["string", "string"] },
2608
+ time: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u6642\u523B ([locale][, style]\u3002style \u306F full / long / medium / short\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 2, argTypes: ["string", "string"] },
2609
+ datetime: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u6642 ([locale][, style]\u3002style \u306F full / long / medium / short\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 2, argTypes: ["string", "string"] },
2550
2610
  ymd: { description: "YYYY-MM-DD \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F -)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
2551
2611
  hms: { description: "HH:MM:SS \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F :)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
2552
2612
  // 真偽値・変換
@@ -2557,7 +2617,9 @@ var builtinFilterMeta = {
2557
2617
  boolean: { description: "\u30D6\u30FC\u30EB\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
2558
2618
  number: { description: "\u6570\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "number", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
2559
2619
  string: { description: "\u6587\u5B57\u5217\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
2560
- nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 }
2620
+ nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
2621
+ // 4.2(formats の最後に登録)
2622
+ currency: { description: "\u901A\u8CA8\u306E\u66F8\u5F0F (code[, locale]\u3002code \u306F ISO 4217 \u2014 JPY / USD / EUR\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 1, maxArgs: 2, argTypes: ["string", "string"] }
2561
2623
  };
2562
2624
  var WCS_MANIFEST_VERSION = 2;
2563
2625
  var builtinFilterAliases = Object.freeze({});
@@ -10943,6 +11005,193 @@ function findOwnThis(text3, kind) {
10943
11005
  forEachChild(root, walk);
10944
11006
  return found;
10945
11007
  }
11008
+ var DESTRUCTIVE_ARRAY_METHODS = /* @__PURE__ */ new Set(["push", "pop", "shift", "unshift", "splice", "sort", "reverse", "fill", "copyWithin"]);
11009
+ function memberChain(node, source) {
11010
+ const segments = [];
11011
+ let cur = unwrapChain(node);
11012
+ while (cur.type === "MemberExpression") {
11013
+ const property = cur.property;
11014
+ let name = null;
11015
+ if (!cur.computed && property.type === "Identifier") name = property.name;
11016
+ else if (property.type === "Literal" && (typeof property.value === "string" || typeof property.value === "number")) name = String(property.value);
11017
+ segments.unshift({ name, raw: source(property), computed: cur.computed });
11018
+ cur = unwrapChain(cur.object);
11019
+ }
11020
+ return { base: cur, segments };
11021
+ }
11022
+ function assignTarget(root, segments) {
11023
+ if (segments.every((s) => s.name !== null)) {
11024
+ const path = segments.map((s) => s.name).join(".");
11025
+ return segments.length === 1 && /^[A-Za-z_$][\w$]*$/.test(path) ? `${root}.${path}` : `${root}["${path}"]`;
11026
+ }
11027
+ return `${root}[\`${segments.map((s) => s.name ?? `\${${s.raw}}`).join(".")}\`]`;
11028
+ }
11029
+ function findInPlaceChanges(text3, kind, root) {
11030
+ const prefix = kind === "body" ? PREFIX : VALUE_PREFIX;
11031
+ let program;
11032
+ try {
11033
+ program = parse3(prefix + text3 + (kind === "body" ? SUFFIX : VALUE_SUFFIX), { ecmaVersion: "latest", sourceType: "module" });
11034
+ } catch {
11035
+ return [];
11036
+ }
11037
+ const wrapper = unwrapWrapper(program);
11038
+ if (wrapper === null) return [];
11039
+ let body = wrapper.body;
11040
+ if (kind === "value") {
11041
+ const statement = wrapper.body.body.length === 1 ? wrapper.body.body[0] : null;
11042
+ const fn = statement?.type === "ExpressionStatement" ? statement.expression : null;
11043
+ if (fn === null || fn.type !== "FunctionExpression" && fn.type !== "ArrowFunctionExpression") return [];
11044
+ if (root === null) {
11045
+ const first = fn.params[0];
11046
+ if (first === void 0 || first.type !== "Identifier") return [];
11047
+ root = first.name;
11048
+ }
11049
+ body = fn.body;
11050
+ }
11051
+ if (root === null) return [];
11052
+ const stateRoot = root;
11053
+ const shift = prefix.length;
11054
+ const source = (n) => (prefix + text3).slice(n.start, n.end);
11055
+ const isRoot = (n) => stateRoot === "this" ? n.type === "ThisExpression" : n.type === "Identifier" && n.name === stateRoot;
11056
+ const bound = /* @__PURE__ */ new Map();
11057
+ const bind = (name) => {
11058
+ bound.set(name, (bound.get(name) ?? 0) + 1);
11059
+ };
11060
+ const aliases = /* @__PURE__ */ new Map();
11061
+ const collect2 = (node) => {
11062
+ if (isFunctionNode(node)) {
11063
+ if (node.type === "FunctionDeclaration" && node.id !== null) bind(node.id.name);
11064
+ const names = /* @__PURE__ */ new Set();
11065
+ for (const param of node.params) bindingNames(param, names);
11066
+ names.forEach(bind);
11067
+ } else if (node.type === "VariableDeclarator") {
11068
+ const names = /* @__PURE__ */ new Set();
11069
+ bindingNames(node.id, names);
11070
+ names.forEach(bind);
11071
+ const init = node.init ? unwrapChain(node.init) : null;
11072
+ if (init !== null && node.id.type === "Identifier" && init.type === "MemberExpression") {
11073
+ const chain = memberChain(init, source);
11074
+ if (isRoot(chain.base) && chain.segments.length > 0 && !chain.segments[0].name?.startsWith("$")) {
11075
+ aliases.set(node.id.name, { source: source(init), segments: chain.segments });
11076
+ }
11077
+ } else if (init !== null && node.id.type === "ObjectPattern" && isRoot(init)) {
11078
+ for (const property of node.id.properties) {
11079
+ if (property.type !== "Property" || property.computed) continue;
11080
+ const key = property.key.type === "Identifier" ? property.key.name : property.key.type === "Literal" ? String(property.key.value) : null;
11081
+ const value = property.value.type === "AssignmentPattern" ? property.value.left : property.value;
11082
+ if (key === null || key.startsWith("$") || value.type !== "Identifier") continue;
11083
+ const segment = { name: key, raw: key, computed: false };
11084
+ aliases.set(value.name, { source: `${stateRoot}.${key}`, segments: [segment] });
11085
+ }
11086
+ }
11087
+ } else if (node.type === "CatchClause" && node.param) {
11088
+ const names = /* @__PURE__ */ new Set();
11089
+ bindingNames(node.param, names);
11090
+ names.forEach(bind);
11091
+ } else if (node.type === "ClassDeclaration" && node.id !== null) {
11092
+ bind(node.id.name);
11093
+ } else if (node.type === "AssignmentExpression" && node.left.type === "Identifier") {
11094
+ bind(node.left.name);
11095
+ }
11096
+ forEachChild(node, collect2);
11097
+ };
11098
+ collect2(body);
11099
+ const alias = (name) => bound.get(name) === 1 ? aliases.get(name) : void 0;
11100
+ const out = [];
11101
+ const report = (target, at2, form, directMin) => {
11102
+ const chain = memberChain(target, source);
11103
+ const base = chain.base;
11104
+ const range = { start: at2.start - shift, end: at2.end - shift };
11105
+ const pick = (segments) => form ?? (segments.length > 0 && segments.every((s) => s.computed) ? "index" : "property");
11106
+ if (base.type === "Identifier" && base.name !== stateRoot) {
11107
+ const a = alias(base.name);
11108
+ if (a === void 0) return;
11109
+ out.push({ form: pick(chain.segments), alias: base.name, source: a.source, target: assignTarget(stateRoot, a.segments), ...range });
11110
+ return;
11111
+ }
11112
+ if (!isRoot(base) || chain.segments.length < directMin || chain.segments[0].name?.startsWith("$")) return;
11113
+ out.push({ form: pick(chain.segments.slice(1)), alias: null, source: source(at2), target: assignTarget(stateRoot, chain.segments), ...range });
11114
+ };
11115
+ const direct = stateRoot === "this" ? Infinity : 2;
11116
+ const visitChanges = (node) => {
11117
+ if (node.type === "AssignmentExpression" && unwrapChain(node.left).type === "MemberExpression") {
11118
+ report(node.left, node.left, null, direct);
11119
+ } else if (node.type === "UpdateExpression" && unwrapChain(node.argument).type === "MemberExpression") {
11120
+ report(node.argument, node.argument, null, direct);
11121
+ } else if (node.type === "UnaryExpression" && node.operator === "delete" && unwrapChain(node.argument).type === "MemberExpression") {
11122
+ report(node.argument, node.argument, "property", direct);
11123
+ } else if (node.type === "CallExpression") {
11124
+ const callee = unwrapChain(node.callee);
11125
+ if (callee.type === "MemberExpression") {
11126
+ const method = !callee.computed && callee.property.type === "Identifier" ? callee.property.name : null;
11127
+ const object = unwrapChain(callee.object);
11128
+ if (method === "assign" && object.type === "Identifier" && object.name === "Object" && node.arguments.length > 0) {
11129
+ const first = node.arguments[0];
11130
+ if (first.type === "Identifier" || unwrapChain(first).type === "MemberExpression") report(first, node, "property", 1);
11131
+ } else if (method !== null && DESTRUCTIVE_ARRAY_METHODS.has(method)) {
11132
+ report(callee.object, node, "method", stateRoot === "this" ? Infinity : 1);
11133
+ }
11134
+ }
11135
+ }
11136
+ forEachChild(node, visitChanges);
11137
+ };
11138
+ visitChanges(body);
11139
+ return out;
11140
+ }
11141
+ var LOCATION_HOLDERS = /* @__PURE__ */ new Set(["window", "document", "globalThis", "self"]);
11142
+ var LOCATION_VARYING = /* @__PURE__ */ new Set(["pathname", "search", "hash", "href"]);
11143
+ function findLocationRead(body) {
11144
+ let program;
11145
+ try {
11146
+ program = parse3(PREFIX + body + SUFFIX, { ecmaVersion: "latest", sourceType: "module" });
11147
+ } catch {
11148
+ return null;
11149
+ }
11150
+ const wrapper = unwrapWrapper(program);
11151
+ if (wrapper === null) return null;
11152
+ const names = /* @__PURE__ */ new Set();
11153
+ const scan = (node) => {
11154
+ if (isFunctionNode(node)) for (const param of node.params) bindingNames(param, names);
11155
+ if (node.type === "VariableDeclarator") bindingNames(node.id, names);
11156
+ forEachChild(node, scan);
11157
+ };
11158
+ scan(wrapper.body);
11159
+ if (names.has("location")) return null;
11160
+ const isLocation = (node) => {
11161
+ const n = unwrapChain(node);
11162
+ if (n.type === "Identifier") return n.name === "location";
11163
+ return n.type === "MemberExpression" && !n.computed && n.property.type === "Identifier" && n.property.name === "location" && n.object.type === "Identifier" && LOCATION_HOLDERS.has(n.object.name);
11164
+ };
11165
+ let found = null;
11166
+ const at2 = (node) => {
11167
+ found = { start: node.start - PREFIX.length, end: node.end - PREFIX.length, text: (PREFIX + body).slice(node.start, node.end) };
11168
+ };
11169
+ const walk = (node) => {
11170
+ if (found !== null) return;
11171
+ if (node.type === "MemberExpression" && isLocation(node.object)) {
11172
+ const part = !node.computed && node.property.type === "Identifier" ? node.property.name : null;
11173
+ if (part === null || LOCATION_VARYING.has(part)) at2(node);
11174
+ return;
11175
+ }
11176
+ if (isLocation(node)) {
11177
+ at2(node);
11178
+ return;
11179
+ }
11180
+ if (node.type === "MemberExpression") {
11181
+ walk(node.object);
11182
+ if (node.computed) walk(node.property);
11183
+ return;
11184
+ }
11185
+ if (node.type === "Property") {
11186
+ if (node.computed) walk(node.key);
11187
+ walk(node.value);
11188
+ return;
11189
+ }
11190
+ forEachChild(node, walk);
11191
+ };
11192
+ walk(wrapper.body);
11193
+ return found;
11194
+ }
10946
11195
 
10947
11196
  // src/core/messages.ts
10948
11197
  function resolveLocale(locale2) {
@@ -10983,6 +11232,8 @@ var ja = {
10983
11232
  getterCycle: (cycle) => `\u30D1\u30B9 getter \u304C\u5FAA\u74B0\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${cycle}`,
10984
11233
  updatedCallbackUnbound: (p) => `$renderedCallback \u306F binding \u99C6\u52D5\u3067\u3059\u3002"${p}" \u306F\u3053\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u3069\u306E\u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u306B\u3082\u73FE\u308C\u306A\u3044\u305F\u3081\u3001\u3053\u306E\u5206\u5C90\u306F\u4E00\u5EA6\u3082\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093\u3002\u63CF\u753B\u306B\u4F9D\u5B58\u305B\u305A\u53CD\u5FDC\u3059\u308B\u306A\u3089 $watch \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044`,
10985
11234
  onHandlerThis: (name) => `$on \u306E\u30CF\u30F3\u30C9\u30E9 "${name}" \u304C this \u3092\u4F7F\u3063\u3066\u3044\u307E\u3059\u3002$on \u306E\u30CF\u30F3\u30C9\u30E9\u306F state \u3092\u7B2C 1 \u5F15\u6570\u3067\u53D7\u3051\u53D6\u308A\uFF08\`${name}(state, event) { state.x = \u2026 }\`\uFF09\u3001this \u306F undefined \u306A\u306E\u3067\u3001this.\u2026 \u306F\u547C\u3070\u308C\u305F\u3068\u304D\u306B\u4F8B\u5916\u306B\u306A\u308A\u307E\u3059\u3002$watch \u3084\u30E1\u30BD\u30C3\u30C9\u3068\u9055\u3046\u70B9\u306B\u6CE8\u610F`,
11235
+ getterReadsLocation: (read) => `\u3053\u306E getter \u306F ${read} \u3092\u8AAD\u3093\u3067\u3044\u307E\u3059\u304C\u3001location \u306F state \u3067\u306F\u306A\u3044\u306E\u3067\u3001\u6700\u521D\u306B\u8AAD\u3093\u3060 URL \u3092\u6301\u3061\u7D9A\u3051\u307E\u3059\uFF08\u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3\u3067\u518D\u8A55\u4FA1\u3055\u308C\u307E\u305B\u3093\uFF09\u3002URL \u306F state \u3092\u901A\u3057\u3066\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044 \u2014 <wcs-router> \u306E path / searchParams \u3092\u675F\u306D\u308B`,
11236
+ submitWithoutPrevent: (h) => `onsubmit \u306B #prevent \u304C\u7121\u304F\u3001"${h}" \u3082 preventDefault \u3092\u547C\u3073\u307E\u305B\u3093\u3002\u30D6\u30E9\u30A6\u30B6\u306F\u30D5\u30A9\u30FC\u30E0\u3082\u9001\u308B\u306E\u3067\u3001\u30DA\u30FC\u30B8\u304C\u8AAD\u307F\u76F4\u3055\u308C\u308B\u304B\u3001router \u304C\u3042\u308C\u3070 GET \u304C\u6B04\u306E\u5024\uFF08\u30D1\u30B9\u30EF\u30FC\u30C9\u3082\uFF09\u3092 URL \u3068\u5C65\u6B74\u306B\u8F09\u305B\u307E\u3059\u3002onsubmit#prevent: ${h} \u3068\u66F8\u304F\u304B\u3001\u30CF\u30F3\u30C9\u30E9\u3067 event.preventDefault() \u3092\u547C\u3093\u3067\u304F\u3060\u3055\u3044`,
10986
11237
  getterUntrackedRead: (root, sp) => `\u3053\u3053\u3067\u8FFD\u8DE1\u3055\u308C\u308B\u306E\u306F "${root}" \u3060\u3051\u3067\u3059\u3002"${sp}" \u304C\u5909\u308F\u3063\u3066\u3082\u3053\u306E getter \u306F\u518D\u8A55\u4FA1\u3055\u308C\u307E\u305B\u3093\uFF08\u30D1\u30B9\u8AAD\u307F\u53D6\u308A\u306E\u5148\u306E\u7D20\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u30A2\u30AF\u30BB\u30B9\u306F\u8FFD\u8DE1\u3055\u308C\u306A\u3044\uFF09\u3002this["${sp}"] \u3067\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044`,
10987
11238
  handlerFilterNotAllowed: (prop) => `\u30A4\u30D9\u30F3\u30C8\u30CF\u30F3\u30C9\u30E9 "${prop}" \u306B\u30D5\u30A3\u30EB\u30BF\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
10988
11239
  typeExpectation: (label, expected, resultType) => `"${label}" \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728\u306E\u578B: ${resultType}\uFF09`,
@@ -11006,6 +11257,8 @@ var ja = {
11006
11257
  typeAnnotationIncompatible: (vt, rt) => `\u578B "${vt}" \u306F @type {${rt}} \u3068\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093`,
11007
11258
  arrayMutation: (m, alt) => `\u914D\u5217\u306E\u7834\u58CA\u7684\u30E1\u30BD\u30C3\u30C9 "${m}" \u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\uFF08\u540C\u4E00\u53C2\u7167\u306E\u81EA\u5DF1\u518D\u4EE3\u5165\u3067\u3082\u8981\u7D20\u306E\u8FFD\u52A0\u30FB\u524A\u9664\u306F\u53CD\u6620\u3055\u308C\u307E\u305B\u3093\uFF09\u3002\u975E\u7834\u58CA\u30E1\u30BD\u30C3\u30C9\u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: ${alt}\uFF09\u3002`,
11008
11259
  arrayIndexAssign: (sp) => `\u914D\u5217\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3078\u306E\u76F4\u63A5\u4EE3\u5165\u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\u3002this["${sp}"] \u306E\u3088\u3046\u306A\u30C9\u30C3\u30C8\u30D1\u30B9\u4EE3\u5165\u3001\u307E\u305F\u306F with() \u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
11260
+ inPlaceAlias: (a, src, t) => `"${a}" \u306F ${src}\uFF08state \u306E\u5024\u305D\u306E\u3082\u306E\uFF09\u3067\u3059\u3002\u305D\u308C\u3092\u901A\u3057\u305F\u5909\u66F4\u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u305B\u305A\u3001\u540C\u3058\u5024\u3092\u66F8\u304D\u623B\u3057\u3066\u3082\u4E00\u89A7\u306E\u884C\u306F\u8FFD\u52A0\u30FB\u524A\u9664\u30FB\u7F6E\u304D\u63DB\u3048\u3055\u308C\u307E\u305B\u3093\u3002\u65B0\u3057\u3044\u5024\u3092\u4F5C\u3063\u3066 ${t} \u306B\u4EE3\u5165\u3057\u3066\u304F\u3060\u3055\u3044\uFF08${a}.with(i, item) / [...${a}, item] / { ...${a}, key: value }\uFF09`,
11261
+ inPlaceChange: (src, t) => `${src} \u306F state \u306E\u5024\u3092\u305D\u306E\u5834\u3067\u5909\u3048\u308B\u306E\u3067\u3001\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\u3002\u65B0\u3057\u3044\u5024\u3092\u4F5C\u3063\u3066 ${t} \u306B\u4EE3\u5165\u3057\u3066\u304F\u3060\u3055\u3044`,
11009
11262
  tagMemberUnknown: (prop, tag) => `"${prop}" \u306F <${tag}> \u306E wcBindable \u30E1\u30F3\u30D0\u30FC\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u672A\u77E5\u30E1\u30F3\u30D0\u30FC\u3078\u306E\u30D0\u30A4\u30F3\u30C9\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059\uFF09`,
11010
11263
  nameAlias: (written, canonical) => `"${written}" \u306F 4.0 \u3067\u5916\u308C\u307E\u3057\u305F\uFF08"${canonical}" \u306E 3.x \u306E\u65E7\u540D\u3067\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u3093\u3060\u6642\u70B9\u3067 throw \u3057\u307E\u3059\uFF09\u3002"${canonical}" \u3068\u66F8\u3044\u3066\u304F\u3060\u3055\u3044`,
11011
11264
  declarationAliasRead: (alias, canonical) => `"${alias}" \u306F 4.0 \u3067\u5916\u308C\u305F\u5BA3\u8A00\u30AD\u30FC\uFF08"${canonical}" \u306E 3.x \u306E\u65E7\u540D\uFF09\u306A\u306E\u3067\u3001this["${alias}"] \u306F undefined \u306B\u306A\u308A\u307E\u3059\u3002"${canonical}" \u3092\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044`,
@@ -11042,6 +11295,9 @@ var ja = {
11042
11295
  baseHrefMissing: () => `@wcstack/router \u3092\u4F7F\u3046 SPA \u306B\u306F <head> \u5185\u306E <base href="/"> \u304C\u5FC5\u8981\u3067\u3059\uFF08\u7121\u3044\u3068\u30C7\u30A3\u30FC\u30D7\u30EA\u30F3\u30AF\u3067 basename \u304C\u8AA4\u5C0E\u51FA\u3055\u308C\u307E\u3059\uFF09`,
11043
11296
  routerOutletMisplaced: () => `\u3053\u306E <wcs-outlet> \u306F <wcs-router> \u306E\u76F4\u5F8C\u306B\u7121\u3044\u306E\u3067\u3001\u3069\u306E router \u3082\u4F7F\u3044\u307E\u305B\u3093\u3002router \u306F\u81EA\u5206\u306E\u76F4\u5F8C\u306E\u8981\u7D20\u3060\u3051\u3092 outlet \u3068\u3057\u3066\u4F7F\u3044\u3001\u305D\u3053\u306B <wcs-outlet> \u304C\u7121\u3051\u308C\u3070\u81EA\u524D\u306E outlet \u3092\u4F5C\u308A\u307E\u3059\u3002\u30EB\u30FC\u30C8\u306F router \u306E\u76F4\u5F8C\u306B\u63CF\u304B\u308C\u3001\u3053\u306E outlet \u306F\u7A7A\u306E\u307E\u307E\u3067\u3059 \u2014 <wcs-router> \u306E\u76F4\u5F8C\u3078\u79FB\u3057\u3066\u304F\u3060\u3055\u3044`,
11044
11297
  signalsDualEntry: () => `@wcstack/signals \u3068 @wcstack/signals/dom \u304C\u540C\u4E00\u30DA\u30FC\u30B8\u304B\u3089 import \u3055\u308C\u3066\u3044\u307E\u3059\u3002CDN \u3067\u306F\u5404\u30A8\u30F3\u30C8\u30EA\u304C\u81EA\u5DF1\u5B8C\u7D50\u30D0\u30F3\u30C9\u30EB\u306E\u305F\u3081\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u30B3\u30A2\u304C\u4E8C\u91CD\u5316\u3057\u3001\u5883\u754C\u3067\u53CD\u5FDC\u304C\u58CA\u308C\u307E\u3059 \u2014 \u3059\u3079\u3066 /dom \u30A8\u30F3\u30C8\u30EA\u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044`,
11298
+ signalsImportMapBoth: () => `\u3053\u306E import map \u306F @wcstack/signals \u3068 @wcstack/signals/dom \u306E\u4E21\u65B9\u3092\u5272\u308A\u5F53\u3066\u3066\u3044\u307E\u3059\u3002CDN \u3067\u306F\u5404\u30A8\u30F3\u30C8\u30EA\u304C\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u30B3\u30A2\u3092\u6301\u3064\u306E\u3067\u3001@wcstack/signals \u3092 import \u3059\u308B\u30E2\u30B8\u30E5\u30FC\u30EB\uFF08\u3053\u306E\u30DA\u30FC\u30B8\u306E\u3082\u306E\u3067\u3082\u3001\u8AAD\u307F\u8FBC\u3080 .js \u3067\u3082\uFF09\u304C 2 \u3064\u76EE\u306E\u30B3\u30A2\u3092\u6301\u3061\u3001\u5883\u754C\u3067\u53CD\u5FDC\u304C\u58CA\u308C\u307E\u3059\u3002@wcstack/signals/dom \u3060\u3051\u3092\u5272\u308A\u5F53\u3066\u3066\u304F\u3060\u3055\u3044\uFF08\u30B3\u30A2\u306E\u5168\u90E8\u3092 export \u3057\u307E\u3059\uFF09`,
11299
+ routeBareMustache: (inLayout) => inLayout ? `\u30EC\u30A4\u30A2\u30A6\u30C8\u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u306E\u76F4\u4E0B\u306E\u30C6\u30AD\u30B9\u30C8\u306E {{ }} \u306F\u3001\u9077\u79FB\u3067\u306F\u675F\u306D\u3089\u308C\u307E\u305B\u3093 \u2014 \u8981\u7D20\u3067\u5305\u3093\u3067\u304F\u3060\u3055\u3044\uFF08<span>{{ \u2026 }}</span>\uFF09` : `<wcs-route> \u306E\u76F4\u4E0B\u306E\u30C6\u30AD\u30B9\u30C8\u306E {{ }} \u306F\u3001\u6700\u521D\u306B\u7740\u3044\u305F\u30EB\u30FC\u30C8\u3067\u3057\u304B\u675F\u306D\u3089\u308C\u307E\u305B\u3093\u3002\u9077\u79FB\u3067\u5165\u3063\u305F\u30EB\u30FC\u30C8\u3067\u306F\u66F8\u3044\u305F\u307E\u307E\u306E\u6587\u5B57\u304C\u51FA\u307E\u3059 \u2014 \u8981\u7D20\u3067\u5305\u3093\u3067\u304F\u3060\u3055\u3044\uFF08<h1>{{ \u2026 }}</h1>\uFF09`,
11300
+ layoutSlotMissing: (l) => `\u30EC\u30A4\u30A2\u30A6\u30C8 "${l}" \u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u306B\u65E2\u5B9A\u306E <slot>\uFF08name \u306E\u7121\u3044\u3082\u306E\uFF09\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u3053\u306E <wcs-layout> \u306E\u540D\u524D\u4ED8\u304D\u30B9\u30ED\u30C3\u30C8\u306E\u5916\u306E\u4E2D\u8EAB\uFF08\u5165\u308C\u5B50\u306E\u30EB\u30FC\u30C8\u3068\u305D\u306E\u30DA\u30FC\u30B8\uFF09\u306F\u8868\u793A\u3055\u308C\u307E\u305B\u3093 \u2014 \u30DA\u30FC\u30B8\u3092\u7F6E\u304F\u5834\u6240\u306B <slot></slot> \u3092\u66F8\u3044\u3066\u304F\u3060\u3055\u3044`,
11045
11301
  namedStateAttrDeprecated: (name) => `name \u5C5E\u6027\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\uFF081 root 1 \u30C4\u30EA\u30FC\uFF09\u3002\u30EB\u30FC\u30C8\u30C4\u30EA\u30FC\u3078\u306E\u30DE\u30A6\u30F3\u30C8 <wcs-state mount="${name}"> \u306B\u7F6E\u304D\u63DB\u3048\u3001\u30D1\u30B9\u306F "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`,
11046
11302
  namedStatePathDeprecated: (name) => name === "default" ? `"@default" \u30BB\u30EC\u30AF\u30BF\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\u3002"@default" \u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09` : `"@name" \u30BB\u30EC\u30AF\u30BF\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\uFF081 root 1 \u30C4\u30EA\u30FC\uFF09\u3002\u30DE\u30A6\u30F3\u30C8\u3057\u305F\u30C4\u30EA\u30FC\u3092 "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`,
11047
11303
  mountPathInvalid: (problem, mountPath) => {
@@ -11175,6 +11431,8 @@ var en = {
11175
11431
  getterCycle: (cycle) => `Path getters form a dependency cycle: ${cycle}`,
11176
11432
  updatedCallbackUnbound: (p) => `$renderedCallback is binding-driven. "${p}" is not bound anywhere in this document, so this branch never runs. Use $watch to react without depending on what is rendered`,
11177
11433
  onHandlerThis: (name) => `The $on handler "${name}" uses this. A $on handler receives the state as its first argument (\`${name}(state, event) { state.x = \u2026 }\`) and this is undefined, so this.\u2026 throws when it is called \u2014 unlike $watch handlers and methods`,
11434
+ getterReadsLocation: (read) => `This getter reads ${read}, which is not state: it keeps the URL it first read (a navigation does not re-evaluate it). Read the URL through state \u2014 bind <wcs-router>'s path / searchParams`,
11435
+ submitWithoutPrevent: (h) => `onsubmit has no #prevent and "${h}" does not call preventDefault, so the browser submits the form too: the page reloads, or with the router a GET puts the fields (a password too) in the URL and the history. Write onsubmit#prevent: ${h}, or call event.preventDefault() in the handler`,
11178
11436
  getterUntrackedRead: (root, sp) => `Only "${root}" is tracked here: the getter is not re-evaluated when "${sp}" changes (plain property access after a path read is not tracked). Read this["${sp}"] instead`,
11179
11437
  handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
11180
11438
  typeExpectation: (label, expected, resultType) => `"${label}" requires ${EN_EXPECTED_LABEL[expected]} (current type: ${resultType})`,
@@ -11198,6 +11456,8 @@ var en = {
11198
11456
  typeAnnotationIncompatible: (vt, rt) => `Type "${vt}" is not compatible with @type {${rt}}`,
11199
11457
  arrayMutation: (m, alt) => `Destructive array method "${m}" does not trigger a reactive update (re-assigning the same reference does not reflect added/removed elements either). Use a non-destructive method with reassignment (e.g. ${alt}).`,
11200
11458
  arrayIndexAssign: (sp) => `Assigning directly to an array index does not trigger a reactive update. Use a dot-path assignment like this["${sp}"], or with() plus reassignment.`,
11459
+ inPlaceAlias: (a, src, t) => `"${a}" is ${src} \u2014 the state's own value. A change through it does not trigger a reactive update, and writing the same value back does not add, remove or replace a list's rows. Build a new value and assign it to ${t} (${a}.with(i, item), [...${a}, item], { ...${a}, key: value })`,
11460
+ inPlaceChange: (src, t) => `${src} changes the state's value in place, which does not trigger a reactive update. Build a new value and assign it to ${t}`,
11201
11461
  tagMemberUnknown: (prop, tag) => `"${prop}" is not a wcBindable member of <${tag}> (bindings to unknown members are silently ignored)`,
11202
11462
  nameAlias: (written, canonical) => `"${written}" was removed in 4.0 (the 3.x old name of "${canonical}"; the runtime throws when it is read) \u2014 write "${canonical}"`,
11203
11463
  declarationAliasRead: (alias, canonical) => `"${alias}" is a declaration key 4.0 removed (the 3.x old name of "${canonical}"), so this["${alias}"] is undefined. Read "${canonical}" instead`,
@@ -11234,6 +11494,9 @@ var en = {
11234
11494
  baseHrefMissing: () => `An SPA using @wcstack/router needs <base href="/"> in <head> (without it, deep links misderive the basename)`,
11235
11495
  routerOutletMisplaced: () => `This <wcs-outlet> is not right after a <wcs-router>, so no router uses it: a router uses only the element right after it as its outlet, and makes an outlet of its own there when that is not a <wcs-outlet>. The routes render right after the router and this outlet stays empty \u2014 move it right after the <wcs-router>`,
11236
11496
  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`,
11497
+ signalsImportMapBoth: () => `This import map maps both @wcstack/signals and @wcstack/signals/dom. On a CDN each entry carries its own reactive core, so a module that imports @wcstack/signals \u2014 this page's, or a .js file it loads \u2014 gets a second core and reactivity breaks at the seam. Map only @wcstack/signals/dom (it exports the whole core)`,
11498
+ routeBareMustache: (inLayout) => inLayout ? `{{ }} in text directly under a layout template is not bound on a navigation. Wrap it in an element (<span>{{ \u2026 }}</span>)` : `{{ }} in text directly under <wcs-route> is bound only on the landing route: a route entered by navigation shows it as written. Wrap it in an element (<h1>{{ \u2026 }}</h1>)`,
11499
+ layoutSlotMissing: (l) => `The template of layout "${l}" has no default <slot> (one without a name), so what this <wcs-layout> holds outside the named slots \u2014 the nested routes and their pages \u2014 is not shown. Put a <slot></slot> where the page goes`,
11237
11500
  namedStateAttrDeprecated: (name) => `The "name" attribute was removed in v2 \u2014 there is a single state tree per root. Mount this state onto the tree instead: <wcs-state mount="${name}"> and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
11238
11501
  namedStatePathDeprecated: (name) => name === "default" ? `The "@default" selector was removed in v2 \u2014 drop it (docs/state-mount-design.md \xA79)` : `The "@name" selector was removed in v2 \u2014 there is a single state tree. Mount the named state onto the tree (<wcs-state mount="...">) and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
11239
11502
  mountPathInvalid: (problem, mountPath) => {
@@ -11456,6 +11719,19 @@ var DELEGATED_EVENTS = /* @__PURE__ */ new Set([
11456
11719
  var CUSTOM_ELEMENT_OWN_EVENTS = /* @__PURE__ */ new Set(["input", "change", "submit"]);
11457
11720
  function collectCurrentTargetHandlers(elements, html, fileReader) {
11458
11721
  const names = /* @__PURE__ */ new Set();
11722
+ forEachStateMethod(elements, html, fileReader, "currentTarget", (name, params, body) => {
11723
+ if (readsEventCurrentTarget(params, body)) names.add(name);
11724
+ });
11725
+ return names;
11726
+ }
11727
+ function collectPreventDefaultHandlers(elements, html, fileReader) {
11728
+ const handlers = /* @__PURE__ */ new Map();
11729
+ forEachStateMethod(elements, html, fileReader, null, (name, _params, body) => {
11730
+ handlers.set(name, /\bpreventDefault\b/.test(body));
11731
+ });
11732
+ return handlers;
11733
+ }
11734
+ function forEachStateMethod(elements, html, fileReader, spelling, fn) {
11459
11735
  let insideTemplate = null;
11460
11736
  for (const element2 of elements) {
11461
11737
  if (element2.bindComponent) continue;
@@ -11465,20 +11741,19 @@ function collectCurrentTargetHandlers(elements, html, fileReader) {
11465
11741
  const script = readStateScript(element2.srcAttr, fileReader);
11466
11742
  if (script !== void 0) scripts = [script];
11467
11743
  }
11468
- scripts = scripts.filter((script) => script.includes("currentTarget"));
11744
+ if (spelling !== null) scripts = scripts.filter((script) => script.includes(spelling));
11469
11745
  if (scripts.length === 0) continue;
11470
11746
  if ((insideTemplate ??= createTemplateTester(html))(element2.tagStart)) continue;
11471
11747
  for (const script of scripts) {
11472
11748
  for (const callable of analyzeCallableBodies(script)) {
11473
- if (callable.kind !== "method" || !callable.body.includes("currentTarget")) continue;
11749
+ if (callable.kind !== "method" || spelling !== null && !callable.body.includes(spelling)) continue;
11474
11750
  const head = script.slice(callable.end, callable.bodyStart - 1);
11475
11751
  const params = /\(([^)]*)\)\s*$/.exec(head);
11476
11752
  if (params === null) continue;
11477
- if (readsEventCurrentTarget(params[1], callable.body)) names.add(volume === null ? callable.name : `${volume}.${callable.name}`);
11753
+ fn(volume === null ? callable.name : `${volume}.${callable.name}`, params[1], callable.body);
11478
11754
  }
11479
11755
  }
11480
11756
  }
11481
- return names;
11482
11757
  }
11483
11758
  function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, fileReader, applicationSchema) {
11484
11759
  const diagnostics = [];
@@ -11498,6 +11773,11 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
11498
11773
  currentTargetHandlers ??= collectCurrentTargetHandlers(pathIndex.elements, html, fileReader);
11499
11774
  return currentTargetHandlers;
11500
11775
  };
11776
+ let preventDefaultHandlers = null;
11777
+ const getPreventDefaultHandlers = () => {
11778
+ preventDefaultHandlers ??= collectPreventDefaultHandlers(pathIndex.elements, html, fileReader);
11779
+ return preventDefaultHandlers;
11780
+ };
11501
11781
  for (const attr of attrs) {
11502
11782
  const bindings = splitBindingExpressions(attr.value);
11503
11783
  const nonEmptyCount = bindings.filter((b) => b.trim().length > 0).length;
@@ -11559,6 +11839,15 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
11559
11839
  const eventName = propNoMod.slice(2);
11560
11840
  const handler = parsed.path.trim();
11561
11841
  const modifiers = modifiersOf(parsed.property);
11842
+ if (eventName === "submit" && attr.tagName === "form" && !modifiers.includes("prevent") && getPreventDefaultHandlers().get(handler) === false && !/\saction\s*=/i.test(openTagBefore(html, attr.valueStart))) {
11843
+ diagnostics.push({
11844
+ code: WcsDiagnosticCode.SubmitWithoutPrevent,
11845
+ start: bindingStart + leadingSpace,
11846
+ end: bindingStart + leadingSpace + propNoMod.length,
11847
+ message: msgs.submitWithoutPrevent(handler),
11848
+ severity: "warning"
11849
+ });
11850
+ }
11562
11851
  if (DELEGATED_EVENTS.has(eventName) && !modifiers.includes(DIRECT_MODIFIER) && attr.tagName !== void 0 && !(attr.tagName.includes("-") && CUSTOM_ELEMENT_OWN_EVENTS.has(eventName)) && /^[A-Za-z_][\w$]*(?:\.[A-Za-z_][\w$]*)*$/.test(handler) && getCurrentTargetHandlers().has(handler)) {
11563
11852
  currentTargetCandidates.push({
11564
11853
  offset: attr.valueStart,
@@ -11867,6 +12156,10 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
11867
12156
  }
11868
12157
  return diagnostics;
11869
12158
  }
12159
+ function openTagBefore(html, valueStart) {
12160
+ const lt2 = html.lastIndexOf("<", valueStart);
12161
+ return lt2 === -1 ? "" : html.slice(lt2, valueStart);
12162
+ }
11870
12163
  function findAllBindAttributes(html, attrName) {
11871
12164
  const attrs = [];
11872
12165
  const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -14459,9 +14752,63 @@ function validateDocumentEnv(html, locale2) {
14459
14752
  severity: "error",
14460
14753
  message: msgs.signalsDualEntry()
14461
14754
  });
14755
+ } else {
14756
+ const both = findSignalsImportMapBoth(scanText);
14757
+ if (both) {
14758
+ diagnostics.push({
14759
+ code: WcsDiagnosticCode.SignalsDualEntry,
14760
+ start: both.start,
14761
+ end: both.end,
14762
+ severity: "warning",
14763
+ message: msgs.signalsImportMapBoth()
14764
+ });
14765
+ }
14766
+ }
14767
+ const structure = scanStructure(scanText);
14768
+ for (const at2 of structure.bareMustaches) {
14769
+ diagnostics.push({
14770
+ code: WcsDiagnosticCode.RouteBareMustache,
14771
+ start: at2.start,
14772
+ end: at2.end,
14773
+ severity: "warning",
14774
+ message: msgs.routeBareMustache(at2.inLayout)
14775
+ });
14776
+ }
14777
+ for (const layout of structure.layoutsWithoutDefaultSlot) {
14778
+ diagnostics.push({
14779
+ code: WcsDiagnosticCode.LayoutSlotMissing,
14780
+ start: layout.start,
14781
+ end: layout.end,
14782
+ severity: "warning",
14783
+ message: msgs.layoutSlotMissing(layout.id)
14784
+ });
14462
14785
  }
14463
14786
  return diagnostics;
14464
14787
  }
14788
+ function findSignalsImportMapBoth(html) {
14789
+ const mapRegex = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/script\s*>/gi;
14790
+ let match;
14791
+ while ((match = mapRegex.exec(html)) !== null) {
14792
+ if (!/\btype\s*=\s*(["'])importmap\1/i.test(match[1])) continue;
14793
+ let imports;
14794
+ try {
14795
+ imports = JSON.parse(match[2]).imports;
14796
+ } catch {
14797
+ continue;
14798
+ }
14799
+ if (imports === null || typeof imports !== "object") continue;
14800
+ const core = imports["@wcstack/signals"];
14801
+ const dom = imports["@wcstack/signals/dom"] ?? imports["@wcstack/signals/"];
14802
+ if (typeof core !== "string" || typeof dom !== "string") continue;
14803
+ if (core.includes("/dist/") && dom.includes("/dist/")) continue;
14804
+ const key = '"@wcstack/signals"';
14805
+ const at2 = match[2].indexOf(key);
14806
+ const bodyStart = match.index + match[0].indexOf(">") + 1;
14807
+ const start = bodyStart + (at2 === -1 ? 0 : at2 + 1);
14808
+ return { start, end: start + key.length - 2 };
14809
+ }
14810
+ return null;
14811
+ }
14465
14812
  var SCOPED_AUTO = /@wcstack\/([a-z0-9-]+)(?:@[^/"'\s]+)?\/(?:auto\b|dist\/(?:split\/)?auto(?:\.min)?\.js\b)/;
14466
14813
  var BUNDLE_AUTO = /(?:^|\/)wcstack(?:@[^/"'\s]+)?\/(?:auto\b|dist\/auto\.min\.js\b)/;
14467
14814
  var BUNDLE_PACKAGES = ["state", "router", "fetch", "storage", "autoloader"];
@@ -14540,6 +14887,88 @@ function blankHtmlComments(html) {
14540
14887
  function blankJsComments(code) {
14541
14888
  return code.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/(^|[^:])\/\/[^\n]*/g, (m, pre) => pre + " ".repeat(m.length - pre.length));
14542
14889
  }
14890
+ var VOID_TAGS = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr"]);
14891
+ var ROUTE_TAG = "wcs-route";
14892
+ var LAYOUT_TAG2 = "wcs-layout";
14893
+ function attrValue(attrs, name) {
14894
+ const m = new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i").exec(attrs);
14895
+ return m === null ? null : m[1] ?? m[2] ?? m[3] ?? "";
14896
+ }
14897
+ function scanStructure(html) {
14898
+ const scan = html.replace(
14899
+ /(<(script|style)\b[^>]*>)([\s\S]*?)(<\/\2\s*>)/gi,
14900
+ (_m, open, _name, body, close) => open + " ".repeat(body.length) + close
14901
+ );
14902
+ const tagRegex = /<(\/?)([a-zA-Z][\w-]*)((?:"[^"]*"|'[^']*'|[^>"'])*)>/g;
14903
+ const layoutIds = /* @__PURE__ */ new Set();
14904
+ for (const m of scan.matchAll(/<wcs-layout\b((?:"[^"]*"|'[^']*'|[^>"'])*)>/gi)) {
14905
+ const id = attrValue(m[1], "layout");
14906
+ if (id !== null && id !== "") layoutIds.add(id);
14907
+ }
14908
+ const bareMustaches = [];
14909
+ const layouts = [];
14910
+ const templateHasDefaultSlot = /* @__PURE__ */ new Map();
14911
+ const stack = [];
14912
+ let last = 0;
14913
+ let match;
14914
+ const text3 = (from, to) => {
14915
+ const top = stack[stack.length - 1];
14916
+ if (top === void 0 || to <= from) return;
14917
+ const chunk = scan.slice(from, to);
14918
+ if (top.name === LAYOUT_TAG2 && chunk.trim() !== "") top.unslotted = true;
14919
+ if (top.name !== ROUTE_TAG && !top.layoutTemplate) return;
14920
+ for (const m of chunk.matchAll(/\{\{[\s\S]*?\}\}/g)) {
14921
+ bareMustaches.push({ start: from + m.index, end: from + m.index + m[0].length, inLayout: top.layoutTemplate });
14922
+ }
14923
+ };
14924
+ while ((match = tagRegex.exec(scan)) !== null) {
14925
+ text3(last, match.index);
14926
+ last = match.index + match[0].length;
14927
+ const name = match[2].toLowerCase();
14928
+ const nameStart = match.index + 1 + match[1].length;
14929
+ if (match[1] === "/") {
14930
+ const at2 = stack.map((e) => e.name).lastIndexOf(name);
14931
+ if (at2 === -1) continue;
14932
+ for (const closed of stack.splice(at2)) {
14933
+ if (closed.name === LAYOUT_TAG2 && closed.unslotted && closed.layoutId !== void 0) {
14934
+ layouts.push({ start: closed.nameStart, end: closed.nameEnd, id: closed.layoutId });
14935
+ }
14936
+ }
14937
+ continue;
14938
+ }
14939
+ const attrs = match[3];
14940
+ const parent = stack[stack.length - 1];
14941
+ if (parent?.name === LAYOUT_TAG2 && !attrValue(attrs, "slot")) parent.unslotted = true;
14942
+ for (const open of stack) {
14943
+ if (open.layoutTemplate && name === "slot" && !attrValue(attrs, "name")) templateHasDefaultSlot.set(open.layoutId, true);
14944
+ }
14945
+ if (VOID_TAGS.has(name) || /\/\s*$/.test(attrs)) continue;
14946
+ const id = name === "template" ? attrValue(attrs, "id") : null;
14947
+ const element2 = {
14948
+ name,
14949
+ layoutTemplate: id !== null && layoutIds.has(id),
14950
+ nameStart,
14951
+ nameEnd: nameStart + match[2].length
14952
+ };
14953
+ if (element2.layoutTemplate) {
14954
+ element2.layoutId = id;
14955
+ if (!templateHasDefaultSlot.has(id)) templateHasDefaultSlot.set(id, false);
14956
+ }
14957
+ if (name === LAYOUT_TAG2 && attrValue(attrs, "src") === null) {
14958
+ const layoutId = attrValue(attrs, "layout");
14959
+ if (layoutId !== null && layoutId !== "") element2.layoutId = layoutId;
14960
+ }
14961
+ stack.push(element2);
14962
+ }
14963
+ text3(last, scan.length);
14964
+ for (const closed of stack) {
14965
+ if (closed.name === LAYOUT_TAG2 && closed.unslotted && closed.layoutId !== void 0) {
14966
+ layouts.push({ start: closed.nameStart, end: closed.nameEnd, id: closed.layoutId });
14967
+ }
14968
+ }
14969
+ const layoutsWithoutDefaultSlot = layouts.filter((l) => templateHasDefaultSlot.get(l.id) === false);
14970
+ return { bareMustaches, layoutsWithoutDefaultSlot };
14971
+ }
14543
14972
  var ROUTER_TAG2 = "wcs-router";
14544
14973
  var OUTLET_TAG = "wcs-outlet";
14545
14974
  function findMisplacedOutlets(html) {
@@ -15969,6 +16398,54 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
15969
16398
  }
15970
16399
  return out;
15971
16400
  }
16401
+ function validateGetterLocationReads(script, scriptStart, locale2) {
16402
+ if (!script.includes("location")) return [];
16403
+ const msgs = getMessages(locale2);
16404
+ const out = [];
16405
+ for (const getter of analyzeCallableBodies(script)) {
16406
+ if (getter.kind !== "getter" || getter.accessor !== "get" || !getter.body.includes("location")) continue;
16407
+ const read = findLocationRead(getter.body);
16408
+ if (read === null) continue;
16409
+ out.push({
16410
+ code: WcsDiagnosticCode.GetterUntrackedRead,
16411
+ start: scriptStart + getter.bodyStart + read.start,
16412
+ end: scriptStart + getter.bodyStart + read.end,
16413
+ message: msgs.getterReadsLocation(read.text),
16414
+ severity: "warning"
16415
+ });
16416
+ }
16417
+ return out;
16418
+ }
16419
+ function onHandlerStateParam(script, handler) {
16420
+ if (handler.kind === "body") {
16421
+ const head = script.slice(Math.max(0, handler.start - 400), handler.start);
16422
+ return /\(\s*([A-Za-z_$][\w$]*)[^()]*\)\s*\{\s*$/.exec(head)?.[1] ?? null;
16423
+ }
16424
+ return /^\s*(?:async\s+)?(?:function\b[^(]*\(\s*([A-Za-z_$][\w$]*)|\(\s*([A-Za-z_$][\w$]*)|([A-Za-z_$][\w$]*)\s*=>)/.exec(handler.text)?.slice(1).find((name) => name !== void 0) ?? null;
16425
+ }
16426
+ function validateInPlaceChanges(script, scriptStart, locale2) {
16427
+ const msgs = getMessages(locale2);
16428
+ const out = [];
16429
+ const push3 = (text3, kind, textStart, root) => {
16430
+ for (const change of findInPlaceChanges(text3, kind, root)) {
16431
+ out.push({
16432
+ code: change.form === "method" ? WcsDiagnosticCode.ArrayMutation : change.form === "index" ? WcsDiagnosticCode.ArrayIndexAssign : WcsDiagnosticCode.NestedAssign,
16433
+ start: scriptStart + textStart + change.start,
16434
+ end: scriptStart + textStart + change.end,
16435
+ severity: "error",
16436
+ message: change.alias !== null ? msgs.inPlaceAlias(change.alias, change.source, change.target) : msgs.inPlaceChange(change.source, change.target)
16437
+ });
16438
+ }
16439
+ };
16440
+ for (const callable of analyzeCallableBodies(script)) push3(callable.body, "body", callable.bodyStart, "this");
16441
+ for (const handler of analyzeWatchHandlerSources(script)) push3(handler.text, handler.kind, handler.start, "this");
16442
+ for (const handler of analyzeOnHandlerSources(script)) {
16443
+ const root = handler.kind === "body" ? onHandlerStateParam(script, handler) : null;
16444
+ if (handler.kind === "body" && root === null) continue;
16445
+ push3(handler.text, handler.kind, handler.start, root);
16446
+ }
16447
+ return out;
16448
+ }
15972
16449
  var NESTED_WRITE_INPUTS = {
15973
16450
  "wcs-storage": { inputs: ["value"], exemptWhenManual: true },
15974
16451
  "wcs-fetch": { inputs: ["body"], exemptWhenManual: false, oneShot: true }
@@ -16080,6 +16557,26 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
16080
16557
  if (path !== null) addPrefixes(path, false);
16081
16558
  }
16082
16559
  }
16560
+ for (const handler of analyzeOnHandlerSources(block.content)) {
16561
+ const param = onHandlerStateParam(block.content, handler);
16562
+ if (param === null) continue;
16563
+ const name = param.replace(/\$/g, "\\$");
16564
+ const region = { start: handler.start, end: handler.start + handler.text.length };
16565
+ const bracket = `\\b${name}\\s*(?:\\?\\.)?\\s*\\[\\s*["']([^"']+)["']\\s*\\]`;
16566
+ const template = `\\b${name}\\s*(?:\\?\\.)?\\s*\\[\\s*\`([^\`]*)\`` + String.raw`\s*\]`;
16567
+ for (const [pattern, quote] of [[bracket, /["']/], [template, /`/]]) {
16568
+ for (const regex of [new RegExp(`${pattern}${ASSIGN_TAIL}`, "g"), new RegExp(`${PRE_INCDEC}${pattern}`, "g")]) {
16569
+ let match;
16570
+ const part = scan.slice(region.start, region.end);
16571
+ while ((match = regex.exec(part)) !== null) {
16572
+ const keyStart = region.start + match.index + match[0].search(quote) + 1;
16573
+ const raw = block.content.slice(keyStart, keyStart + match[1].length);
16574
+ const path = quote.source === "`" ? templateKeyPath(raw) : raw;
16575
+ if (path !== null) addPrefixes(path, false);
16576
+ }
16577
+ }
16578
+ }
16579
+ }
16083
16580
  API_CALL.lastIndex = 0;
16084
16581
  let call;
16085
16582
  while ((call = API_CALL.exec(scan)) !== null) {
@@ -16210,7 +16707,9 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
16210
16707
  out.push(...validateIndexParamReads(block.content, block.contentStart, locale2));
16211
16708
  out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
16212
16709
  out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
16710
+ out.push(...validateGetterLocationReads(block.content, block.contentStart, locale2));
16213
16711
  out.push(...validateOnHandlerThis(block.content, block.contentStart, locale2));
16712
+ out.push(...validateInPlaceChanges(block.content, block.contentStart, locale2));
16214
16713
  }
16215
16714
  out.push(...validateInputNestedWrites(html, stateTagName, bindAttrName, getNestedWriteRoots, locale2));
16216
16715
  out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2));
package/dist/tsc-core.cjs CHANGED
@@ -255,11 +255,17 @@ function optionsRequired(fnName) {
255
255
  function optionMustBeNumber(fnName) {
256
256
  raise(27, [fnName]);
257
257
  }
258
- function valueMustBeNumber(fnName) {
259
- raise(28, [fnName]);
258
+ function styleInvalid(fnName, value) {
259
+ raise(52, [fnName, value]);
260
260
  }
261
- function valueMustBeDate(fnName) {
262
- raise(29, [fnName]);
261
+ function currencyInvalid(value) {
262
+ raise(53, [value]);
263
+ }
264
+ function valueMustBeNumber(fnName, value) {
265
+ raise(28, [fnName, value]);
266
+ }
267
+ function valueMustBeDate(fnName, value) {
268
+ raise(29, [fnName, value]);
263
269
  }
264
270
  function valueMustBeArray(fnName) {
265
271
  raise(30, [fnName]);
@@ -326,7 +332,7 @@ var numeric = (name, op) => ({
326
332
  const opt = requiredNumberOption(options, 0, name);
327
333
  return (value) => {
328
334
  if (typeof value !== "number") {
329
- valueMustBeNumber(name);
335
+ valueMustBeNumber(name, value);
330
336
  }
331
337
  return op(value, opt);
332
338
  };
@@ -356,7 +362,7 @@ var toFixed = (options) => {
356
362
  const opt = numberOption(options?.[0] ?? "0", "toFixed");
357
363
  return (value) => {
358
364
  if (typeof value !== "number") {
359
- valueMustBeNumber("toFixed");
365
+ valueMustBeNumber("toFixed", value);
360
366
  }
361
367
  return value.toFixed(opt);
362
368
  };
@@ -365,7 +371,7 @@ var round = (options) => {
365
371
  const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "round"));
366
372
  return (value) => {
367
373
  if (typeof value !== "number") {
368
- valueMustBeNumber("round");
374
+ valueMustBeNumber("round", value);
369
375
  }
370
376
  return Math.round(value * optValue) / optValue;
371
377
  };
@@ -374,7 +380,7 @@ var floor = (options) => {
374
380
  const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "floor"));
375
381
  return (value) => {
376
382
  if (typeof value !== "number") {
377
- valueMustBeNumber("floor");
383
+ valueMustBeNumber("floor", value);
378
384
  }
379
385
  return Math.floor(value * optValue) / optValue;
380
386
  };
@@ -383,7 +389,7 @@ var ceil = (options) => {
383
389
  const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "ceil"));
384
390
  return (value) => {
385
391
  if (typeof value !== "number") {
386
- valueMustBeNumber("ceil");
392
+ valueMustBeNumber("ceil", value);
387
393
  }
388
394
  return Math.ceil(value * optValue) / optValue;
389
395
  };
@@ -392,7 +398,7 @@ var percent = (options) => {
392
398
  const opt = numberOption(options?.[0] ?? "0", "percent");
393
399
  return (value) => {
394
400
  if (typeof value !== "number") {
395
- valueMustBeNumber("percent");
401
+ valueMustBeNumber("percent", value);
396
402
  }
397
403
  return `${(value * 100).toFixed(opt)}%`;
398
404
  };
@@ -419,11 +425,29 @@ var locale = (options) => {
419
425
  const explicit = options?.[0];
420
426
  return (value) => {
421
427
  if (typeof value !== "number") {
422
- valueMustBeNumber("locale");
428
+ valueMustBeNumber("locale", value);
423
429
  }
424
430
  return value.toLocaleString(explicit ?? defaultLocale());
425
431
  };
426
432
  };
433
+ var currency = (options) => {
434
+ const code = options?.[0] ?? optionsRequired("currency");
435
+ const explicit = options?.[1];
436
+ const format = { style: "currency", currency: code };
437
+ try {
438
+ new Intl.NumberFormat("en", format);
439
+ const known = Intl.supportedValuesOf?.("currency");
440
+ if (known && !known.includes(code.toUpperCase())) throw 0;
441
+ } catch {
442
+ currencyInvalid(code);
443
+ }
444
+ return (value) => {
445
+ if (typeof value !== "number") {
446
+ valueMustBeNumber("currency", value);
447
+ }
448
+ return value.toLocaleString(explicit ?? defaultLocale(), format);
449
+ };
450
+ };
427
451
  var upper = () => (value) => String(value).toUpperCase();
428
452
  var lower = () => (value) => String(value).toLowerCase();
429
453
  var capitalize = () => (value) => {
@@ -480,38 +504,48 @@ var join = (options) => {
480
504
  return value.join(opt);
481
505
  };
482
506
  };
507
+ var STYLES = ["full", "long", "medium", "short"];
508
+ function dateOptions(options, fn) {
509
+ let [loc, style] = options ?? [];
510
+ if (style === void 0 && loc !== void 0 && STYLES.includes(loc)) [loc, style] = [void 0, loc];
511
+ if (style !== void 0 && !STYLES.includes(style)) styleInvalid(fn, style);
512
+ return [loc, style];
513
+ }
483
514
  var date = (options) => {
484
- const explicit = options?.[0];
515
+ const [explicit, style] = dateOptions(options, "date");
516
+ const format = style && { dateStyle: style };
485
517
  return (value) => {
486
518
  if (!(value instanceof Date)) {
487
- valueMustBeDate("date");
519
+ valueMustBeDate("date", value);
488
520
  }
489
- return value.toLocaleDateString(explicit ?? defaultLocale());
521
+ return value.toLocaleDateString(explicit ?? defaultLocale(), format);
490
522
  };
491
523
  };
492
524
  var time = (options) => {
493
- const explicit = options?.[0];
525
+ const [explicit, style] = dateOptions(options, "time");
526
+ const format = style && { timeStyle: style };
494
527
  return (value) => {
495
528
  if (!(value instanceof Date)) {
496
- valueMustBeDate("time");
529
+ valueMustBeDate("time", value);
497
530
  }
498
- return value.toLocaleTimeString(explicit ?? defaultLocale());
531
+ return value.toLocaleTimeString(explicit ?? defaultLocale(), format);
499
532
  };
500
533
  };
501
534
  var datetime = (options) => {
502
- const explicit = options?.[0];
535
+ const [explicit, style] = dateOptions(options, "datetime");
536
+ const format = style && { dateStyle: style, timeStyle: style };
503
537
  return (value) => {
504
538
  if (!(value instanceof Date)) {
505
- valueMustBeDate("datetime");
539
+ valueMustBeDate("datetime", value);
506
540
  }
507
- return value.toLocaleString(explicit ?? defaultLocale());
541
+ return value.toLocaleString(explicit ?? defaultLocale(), format);
508
542
  };
509
543
  };
510
544
  var ymd = (options) => {
511
545
  const opt = options?.[0] ?? "-";
512
546
  return (value) => {
513
547
  if (!(value instanceof Date)) {
514
- valueMustBeDate("ymd");
548
+ valueMustBeDate("ymd", value);
515
549
  }
516
550
  const year = value.getFullYear().toString();
517
551
  const month = (value.getMonth() + 1).toString().padStart(2, "0");
@@ -523,7 +557,7 @@ var hms = (options) => {
523
557
  const opt = options?.[0] ?? ":";
524
558
  return (value) => {
525
559
  if (!(value instanceof Date)) {
526
- valueMustBeDate("hms");
560
+ valueMustBeDate("hms", value);
527
561
  }
528
562
  const hours = value.getHours().toString().padStart(2, "0");
529
563
  const minutes = value.getMinutes().toString().padStart(2, "0");
@@ -533,7 +567,7 @@ var hms = (options) => {
533
567
  };
534
568
  var abs = () => (value) => {
535
569
  if (typeof value !== "number") {
536
- valueMustBeNumber("abs");
570
+ valueMustBeNumber("abs", value);
537
571
  }
538
572
  return Math.abs(value);
539
573
  };
@@ -542,7 +576,7 @@ var clamp = (options) => {
542
576
  const max = requiredNumberOption(options, 1, "clamp");
543
577
  return (value) => {
544
578
  if (typeof value !== "number") {
545
- valueMustBeNumber("clamp");
579
+ valueMustBeNumber("clamp", value);
546
580
  }
547
581
  return Math.min(Math.max(value, min), max);
548
582
  };
@@ -589,10 +623,10 @@ var registered = {
589
623
  ceil: { factory: ceil, arity: [0, 1] },
590
624
  percent: { factory: percent, arity: [0, 1] },
591
625
  unit: { factory: nullishPassthrough(unit), arity: [1, 1] },
592
- // The locale-dependent three take a locale like `locale` does (`date(ja-JP)`)
593
- date: { factory: date, arity: [0, 1] },
594
- time: { factory: time, arity: [0, 1] },
595
- datetime: { factory: datetime, arity: [0, 1] },
626
+ // The locale-dependent three take a locale like `locale` does (`date(ja-JP)`), and a style (`date(ja-JP,long)`)
627
+ date: { factory: date, arity: [0, 2] },
628
+ time: { factory: time, arity: [0, 2] },
629
+ datetime: { factory: datetime, arity: [0, 2] },
596
630
  ymd: { factory: ymd, arity: [0, 1] },
597
631
  hms: { factory: hms, arity: [0, 1] },
598
632
  // the core set of 4.0.0-rc.3 (the core's own: eq ne not lt le gt ge falsy truthy boolean)
@@ -619,7 +653,9 @@ var registered = {
619
653
  boolean: boolean2,
620
654
  number: { factory: number, arity: [0, 0] },
621
655
  string: { factory: string, arity: [0, 0] },
622
- nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] }
656
+ nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] },
657
+ // (added in 4.2: last, so the order of the others is unchanged)
658
+ currency: { factory: currency, arity: [1, 2] }
623
659
  };
624
660
  var formatFilters = /* @__PURE__ */ Object.fromEntries(
625
661
  /* @__PURE__ */ Object.entries(registered).filter(([name]) => !(name in coreFilters))
@@ -690,10 +726,11 @@ var builtinFilterMeta = {
690
726
  unit: { description: "\u5358\u4F4D\uFF08\u63A5\u5C3E\u8F9E\uFF09\u3092\u4ED8\u52A0", hasArgs: true, resultType: "string", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["string"] },
691
727
  // 日付・時刻
692
728
  // `locale` と同じくロケールを 1 つ受ける(`date(ja-JP)`)。実装は最初からこれを読んでいたが、
693
- // メタデータ側が maxArgs: 0 だったため lint と補完が正しい書き方を誤りとして報告していた
694
- date: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u4ED8 ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
695
- time: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u6642\u523B ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
696
- datetime: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u6642 ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
729
+ // メタデータ側が maxArgs: 0 だったため lint と補完が正しい書き方を誤りとして報告していた。
730
+ // 4.2: 2 つ目は書式(full / long / medium / short — Intl の dateStyle / timeStyle)。書式だけなら既定のロケール
731
+ date: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u4ED8 ([locale][, style]\u3002style \u306F full / long / medium / short\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 2, argTypes: ["string", "string"] },
732
+ time: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u6642\u523B ([locale][, style]\u3002style \u306F full / long / medium / short\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 2, argTypes: ["string", "string"] },
733
+ datetime: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u6642 ([locale][, style]\u3002style \u306F full / long / medium / short\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 2, argTypes: ["string", "string"] },
697
734
  ymd: { description: "YYYY-MM-DD \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F -)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
698
735
  hms: { description: "HH:MM:SS \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F :)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
699
736
  // 真偽値・変換
@@ -704,7 +741,9 @@ var builtinFilterMeta = {
704
741
  boolean: { description: "\u30D6\u30FC\u30EB\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
705
742
  number: { description: "\u6570\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "number", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
706
743
  string: { description: "\u6587\u5B57\u5217\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
707
- nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 }
744
+ nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
745
+ // 4.2(formats の最後に登録)
746
+ currency: { description: "\u901A\u8CA8\u306E\u66F8\u5F0F (code[, locale]\u3002code \u306F ISO 4217 \u2014 JPY / USD / EUR\u3001\u65E2\u5B9A\u306E\u30ED\u30B1\u30FC\u30EB\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 1, maxArgs: 2, argTypes: ["string", "string"] }
708
747
  };
709
748
  var WCS_MANIFEST_VERSION = 2;
710
749
  var builtinFilterAliases = Object.freeze({});
@@ -573,7 +573,7 @@ function loadSchemaCore() {
573
573
  return cached;
574
574
  }
575
575
 
576
- var version = "4.1.1";
576
+ var version = "4.2.0";
577
577
  var pkg = {
578
578
  version: version};
579
579
 
package/dist/wcs-tsc.mjs CHANGED
@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
5
5
  import { dirname, join, isAbsolute, resolve, basename } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
- var version = "4.1.1";
8
+ var version = "4.2.0";
9
9
  var pkg = {
10
10
  version: version};
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/typescript",
3
- "version": "4.1.1",
3
+ "version": "4.2.0",
4
4
  "description": "TypeScript tooling for wcstack apps: wcs-schema generates the sidecar stateSchema from a typed state file, so wcs-validate checks data-wcs paths against real types in CI and every editor.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",