@wcstack/state 3.1.0 → 3.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.
Files changed (36) hide show
  1. package/README.ja.md +25 -17
  2. package/README.md +25 -17
  3. package/dist/auto.min.js +1 -1
  4. package/dist/auto.min.js.map +1 -1
  5. package/dist/define.d.ts +11 -7
  6. package/dist/index.d.ts +13 -7
  7. package/dist/index.esm.js +1 -1
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/manifest.d.ts +12 -1
  10. package/dist/manifest.esm.js +90 -35
  11. package/dist/split/chunks/declarationAliases.js +2 -0
  12. package/dist/split/chunks/declarationAliases.js.map +1 -0
  13. package/dist/split/chunks/filterRegistry.js +1 -1
  14. package/dist/split/chunks/filterRegistry.js.map +1 -1
  15. package/dist/split/chunks/mount.js +1 -1
  16. package/dist/split/chunks/mount.js.map +1 -1
  17. package/dist/split/chunks/pathDiagnostics.js +1 -1
  18. package/dist/split/chunks/pathDiagnostics.js.map +1 -1
  19. package/dist/split/chunks/registerComponents.js +1 -1
  20. package/dist/split/chunks/registerComponents.js.map +1 -1
  21. package/dist/split/chunks/version.js +1 -1
  22. package/dist/split/core.d.ts +11 -7
  23. package/dist/split/core.js +1 -1
  24. package/dist/split/core.js.map +1 -1
  25. package/dist/split/features/devtools.js +1 -1
  26. package/dist/split/features/devtools.js.map +1 -1
  27. package/dist/split/features/formats.js +1 -1
  28. package/dist/split/features/formats.js.map +1 -1
  29. package/dist/split/features/scopes.js +1 -1
  30. package/dist/split/features/scopes.js.map +1 -1
  31. package/dist/split/features/ssr.js +1 -1
  32. package/dist/split/features/ssr.js.map +1 -1
  33. package/dist/split/features/temporal.js +1 -1
  34. package/dist/split/features/temporal.js.map +1 -1
  35. package/dist/wcs-manifest.json +64 -25
  36. package/package.json +1 -1
@@ -30,6 +30,15 @@ interface IFilterMeta {
30
30
  /** 組み込みフィルタ名 → 構造化メタデータ。キー集合は builtinFilters と一致しなければならない。 */
31
31
  declare const builtinFilterMeta: Record<string, IFilterMeta>;
32
32
 
33
+ /**
34
+ * filters/filterAliases.ts — 組み込みフィルタの旧名 → 正式名(要件 B12・docs/state-3x-naming.ja.md V1〜V9)。
35
+ *
36
+ * 旧名は 3.x の間エイリアスとして残り、4.0 で外す(D4)。解決は登録簿(core/filterRegistry)が行い、
37
+ * 実装・引数の個数・メタデータは正式名だけが持つ。formats の install と manifest(tooling)の両方が読むので、
38
+ * 実装にもメタデータにも依存しない小さな表として独立させている。
39
+ */
40
+ declare const builtinFilterAliases: Readonly<Record<string, string>>;
41
+
33
42
  type BindingType = 'text' | 'prop' | 'event' | 'for' | 'if' | 'elseif' | 'else' | 'radio' | 'checkbox' | 'spread';
34
43
 
35
44
  declare const STRUCTURAL_BINDING_TYPE_SET: Set<BindingType>;
@@ -98,6 +107,8 @@ interface IWcsManifest {
98
107
  filters: string[];
99
108
  /** 組み込みフィルタの構造化メタデータ(説明・引数仕様・型)。vscode-wcs の手リスト撤去用。 */
100
109
  filterMeta: Record<string, IFilterMeta>;
110
+ /** 組み込みフィルタの旧名 → 正式名(要件 B12)。旧名も解決するが、ツールは正式名を提案する */
111
+ filterAliases: Readonly<Record<string, string>>;
101
112
  /** 予約ライフサイクルフック名 */
102
113
  reservedLifecycle: readonly string[];
103
114
  /** 予約 state API(プロトコル系の `$` 名前空間) */
@@ -106,5 +117,5 @@ interface IWcsManifest {
106
117
  /** 機械可読な単一正本を返す。vscode-wcs はこれを消費する想定。 */
107
118
  declare function getWcsManifest(): IWcsManifest;
108
119
 
109
- export { STRUCTURAL_BINDING_TYPE_SET, WCS_MANIFEST_VERSION, builtinFilterMeta, getWcsManifest };
120
+ export { STRUCTURAL_BINDING_TYPE_SET, WCS_MANIFEST_VERSION, builtinFilterAliases, builtinFilterMeta, getWcsManifest };
110
121
  export type { FilterArgType, FilterResultType, IFilterMeta, IWcsManifest };
@@ -81,6 +81,26 @@ function valueMustBeArray(fnName) {
81
81
  raiseError(`filter ${fnName} requires an array value`);
82
82
  }
83
83
 
84
+ /**
85
+ * filters/filterAliases.ts — 組み込みフィルタの旧名 → 正式名(要件 B12・docs/state-3x-naming.ja.md V1〜V9)。
86
+ *
87
+ * 旧名は 3.x の間エイリアスとして残り、4.0 で外す(D4)。解決は登録簿(core/filterRegistry)が行い、
88
+ * 実装・引数の個数・メタデータは正式名だけが持つ。formats の install と manifest(tooling)の両方が読むので、
89
+ * 実装にもメタデータにも依存しない小さな表として独立させている。
90
+ */
91
+ const builtinFilterAliases = {
92
+ inc: "add",
93
+ dec: "sub",
94
+ fix: "toFixed",
95
+ uc: "upper",
96
+ lc: "lower",
97
+ cap: "capitalize",
98
+ rep: "repeat",
99
+ rev: "reverse",
100
+ pad: "padStart",
101
+ null: "nullIfEmpty",
102
+ };
103
+
84
104
  /**
85
105
  * builtinFilters.ts
86
106
  *
@@ -245,13 +265,13 @@ const ge = (options) => {
245
265
  * @returns Filter function that returns incremented number
246
266
  */
247
267
  const inc = (options) => {
248
- const opt = options?.[0] ?? optionsRequired('inc');
268
+ const opt = options?.[0] ?? optionsRequired('add');
249
269
  if (!validateNumberString(opt)) {
250
- optionMustBeNumber('inc');
270
+ optionMustBeNumber('add');
251
271
  }
252
272
  return (value) => {
253
273
  if (typeof value !== 'number') {
254
- valueMustBeNumber('inc');
274
+ valueMustBeNumber('add');
255
275
  }
256
276
  return value + Number(opt);
257
277
  };
@@ -263,13 +283,13 @@ const inc = (options) => {
263
283
  * @returns Filter function that returns decremented number
264
284
  */
265
285
  const dec = (options) => {
266
- const opt = options?.[0] ?? optionsRequired('dec');
286
+ const opt = options?.[0] ?? optionsRequired('sub');
267
287
  if (!validateNumberString(opt)) {
268
- optionMustBeNumber('dec');
288
+ optionMustBeNumber('sub');
269
289
  }
270
290
  return (value) => {
271
291
  if (typeof value !== 'number') {
272
- valueMustBeNumber('dec');
292
+ valueMustBeNumber('sub');
273
293
  }
274
294
  return value - Number(opt);
275
295
  };
@@ -379,11 +399,11 @@ const clamp = (options) => {
379
399
  const fix = (options) => {
380
400
  const opt = options?.[0] ?? "0";
381
401
  if (!validateNumberString(opt)) {
382
- optionMustBeNumber('fix');
402
+ optionMustBeNumber('toFixed');
383
403
  }
384
404
  return (value) => {
385
405
  if (typeof value !== 'number') {
386
- valueMustBeNumber('fix');
406
+ valueMustBeNumber('toFixed');
387
407
  }
388
408
  return value.toFixed(Number(opt));
389
409
  };
@@ -521,15 +541,31 @@ const substr = (options) => {
521
541
  * @returns Filter function that returns padded string
522
542
  */
523
543
  const pad = (options) => {
524
- const opt1 = options?.[0] ?? optionsRequired('pad');
544
+ const opt1 = options?.[0] ?? optionsRequired('padStart');
525
545
  if (!validateNumberString(opt1)) {
526
- optionMustBeNumber('pad');
546
+ optionMustBeNumber('padStart');
527
547
  }
528
548
  const opt2 = options?.[1] ?? '0';
529
549
  return (value) => {
530
550
  return String(value).padStart(Number(opt1), opt2);
531
551
  };
532
552
  };
553
+ /**
554
+ * padEnd filter - pads string to specified length from the end (the pair of `padStart`, requirement B12).
555
+ *
556
+ * @param options - Array with target length and pad string (default: ' ')
557
+ * @returns Filter function that returns padded string
558
+ */
559
+ const padEnd = (options) => {
560
+ const opt1 = options?.[0] ?? optionsRequired('padEnd');
561
+ if (!validateNumberString(opt1)) {
562
+ optionMustBeNumber('padEnd');
563
+ }
564
+ const opt2 = options?.[1] ?? ' ';
565
+ return (value) => {
566
+ return String(value).padEnd(Number(opt1), opt2);
567
+ };
568
+ };
533
569
  /**
534
570
  * Repeat filter - repeats string specified number of times.
535
571
  *
@@ -537,9 +573,9 @@ const pad = (options) => {
537
573
  * @returns Filter function that returns repeated string
538
574
  */
539
575
  const rep = (options) => {
540
- const opt = options?.[0] ?? optionsRequired('rep');
576
+ const opt = options?.[0] ?? optionsRequired('repeat');
541
577
  if (!validateNumberString(opt)) {
542
- optionMustBeNumber('rep');
578
+ optionMustBeNumber('repeat');
543
579
  }
544
580
  return (value) => {
545
581
  return String(value).repeat(Number(opt));
@@ -847,6 +883,18 @@ const defaults = (options, literals) => {
847
883
  return value;
848
884
  };
849
885
  };
886
+ /**
887
+ * coalesce filter - returns the default only for null / undefined (`defaults` also replaces 0, false and "").
888
+ * Requirement B12: the nullish counterpart of `defaults`, read like SQL COALESCE.
889
+ *
890
+ * @param options - Array with default value as first element
891
+ * @returns Filter function that returns value or default
892
+ */
893
+ const coalesce = (options, literals) => {
894
+ const opt = options?.[0] ?? optionsRequired('coalesce');
895
+ const fallback = literals !== undefined && literals.length > 0 ? literals[0] : opt;
896
+ return (value) => value ?? fallback;
897
+ };
850
898
  /**
851
899
  * Boolean filter - converts value to boolean.
852
900
  *
@@ -899,24 +947,25 @@ const builtinFilters = {
899
947
  "le": le,
900
948
  "gt": gt,
901
949
  "ge": ge,
902
- "inc": inc,
903
- "dec": dec,
950
+ "add": inc,
951
+ "sub": dec,
904
952
  "mul": mul,
905
953
  "div": div,
906
954
  "mod": mod,
907
955
  "abs": abs,
908
956
  "clamp": clamp,
909
- "fix": fix,
957
+ "toFixed": fix,
910
958
  "locale": locale,
911
- "uc": uc,
912
- "lc": lc,
913
- "cap": cap,
959
+ "upper": uc,
960
+ "lower": lc,
961
+ "capitalize": cap,
914
962
  "trim": trim,
915
963
  "slice": slice,
916
964
  "substr": substr,
917
- "pad": pad,
918
- "rep": rep,
919
- "rev": rev,
965
+ "padStart": pad,
966
+ "padEnd": padEnd,
967
+ "repeat": rep,
968
+ "reverse": rev,
920
969
  "truncate": truncate,
921
970
  "join": join,
922
971
  "int": int,
@@ -934,10 +983,11 @@ const builtinFilters = {
934
983
  "falsy": falsy,
935
984
  "truthy": truthy,
936
985
  "defaults": defaults,
986
+ "coalesce": coalesce,
937
987
  "boolean": boolean,
938
988
  "number": number,
939
989
  "string": string,
940
- "null": _null,
990
+ "nullIfEmpty": _null,
941
991
  };
942
992
  const outputBuiltinFilters = builtinFilters;
943
993
 
@@ -963,26 +1013,27 @@ const builtinFilterMeta = {
963
1013
  gt: { description: "より大きいか", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
964
1014
  ge: { description: "以上か", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
965
1015
  // 算術
966
- inc: { description: "加算", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
967
- dec: { description: "減算", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
1016
+ add: { description: "加算", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
1017
+ sub: { description: "減算", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
968
1018
  mul: { description: "乗算", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
969
1019
  div: { description: "除算", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
970
1020
  mod: { description: "剰余", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
971
1021
  abs: { description: "絶対値", hasArgs: false, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 0 },
972
1022
  clamp: { description: "範囲内に丸める (min,max)", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 2, maxArgs: 2, argTypes: ["number", "number"] },
973
1023
  // 数値フォーマット
974
- fix: { description: "固定小数点表記", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
1024
+ toFixed: { description: "固定小数点表記", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
975
1025
  locale: { description: "ロケール形式で数値フォーマット", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
976
1026
  // 文字列
977
- uc: { description: "大文字に変換", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
978
- lc: { description: "小文字に変換", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
979
- cap: { description: "先頭文字を大文字に", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1027
+ upper: { description: "大文字に変換", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1028
+ lower: { description: "小文字に変換", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1029
+ capitalize: { description: "先頭文字を大文字に", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
980
1030
  trim: { description: "前後の空白を削除", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
981
1031
  slice: { description: "部分文字列 (start[,end])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
982
1032
  substr: { description: "部分文字列 (pos,len)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
983
- pad: { description: "パディング (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
984
- rep: { description: "繰り返し (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
985
- rev: { description: "文字順を反転", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1033
+ padStart: { description: "先頭を埋める (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1034
+ padEnd: { description: "末尾を埋める (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1035
+ repeat: { description: "繰り返し (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
1036
+ reverse: { description: "文字順を反転", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
986
1037
  truncate: { description: "切り詰めて省略記号 (length[,suffix])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
987
1038
  join: { description: "配列を連結 ([separator])", hasArgs: true, resultType: "string", acceptTypes: ["array"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
988
1039
  // 数値パース・丸め
@@ -1005,10 +1056,11 @@ const builtinFilterMeta = {
1005
1056
  falsy: { description: "偽値か判定", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1006
1057
  truthy: { description: "真値か判定", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1007
1058
  defaults: { description: "偽値の場合デフォルト値", hasArgs: true, resultType: "passthrough", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
1059
+ coalesce: { description: "null / undefined の場合デフォルト値", hasArgs: true, resultType: "passthrough", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
1008
1060
  boolean: { description: "ブール値に変換", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1009
1061
  number: { description: "数値に変換", hasArgs: false, resultType: "number", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1010
1062
  string: { description: "文字列に変換", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1011
- null: { description: "空文字列をnullに変換", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1063
+ nullIfEmpty: { description: "空文字列をnullに変換", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1012
1064
  };
1013
1065
 
1014
1066
  const STRUCTURAL_BINDING_TYPE_SET = new Set([
@@ -1075,7 +1127,8 @@ for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
1075
1127
  Object.freeze(tmpIndexByIndexName);
1076
1128
  const STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
1077
1129
  const STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
1078
- const STATE_UPDATED_CALLBACK_NAME = "$updatedCallback";
1130
+ /** 旧名 `$updatedCallback` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
1131
+ const STATE_UPDATED_CALLBACK_NAME = "$renderedCallback";
1079
1132
  const STATE_ERROR_CALLBACK_NAME = "$errorCallback";
1080
1133
  const WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
1081
1134
  const STATE_BINDABLES_NAME = "$bindables";
@@ -1084,7 +1137,8 @@ const STATE_COMMAND_TOKENS_NAME = "$commandTokens";
1084
1137
  const STATE_COMMAND_NAMESPACE_NAME = "$command";
1085
1138
  const STATE_EVENT_TOKENS_NAME = "$eventTokens";
1086
1139
  const STATE_ON_NAME = "$on";
1087
- const STATE_STREAMS_NAME = "$streams";
1140
+ /** 旧名 `$streams` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
1141
+ const STATE_STREAMS_NAME = "$stream";
1088
1142
  const STATE_WATCH_NAME = "$watch";
1089
1143
  const STATE_SCAN_NAME = "$scan";
1090
1144
  const STATE_RECURSION_NAME = "$recursion";
@@ -1150,6 +1204,7 @@ function getWcsManifest() {
1150
1204
  // 実装(Record のキー)から自動導出。手リストを持たない=ドリフトの構造的排除。
1151
1205
  filters: Object.keys(outputBuiltinFilters),
1152
1206
  filterMeta: builtinFilterMeta,
1207
+ filterAliases: builtinFilterAliases,
1153
1208
  reservedLifecycle: [
1154
1209
  STATE_CONNECTED_CALLBACK_NAME,
1155
1210
  STATE_DISCONNECTED_CALLBACK_NAME,
@@ -1175,4 +1230,4 @@ function getWcsManifest() {
1175
1230
  };
1176
1231
  }
1177
1232
 
1178
- export { STRUCTURAL_BINDING_TYPE_SET, WCS_MANIFEST_VERSION, builtinFilterMeta, getWcsManifest };
1233
+ export { STRUCTURAL_BINDING_TYPE_SET, WCS_MANIFEST_VERSION, builtinFilterAliases, builtinFilterMeta, getWcsManifest };
@@ -0,0 +1,2 @@
1
+ import{a as e,o as t}from"./pathDiagnostics.js";import{r as o}from"./errorGuidance.js";const r={$updatedCallback:t,$streams:e},a=new WeakSet;function n(e,t){let o=e;for(;null!==o&&!Object.prototype.hasOwnProperty.call(o,t);)o=Object.getPrototypeOf(o);return o}function s(e){if(!a.has(e)){for(const t of Object.keys(r)){if(!(t in e))continue;const a=r[t];a in e&&o(`[wcs/declaration-alias] The state declares both "${t}" and "${a}". "${t}" is the old name of "${a}" (it works through 3.x) — keep "${a}".`);const s=n(e,t),c=Object.getOwnPropertyDescriptor(s,t);Object.defineProperty(e,a,{...c,configurable:!0}),s===e&&delete e[t]}a.add(e)}}export{s as n};
2
+ //# sourceMappingURL=declarationAliases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declarationAliases.js","sources":["../../../../../src/declarationAliases.ts"],"sourcesContent":["/**\n * declarationAliases.ts — 宣言キーの旧名 → 正式名(要件 B12・docs/state-3x-naming.ja.md V13 / V14)。\n *\n * `$updatedCallback` → `$renderedCallback`、`$streams` → `$stream`。旧名は 3.x の間は受け、4.0 で外す(D4)。\n * state オブジェクトがランタイムに入る入口(State の `_state` と `_loadStateFromSource`、マウント記録)で\n * 1 回だけ正規化し、以降のすべての読み手は正式名だけを見る。両方の綴りを宣言したらどちらが効くのか\n * 書き手に見えないので、名指しで落とす(D38)。\n */\nimport { STATE_STREAMS_NAME, STATE_UPDATED_CALLBACK_NAME } from \"./define\";\nimport { raiseError } from \"./raiseError\";\n\nexport const DECLARATION_ALIASES: Readonly<Record<string, string>> = {\n $updatedCallback: STATE_UPDATED_CALLBACK_NAME,\n $streams: STATE_STREAMS_NAME,\n};\n\n/** 正規化済みの state オブジェクト(同じオブジェクトの再セット・ボリュームの 2 つの入口で二重に処理しない) */\nconst normalizedStates = new WeakSet<object>();\n\nfunction findOwner(value: object, key: string): object {\n let owner: object | null = value;\n while (owner !== null && !Object.prototype.hasOwnProperty.call(owner, key)) {\n owner = Object.getPrototypeOf(owner);\n }\n return owner!;\n}\n\n/**\n * 旧名で宣言されたキーを正式名へ写す。自前のプロパティなら旧名は消す。class の state でメソッドが\n * プロトタイプにある形(`$updatedCallback() {}`)は、インスタンスに正式名の定義を足すだけにする。\n */\nexport function normalizeDeclarationAliases(state: object): void {\n if (normalizedStates.has(state)) {\n return;\n }\n for (const alias of Object.keys(DECLARATION_ALIASES)) {\n if (!(alias in state)) {\n continue;\n }\n const canonical = DECLARATION_ALIASES[alias];\n if (canonical in state) {\n raiseError(\n `[wcs/declaration-alias] The state declares both \"${alias}\" and \"${canonical}\". ` +\n `\"${alias}\" is the old name of \"${canonical}\" (it works through 3.x) — keep \"${canonical}\".`,\n );\n }\n const owner = findOwner(state, alias);\n const descriptor = Object.getOwnPropertyDescriptor(owner, alias)!;\n Object.defineProperty(state, canonical, { ...descriptor, configurable: true });\n if (owner === state) {\n delete (state as Record<string, unknown>)[alias];\n }\n }\n normalizedStates.add(state);\n}\n"],"names":["DECLARATION_ALIASES","$updatedCallback","STATE_UPDATED_CALLBACK_NAME","$streams","STATE_STREAMS_NAME","normalizedStates","WeakSet","findOwner","value","key","owner","Object","prototype","hasOwnProperty","call","getPrototypeOf","normalizeDeclarationAliases","state","has","alias","keys","canonical","raiseError","descriptor","getOwnPropertyDescriptor","defineProperty","configurable","add"],"mappings":"uFAWO,MAAMA,EAAwD,CACnEC,iBAAkBC,EAClBC,SAAUC,GAINC,EAAmB,IAAIC,QAE7B,SAASC,EAAUC,EAAeC,GAChC,IAAIC,EAAuBF,EAC3B,KAAiB,OAAVE,IAAmBC,OAAOC,UAAUC,eAAeC,KAAKJ,EAAOD,IACpEC,EAAQC,OAAOI,eAAeL,GAEhC,OAAOA,CACT,CAMM,SAAUM,EAA4BC,GAC1C,IAAIZ,EAAiBa,IAAID,GAAzB,CAGA,IAAK,MAAME,KAASR,OAAOS,KAAKpB,GAAsB,CACpD,KAAMmB,KAASF,GACb,SAEF,MAAMI,EAAYrB,EAAoBmB,GAClCE,KAAaJ,GACfK,EACE,oDAAoDH,WAAeE,QAC/DF,0BAA8BE,qCAA6CA,OAGnF,MAAMX,EAAQH,EAAUU,EAAOE,GACzBI,EAAaZ,OAAOa,yBAAyBd,EAAOS,GAC1DR,OAAOc,eAAeR,EAAOI,EAAW,IAAKE,EAAYG,cAAc,IACnEhB,IAAUO,UACJA,EAAkCE,EAE9C,CACAd,EAAiBsB,IAAIV,EAnBrB,CAoBF"}
@@ -1,2 +1,2 @@
1
- import{r as e,L as t,d as n}from"./errorGuidance.js";const a=Symbol.for("wcstack.ssr.snapshotBuilder");function o(){const e=document.documentElement;return!!e&&"orchestrated"===e.getAttribute("data-wcs-server")}function r(){const e=document.documentElement;return!!e&&e.hasAttribute("data-wcs-server")}const s={bindAttributeName:"data-wcs",commentTextPrefix:"wcs-text",commentForPrefix:"wcs-for",commentIfPrefix:"wcs-if",commentElseIfPrefix:"wcs-elseif",commentElsePrefix:"wcs-else",tagNames:{state:"wcs-state",ssr:"wcs-ssr"},locale:"en",debug:!1,enableMustache:!0,enableDirectionalInitialSync:!0,enablePropagationContext:!0,enableContractAnalyzer:!1,sameValueGuard:!0},i=s;function c(){return i}function l(e){e.tagNames&&Object.assign(s.tagNames,e.tagNames),"string"==typeof e.bindAttributeName&&(s.bindAttributeName=e.bindAttributeName),"string"==typeof e.commentTextPrefix&&(s.commentTextPrefix=e.commentTextPrefix),"string"==typeof e.commentForPrefix&&(s.commentForPrefix=e.commentForPrefix),"string"==typeof e.commentIfPrefix&&(s.commentIfPrefix=e.commentIfPrefix),"string"==typeof e.commentElseIfPrefix&&(s.commentElseIfPrefix=e.commentElseIfPrefix),"string"==typeof e.commentElsePrefix&&(s.commentElsePrefix=e.commentElsePrefix),"string"==typeof e.locale&&(s.locale=e.locale),"boolean"==typeof e.debug&&(s.debug=e.debug),"boolean"==typeof e.enableMustache&&(s.enableMustache=e.enableMustache),"boolean"==typeof e.enableDirectionalInitialSync&&(s.enableDirectionalInitialSync=e.enableDirectionalInitialSync),"boolean"==typeof e.enablePropagationContext&&(s.enablePropagationContext=e.enablePropagationContext),"boolean"==typeof e.enableContractAnalyzer&&(s.enableContractAnalyzer=e.enableContractAnalyzer),"boolean"==typeof e.sameValueGuard&&(s.sameValueGuard=e.sameValueGuard)}const f={not:()=>e=>!e},m={input:new Map,output:new Map},u={input:new Map,output:new Map},b=new Map;function g(e,t,n){const a=m[e],o=u[e];for(const e of Object.keys(t)){a.set(e,t[e]);const r=n?.[e];void 0===r?o.delete(e):o.set(e,r)}b.clear()}function p(a,o,r,s=o){const i=`${a}${JSON.stringify(s)}:${r}`,c=b.get(i);if(void 0!==c)return c;const l=m[r].get(a)??f[a];void 0===l&&e(`[wcs/filter-unknown] filter not found: ${a}.${n(a,function(e){return[...new Set([...Object.keys(f),...m[e].keys()])]}(r))}${t}`);const g=u[r].get(a);void 0!==g&&(o.length<g[0]||o.length>g[1])&&e(o.length<g[0]?`[wcs/filter-arity] filter "${a}" requires at least ${g[0]} argument(s) (${o.length} given).${t}`:`[wcs/filter-arity] filter "${a}" accepts at most ${g[1]} argument(s) (${o.length} given).${t}`);const p=l(o,s);return b.set(i,p),p}export{a as S,o as a,g as b,i as c,c as g,r as i,p as r,l as s};
1
+ import{r as e,L as t,d as n}from"./errorGuidance.js";const o=Symbol.for("wcstack.ssr.snapshotBuilder");function a(){const e=document.documentElement;return!!e&&"orchestrated"===e.getAttribute("data-wcs-server")}function r(){const e=document.documentElement;return!!e&&e.hasAttribute("data-wcs-server")}const s={bindAttributeName:"data-wcs",commentTextPrefix:"wcs-text",commentForPrefix:"wcs-for",commentIfPrefix:"wcs-if",commentElseIfPrefix:"wcs-elseif",commentElsePrefix:"wcs-else",tagNames:{state:"wcs-state",ssr:"wcs-ssr"},locale:"en",debug:!1,enableMustache:!0,enableDirectionalInitialSync:!0,enablePropagationContext:!0,enableContractAnalyzer:!1,sameValueGuard:!0},i=s;function c(){return i}function l(e){e.tagNames&&Object.assign(s.tagNames,e.tagNames),"string"==typeof e.bindAttributeName&&(s.bindAttributeName=e.bindAttributeName),"string"==typeof e.commentTextPrefix&&(s.commentTextPrefix=e.commentTextPrefix),"string"==typeof e.commentForPrefix&&(s.commentForPrefix=e.commentForPrefix),"string"==typeof e.commentIfPrefix&&(s.commentIfPrefix=e.commentIfPrefix),"string"==typeof e.commentElseIfPrefix&&(s.commentElseIfPrefix=e.commentElseIfPrefix),"string"==typeof e.commentElsePrefix&&(s.commentElsePrefix=e.commentElsePrefix),"string"==typeof e.locale&&(s.locale=e.locale),"boolean"==typeof e.debug&&(s.debug=e.debug),"boolean"==typeof e.enableMustache&&(s.enableMustache=e.enableMustache),"boolean"==typeof e.enableDirectionalInitialSync&&(s.enableDirectionalInitialSync=e.enableDirectionalInitialSync),"boolean"==typeof e.enablePropagationContext&&(s.enablePropagationContext=e.enablePropagationContext),"boolean"==typeof e.enableContractAnalyzer&&(s.enableContractAnalyzer=e.enableContractAnalyzer),"boolean"==typeof e.sameValueGuard&&(s.sameValueGuard=e.sameValueGuard)}const f={not:()=>e=>!e},m={input:new Map,output:new Map},u={input:new Map,output:new Map},b={input:new Map,output:new Map},g=new Map;function p(e,t,n,o){const a=m[e],r=u[e];for(const e of Object.keys(t)){a.set(e,t[e]);const o=n?.[e];void 0===o?r.delete(e):r.set(e,o)}for(const t of Object.keys(o??{}))b[e].set(t,o[t]);g.clear()}function x(o,a,r,s=a){const i=`${o}${JSON.stringify(s)}:${r}`,c=g.get(i);if(void 0!==c)return c;const l=b[r].get(o)??o,p=m[r].get(l)??f[l];void 0===p&&e(`[wcs/filter-unknown] filter not found: ${o}.${n(o,function(e){return[...new Set([...Object.keys(f),...m[e].keys()])]}(r))}${t}`);const x=u[r].get(l);void 0!==x&&(a.length<x[0]||a.length>x[1])&&e(a.length<x[0]?`[wcs/filter-arity] filter "${o}" requires at least ${x[0]} argument(s) (${a.length} given).${t}`:`[wcs/filter-arity] filter "${o}" accepts at most ${x[1]} argument(s) (${a.length} given).${t}`);const d=p(a,s);return g.set(i,d),d}export{o as S,a,p as b,i as c,c as g,r as i,x as r,l as s};
2
2
  //# sourceMappingURL=filterRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filterRegistry.js","sources":["../../../../../src/protocol/ssrSnapshot.ts","../../../../../src/config.ts","../../../../../src/core/filterRegistry.ts"],"sourcesContent":["// ===========================================================================\n// AUTO-GENERATED FILE - DO NOT EDIT.\n// Generated from /protocol/ssr-snapshot.ts by scripts/sync-protocol-types.mjs.\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\n// ===========================================================================\n\n// ssr-snapshot protocol — how the SSR renderer asks whoever owns reactive\n// state to build hydration snapshots (<wcs-ssr>) as a final pass, after every\n// DOM inserter (router route content, late custom elements) has settled.\n//\n// Without this, the snapshot is built inside <wcs-state>'s connectedCallback\n// and races DOM inserted by other packages: whether a route's structural\n// templates make it into the snapshot depends on document order and state's\n// load mechanism (docs/ssr-router-design.md §5).\n//\n// The provider (@wcstack/state) installs itself on a well-known global symbol\n// at bootstrap. The renderer (@wcstack/server) looks the builder up after\n// running bootstraps: if present it announces orchestration by setting\n// `data-wcs-server=\"orchestrated\"` on the document element BEFORE parsing, and\n// calls build() right before serialization. The provider keeps its inline\n// per-element fallback whenever the attribute value is anything else, so:\n// - old renderer + new provider -> inline build, yesterday's behavior\n// - new renderer + old provider -> no builder found, attribute stays \"\",\n// the old provider builds inline as before\n// - new renderer + new provider -> orchestrated: snapshots are built last\n// and therefore always see settled DOM\n//\n// The symbol (rather than a package import) also pins the builder to the state\n// copy that actually runs on the page — its module-scoped fragment registries\n// are the ones the snapshot must read.\n//\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/ssr-snapshot.ts), then\n// run `node scripts/sync-protocol-types.mjs` to regenerate the per-package\n// copies (packages/<pkg>/src/protocol/ssrSnapshot.ts). Those copies are\n// generated — do not edit them.\n\n/**\n * Global key the snapshot builder installs itself under. `Symbol.for` so\n * independently loaded copies of this file (state's and server's) still agree.\n */\nexport const SSR_SNAPSHOT_BUILDER_KEY = Symbol.for(\"wcstack.ssr.snapshotBuilder\");\n\n/**\n * `data-wcs-server` attribute value announcing that the renderer will call the\n * builder as a final pass. Providers must skip their inline per-element build\n * when they see this value, and keep it for any other value (including \"\").\n */\nexport const SSR_ORCHESTRATED_VALUE = \"orchestrated\";\n\nexport interface IWcsSsrSnapshotBuilder {\n readonly protocol: \"wcs-ssr-snapshot\";\n /** Integer protocol version. All versions >= 1 are participant-compatible. */\n readonly version: number;\n /**\n * Build a hydration snapshot for every enable-ssr state element in `root`\n * that does not already have one. Called once per render, after all\n * waiting-protocol elements have settled and bindings are ready. Must be\n * idempotent with respect to already-snapshotted elements.\n */\n build(root: Document): void;\n}\n\n/**\n * The installed builder, or null when there is none or it speaks a shape this\n * reader does not.\n */\nexport function getSsrSnapshotBuilder(): IWcsSsrSnapshotBuilder | null {\n const candidate = (globalThis as Record<symbol, unknown>)[\n SSR_SNAPSHOT_BUILDER_KEY\n ] as IWcsSsrSnapshotBuilder | undefined;\n if (candidate === undefined || candidate === null) return null;\n if (candidate.protocol !== \"wcs-ssr-snapshot\") return null;\n if (typeof candidate.version !== \"number\" || candidate.version < 1) return null;\n if (typeof candidate.build !== \"function\") return null;\n return candidate;\n}\n","import { IConfig, IWritableConfig } from \"./types.js\";\nimport { SSR_ORCHESTRATED_VALUE } from \"./protocol/ssrSnapshot.js\";\n\n/**\n * サーバー主導スナップショット(orchestrated)の判定\n * (docs/ssr-router-design.md §5)。renderToString が snapshot builder を\n * 見つけたときだけ `data-wcs-server=\"orchestrated\"` を宣言する — 値が他の\n * もの(旧 server の \"\" を含む)なら inline 生成が従来どおり働く。\n * inSsr と同じ理由でキャッシュしない。\n */\nexport function isOrchestratedSsr(): boolean {\n const html = document.documentElement;\n return html ? html.getAttribute('data-wcs-server') === SSR_ORCHESTRATED_VALUE : false;\n}\n\nexport function inSsr(): boolean {\n // キャッシュしない: SSR モードはプロセスの属性ではなく「現在の document」の\n // 属性。@wcstack/server はグローバル document を差し替えてサーバーレンダリング\n // した後、同一プロセスでクライアント側ハイドレーションが走る(SSR→hydrate の\n // e2e が該当)。サーバーフェーズの判定をキャッシュするとクライアントフェーズが\n // SSR モード扱いになり、hydrateBindings の代わりに buildBindings が選ばれて\n // connectedCallbackPromise が永久に未解決になる。\n const html = document.documentElement;\n return html ? html.hasAttribute('data-wcs-server') : false;\n}\n\ninterface IInternalConfig {\n bindAttributeName: string;\n commentTextPrefix: string;\n commentForPrefix: string;\n commentIfPrefix: string;\n commentElseIfPrefix: string;\n commentElsePrefix: string;\n tagNames: {\n state: string;\n ssr: string;\n };\n locale: string;\n debug: boolean;\n enableMustache: boolean;\n enableDirectionalInitialSync: boolean;\n enablePropagationContext: boolean;\n enableContractAnalyzer: boolean;\n sameValueGuard: boolean;\n}\n\nconst _config: IInternalConfig = {\n bindAttributeName: 'data-wcs',\n commentTextPrefix: 'wcs-text',\n commentForPrefix: 'wcs-for',\n commentIfPrefix: 'wcs-if',\n commentElseIfPrefix: 'wcs-elseif',\n commentElsePrefix: 'wcs-else',\n tagNames: {\n state: 'wcs-state',\n ssr: 'wcs-ssr',\n },\n locale: 'en',\n debug: false,\n enableMustache: true,\n enableDirectionalInitialSync: true,\n enablePropagationContext: true,\n // Phase 5b の dev-time contract analyzer は意図的に explicit opt-in(既定 off)。\n // wcstack は buildless / zero-config で NODE_ENV 相当の確実な dev/prod 判定が無く、\n // hostname や minification の heuristic で auto-ON すると誤検出で prod にコストを\n // 乗せうるため、dev 既定 ON は採らない。利用側が setConfig で明示有効化する\n // (docs/architecture-hardening/10-defaulting-rollout-status.md §C)。\n enableContractAnalyzer: false,\n sameValueGuard: true,\n};\n\n// backward compatible export (read-only usage)\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n return config;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n if (typeof partialConfig.bindAttributeName === \"string\") {\n _config.bindAttributeName = partialConfig.bindAttributeName;\n }\n if (typeof partialConfig.commentTextPrefix === \"string\") {\n _config.commentTextPrefix = partialConfig.commentTextPrefix;\n }\n if (typeof partialConfig.commentForPrefix === \"string\") {\n _config.commentForPrefix = partialConfig.commentForPrefix;\n }\n if (typeof partialConfig.commentIfPrefix === \"string\") {\n _config.commentIfPrefix = partialConfig.commentIfPrefix;\n }\n if (typeof partialConfig.commentElseIfPrefix === \"string\") {\n _config.commentElseIfPrefix = partialConfig.commentElseIfPrefix;\n }\n if (typeof partialConfig.commentElsePrefix === \"string\") {\n _config.commentElsePrefix = partialConfig.commentElsePrefix;\n }\n if (typeof partialConfig.locale === \"string\") {\n _config.locale = partialConfig.locale;\n }\n if (typeof partialConfig.debug === \"boolean\") {\n _config.debug = partialConfig.debug;\n }\n if (typeof partialConfig.enableMustache === \"boolean\") {\n _config.enableMustache = partialConfig.enableMustache;\n }\n if (typeof partialConfig.enableDirectionalInitialSync === \"boolean\") {\n _config.enableDirectionalInitialSync = partialConfig.enableDirectionalInitialSync;\n }\n if (typeof partialConfig.enablePropagationContext === \"boolean\") {\n _config.enablePropagationContext = partialConfig.enablePropagationContext;\n }\n if (typeof partialConfig.enableContractAnalyzer === \"boolean\") {\n _config.enableContractAnalyzer = partialConfig.enableContractAnalyzer;\n }\n if (typeof partialConfig.sameValueGuard === \"boolean\") {\n _config.sameValueGuard = partialConfig.sameValueGuard;\n }\n}\n","/**\n * core/filterRegistry.ts — フィルタ実関数の登録簿(設計案 §4、要件 D16)。\n *\n * 文法(`path|filter(args)` の解析)は core に残り、**実関数は登録簿から束縛計画の段で引く**。\n * 解析の段は名前と引数しか作らない(`bindTextParser/parseFilters.ts`)ので、パーサだけを使う\n * tooling(`@wcstack/state/parser`)はフィルタの実装を 1 バイトも引き込まない。\n *\n * 書式フィルタ群(`uc` / `date` / `round` …)は `features/formats` が install で登録する。\n * core が自前で持つのは、エンジン自身が差し込む `not` だけ(`if` / `else` の反転 —\n * structural/notFilter.ts)。未知のフィルタは束縛計画の段で名指しで落ちる(従来は解析時)。\n */\nimport { didYouMean, LINT_HINT } from \"../errorGuidance\";\nimport type { FilterFn, FilterIOType, FilterWithOptions } from \"../filters/types\";\nimport { raiseError } from \"../raiseError\";\n\n/** `options` を受け取って実関数を返す工場(`filters/types.ts` の `FilterWithOptions` の要素) */\nexport type FilterFactory = (options?: string[], literals?: readonly unknown[]) => FilterFn;\n\n/**\n * エンジン自身が差し込むフィルタ。`if` / `else` の分岐は `not` を付けた束縛として組み立てられる\n * ので、`features/formats` の無いページでも必ず要る。\n */\nconst CORE_FILTERS: Record<string, FilterFactory> = {\n not: () => (value: unknown): boolean => !value,\n};\n\nconst registries: Record<FilterIOType, Map<string, FilterFactory>> = {\n input: new Map<string, FilterFactory>(),\n output: new Map<string, FilterFactory>(),\n};\n\n/** 受け付ける引数の個数 [最少, 最多]。登録した機能が渡したものだけ(要件 B3) */\nconst aritiesByIOType: Record<FilterIOType, Map<string, readonly [number, number]>> = {\n input: new Map<string, readonly [number, number]>(),\n output: new Map<string, readonly [number, number]>(),\n};\n\n/** 名前 + 引数 + 入出力ごとに解決済みの実関数(解決は 1 回だけ) */\nconst resolvedByKey = new Map<string, FilterFn>();\n\n/** 機能の install が呼ぶ(冪等 — 同じ名前は置き換え)。hot path には触れない */\nexport function registerFilters(\n filterIOType: FilterIOType,\n filters: FilterWithOptions,\n arity?: Readonly<Record<string, readonly [number, number]>>,\n): void {\n const registry = registries[filterIOType];\n const arities = aritiesByIOType[filterIOType];\n for (const name of Object.keys(filters)) {\n registry.set(name, filters[name] as FilterFactory);\n const bounds = arity?.[name];\n if (typeof bounds === \"undefined\") {\n arities.delete(name);\n } else {\n arities.set(name, bounds);\n }\n }\n // 登録が変われば解決済みの答えも変わりうる(同じページで 2 回 install することは無いが、\n // テストと tooling は登録簿を入れ替える)\n resolvedByKey.clear();\n}\n\n/** 登録済みの名前(core のものを含む)。診断の did-you-mean が読む */\nexport function knownFilterNames(filterIOType: FilterIOType): string[] {\n return [...new Set([...Object.keys(CORE_FILTERS), ...registries[filterIOType].keys()])];\n}\n\n/** 解決済みの答えを捨てる(tooling: `@wcstack/state/parser` の clearParserCaches) */\nexport function clearFilterResolutionCache(): void {\n resolvedByKey.clear();\n}\n\n/**\n * 実関数を引く(束縛計画の段)。未知のフィルタはここで落ちる — 解析の段では落とさない。\n * 文言は lint の `wcs/filter-unknown` と同じ語彙・同じ did-you-mean 規準(三面同語彙)。\n */\nexport function resolveFilterFn(\n filterName: string,\n args: string[],\n filterIOType: FilterIOType,\n literals: readonly unknown[] = args,\n): FilterFn {\n // 引数は型付きの値の構造のまま鍵にする(`join('a,b')` と `join(a,b)` — 要件 B3、`eq(1)` と\n // `eq('1')` — 要件 B9 を取り違えない)\n const key = `${filterName}${JSON.stringify(literals)}:${filterIOType}`;\n const resolved = resolvedByKey.get(key);\n if (typeof resolved !== \"undefined\") {\n return resolved;\n }\n const factory = registries[filterIOType].get(filterName) ?? CORE_FILTERS[filterName];\n if (typeof factory === \"undefined\") {\n raiseError(`[wcs/filter-unknown] filter not found: ${filterName}.${didYouMean(filterName, knownFilterNames(filterIOType))}${LINT_HINT}`);\n }\n const bounds = aritiesByIOType[filterIOType].get(filterName);\n if (typeof bounds !== \"undefined\" && (args.length < bounds[0] || args.length > bounds[1])) {\n // lint の wcs/filter-arity と同じ語彙\n raiseError(args.length < bounds[0]\n ? `[wcs/filter-arity] filter \"${filterName}\" requires at least ${bounds[0]} argument(s) (${args.length} given).${LINT_HINT}`\n : `[wcs/filter-arity] filter \"${filterName}\" accepts at most ${bounds[1]} argument(s) (${args.length} given).${LINT_HINT}`);\n }\n const filterFn = factory(args, literals);\n resolvedByKey.set(key, filterFn);\n return filterFn;\n}\n"],"names":["SSR_SNAPSHOT_BUILDER_KEY","Symbol","for","isOrchestratedSsr","html","document","documentElement","getAttribute","inSsr","hasAttribute","_config","bindAttributeName","commentTextPrefix","commentForPrefix","commentIfPrefix","commentElseIfPrefix","commentElsePrefix","tagNames","state","ssr","locale","debug","enableMustache","enableDirectionalInitialSync","enablePropagationContext","enableContractAnalyzer","sameValueGuard","config","getConfig","setConfig","partialConfig","Object","assign","CORE_FILTERS","not","value","registries","input","Map","output","aritiesByIOType","resolvedByKey","registerFilters","filterIOType","filters","arity","registry","arities","name","keys","set","bounds","delete","clear","resolveFilterFn","filterName","args","literals","key","JSON","stringify","resolved","get","factory","raiseError","didYouMean","Set","knownFilterNames","LINT_HINT","length","filterFn"],"mappings":"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"}
1
+ {"version":3,"file":"filterRegistry.js","sources":["../../../../../src/protocol/ssrSnapshot.ts","../../../../../src/config.ts","../../../../../src/core/filterRegistry.ts"],"sourcesContent":["// ===========================================================================\n// AUTO-GENERATED FILE - DO NOT EDIT.\n// Generated from /protocol/ssr-snapshot.ts by scripts/sync-protocol-types.mjs.\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\n// ===========================================================================\n\n// ssr-snapshot protocol — how the SSR renderer asks whoever owns reactive\n// state to build hydration snapshots (<wcs-ssr>) as a final pass, after every\n// DOM inserter (router route content, late custom elements) has settled.\n//\n// Without this, the snapshot is built inside <wcs-state>'s connectedCallback\n// and races DOM inserted by other packages: whether a route's structural\n// templates make it into the snapshot depends on document order and state's\n// load mechanism (docs/ssr-router-design.md §5).\n//\n// The provider (@wcstack/state) installs itself on a well-known global symbol\n// at bootstrap. The renderer (@wcstack/server) looks the builder up after\n// running bootstraps: if present it announces orchestration by setting\n// `data-wcs-server=\"orchestrated\"` on the document element BEFORE parsing, and\n// calls build() right before serialization. The provider keeps its inline\n// per-element fallback whenever the attribute value is anything else, so:\n// - old renderer + new provider -> inline build, yesterday's behavior\n// - new renderer + old provider -> no builder found, attribute stays \"\",\n// the old provider builds inline as before\n// - new renderer + new provider -> orchestrated: snapshots are built last\n// and therefore always see settled DOM\n//\n// The symbol (rather than a package import) also pins the builder to the state\n// copy that actually runs on the page — its module-scoped fragment registries\n// are the ones the snapshot must read.\n//\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/ssr-snapshot.ts), then\n// run `node scripts/sync-protocol-types.mjs` to regenerate the per-package\n// copies (packages/<pkg>/src/protocol/ssrSnapshot.ts). Those copies are\n// generated — do not edit them.\n\n/**\n * Global key the snapshot builder installs itself under. `Symbol.for` so\n * independently loaded copies of this file (state's and server's) still agree.\n */\nexport const SSR_SNAPSHOT_BUILDER_KEY = Symbol.for(\"wcstack.ssr.snapshotBuilder\");\n\n/**\n * `data-wcs-server` attribute value announcing that the renderer will call the\n * builder as a final pass. Providers must skip their inline per-element build\n * when they see this value, and keep it for any other value (including \"\").\n */\nexport const SSR_ORCHESTRATED_VALUE = \"orchestrated\";\n\nexport interface IWcsSsrSnapshotBuilder {\n readonly protocol: \"wcs-ssr-snapshot\";\n /** Integer protocol version. All versions >= 1 are participant-compatible. */\n readonly version: number;\n /**\n * Build a hydration snapshot for every enable-ssr state element in `root`\n * that does not already have one. Called once per render, after all\n * waiting-protocol elements have settled and bindings are ready. Must be\n * idempotent with respect to already-snapshotted elements.\n */\n build(root: Document): void;\n}\n\n/**\n * The installed builder, or null when there is none or it speaks a shape this\n * reader does not.\n */\nexport function getSsrSnapshotBuilder(): IWcsSsrSnapshotBuilder | null {\n const candidate = (globalThis as Record<symbol, unknown>)[\n SSR_SNAPSHOT_BUILDER_KEY\n ] as IWcsSsrSnapshotBuilder | undefined;\n if (candidate === undefined || candidate === null) return null;\n if (candidate.protocol !== \"wcs-ssr-snapshot\") return null;\n if (typeof candidate.version !== \"number\" || candidate.version < 1) return null;\n if (typeof candidate.build !== \"function\") return null;\n return candidate;\n}\n","import { IConfig, IWritableConfig } from \"./types.js\";\nimport { SSR_ORCHESTRATED_VALUE } from \"./protocol/ssrSnapshot.js\";\n\n/**\n * サーバー主導スナップショット(orchestrated)の判定\n * (docs/ssr-router-design.md §5)。renderToString が snapshot builder を\n * 見つけたときだけ `data-wcs-server=\"orchestrated\"` を宣言する — 値が他の\n * もの(旧 server の \"\" を含む)なら inline 生成が従来どおり働く。\n * inSsr と同じ理由でキャッシュしない。\n */\nexport function isOrchestratedSsr(): boolean {\n const html = document.documentElement;\n return html ? html.getAttribute('data-wcs-server') === SSR_ORCHESTRATED_VALUE : false;\n}\n\nexport function inSsr(): boolean {\n // キャッシュしない: SSR モードはプロセスの属性ではなく「現在の document」の\n // 属性。@wcstack/server はグローバル document を差し替えてサーバーレンダリング\n // した後、同一プロセスでクライアント側ハイドレーションが走る(SSR→hydrate の\n // e2e が該当)。サーバーフェーズの判定をキャッシュするとクライアントフェーズが\n // SSR モード扱いになり、hydrateBindings の代わりに buildBindings が選ばれて\n // connectedCallbackPromise が永久に未解決になる。\n const html = document.documentElement;\n return html ? html.hasAttribute('data-wcs-server') : false;\n}\n\ninterface IInternalConfig {\n bindAttributeName: string;\n commentTextPrefix: string;\n commentForPrefix: string;\n commentIfPrefix: string;\n commentElseIfPrefix: string;\n commentElsePrefix: string;\n tagNames: {\n state: string;\n ssr: string;\n };\n locale: string;\n debug: boolean;\n enableMustache: boolean;\n enableDirectionalInitialSync: boolean;\n enablePropagationContext: boolean;\n enableContractAnalyzer: boolean;\n sameValueGuard: boolean;\n}\n\nconst _config: IInternalConfig = {\n bindAttributeName: 'data-wcs',\n commentTextPrefix: 'wcs-text',\n commentForPrefix: 'wcs-for',\n commentIfPrefix: 'wcs-if',\n commentElseIfPrefix: 'wcs-elseif',\n commentElsePrefix: 'wcs-else',\n tagNames: {\n state: 'wcs-state',\n ssr: 'wcs-ssr',\n },\n locale: 'en',\n debug: false,\n enableMustache: true,\n enableDirectionalInitialSync: true,\n enablePropagationContext: true,\n // Phase 5b の dev-time contract analyzer は意図的に explicit opt-in(既定 off)。\n // wcstack は buildless / zero-config で NODE_ENV 相当の確実な dev/prod 判定が無く、\n // hostname や minification の heuristic で auto-ON すると誤検出で prod にコストを\n // 乗せうるため、dev 既定 ON は採らない。利用側が setConfig で明示有効化する\n // (docs/architecture-hardening/10-defaulting-rollout-status.md §C)。\n enableContractAnalyzer: false,\n sameValueGuard: true,\n};\n\n// backward compatible export (read-only usage)\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n return config;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n if (typeof partialConfig.bindAttributeName === \"string\") {\n _config.bindAttributeName = partialConfig.bindAttributeName;\n }\n if (typeof partialConfig.commentTextPrefix === \"string\") {\n _config.commentTextPrefix = partialConfig.commentTextPrefix;\n }\n if (typeof partialConfig.commentForPrefix === \"string\") {\n _config.commentForPrefix = partialConfig.commentForPrefix;\n }\n if (typeof partialConfig.commentIfPrefix === \"string\") {\n _config.commentIfPrefix = partialConfig.commentIfPrefix;\n }\n if (typeof partialConfig.commentElseIfPrefix === \"string\") {\n _config.commentElseIfPrefix = partialConfig.commentElseIfPrefix;\n }\n if (typeof partialConfig.commentElsePrefix === \"string\") {\n _config.commentElsePrefix = partialConfig.commentElsePrefix;\n }\n if (typeof partialConfig.locale === \"string\") {\n _config.locale = partialConfig.locale;\n }\n if (typeof partialConfig.debug === \"boolean\") {\n _config.debug = partialConfig.debug;\n }\n if (typeof partialConfig.enableMustache === \"boolean\") {\n _config.enableMustache = partialConfig.enableMustache;\n }\n if (typeof partialConfig.enableDirectionalInitialSync === \"boolean\") {\n _config.enableDirectionalInitialSync = partialConfig.enableDirectionalInitialSync;\n }\n if (typeof partialConfig.enablePropagationContext === \"boolean\") {\n _config.enablePropagationContext = partialConfig.enablePropagationContext;\n }\n if (typeof partialConfig.enableContractAnalyzer === \"boolean\") {\n _config.enableContractAnalyzer = partialConfig.enableContractAnalyzer;\n }\n if (typeof partialConfig.sameValueGuard === \"boolean\") {\n _config.sameValueGuard = partialConfig.sameValueGuard;\n }\n}\n","/**\n * core/filterRegistry.ts — フィルタ実関数の登録簿(設計案 §4、要件 D16)。\n *\n * 文法(`path|filter(args)` の解析)は core に残り、**実関数は登録簿から束縛計画の段で引く**。\n * 解析の段は名前と引数しか作らない(`bindTextParser/parseFilters.ts`)ので、パーサだけを使う\n * tooling(`@wcstack/state/parser`)はフィルタの実装を 1 バイトも引き込まない。\n *\n * 書式フィルタ群(`uc` / `date` / `round` …)は `features/formats` が install で登録する。\n * core が自前で持つのは、エンジン自身が差し込む `not` だけ(`if` / `else` の反転 —\n * structural/notFilter.ts)。未知のフィルタは束縛計画の段で名指しで落ちる(従来は解析時)。\n */\nimport { didYouMean, LINT_HINT } from \"../errorGuidance\";\nimport type { FilterFn, FilterIOType, FilterWithOptions } from \"../filters/types\";\nimport { raiseError } from \"../raiseError\";\n\n/** `options` を受け取って実関数を返す工場(`filters/types.ts` の `FilterWithOptions` の要素) */\nexport type FilterFactory = (options?: string[], literals?: readonly unknown[]) => FilterFn;\n\n/**\n * エンジン自身が差し込むフィルタ。`if` / `else` の分岐は `not` を付けた束縛として組み立てられる\n * ので、`features/formats` の無いページでも必ず要る。\n */\nconst CORE_FILTERS: Record<string, FilterFactory> = {\n not: () => (value: unknown): boolean => !value,\n};\n\nconst registries: Record<FilterIOType, Map<string, FilterFactory>> = {\n input: new Map<string, FilterFactory>(),\n output: new Map<string, FilterFactory>(),\n};\n\n/** 受け付ける引数の個数 [最少, 最多]。登録した機能が渡したものだけ(要件 B3) */\nconst aritiesByIOType: Record<FilterIOType, Map<string, readonly [number, number]>> = {\n input: new Map<string, readonly [number, number]>(),\n output: new Map<string, readonly [number, number]>(),\n};\n\n/** 旧名 → 正式名(要件 B12)。解決・引数の個数は正式名で引く。登録した機能が渡したものだけ */\nconst aliasesByIOType: Record<FilterIOType, Map<string, string>> = {\n input: new Map<string, string>(),\n output: new Map<string, string>(),\n};\n\n/** 名前 + 引数 + 入出力ごとに解決済みの実関数(解決は 1 回だけ) */\nconst resolvedByKey = new Map<string, FilterFn>();\n\n/** 機能の install が呼ぶ(冪等 — 同じ名前は置き換え)。hot path には触れない */\nexport function registerFilters(\n filterIOType: FilterIOType,\n filters: FilterWithOptions,\n arity?: Readonly<Record<string, readonly [number, number]>>,\n aliases?: Readonly<Record<string, string>>,\n): void {\n const registry = registries[filterIOType];\n const arities = aritiesByIOType[filterIOType];\n for (const name of Object.keys(filters)) {\n registry.set(name, filters[name] as FilterFactory);\n const bounds = arity?.[name];\n if (typeof bounds === \"undefined\") {\n arities.delete(name);\n } else {\n arities.set(name, bounds);\n }\n }\n for (const alias of Object.keys(aliases ?? {})) {\n aliasesByIOType[filterIOType].set(alias, aliases![alias]);\n }\n // 登録が変われば解決済みの答えも変わりうる(同じページで 2 回 install することは無いが、\n // テストと tooling は登録簿を入れ替える)\n resolvedByKey.clear();\n}\n\n/** 登録済みの名前(core のものを含む)。診断の did-you-mean が読む */\nexport function knownFilterNames(filterIOType: FilterIOType): string[] {\n return [...new Set([...Object.keys(CORE_FILTERS), ...registries[filterIOType].keys()])];\n}\n\n/** 解決済みの答えを捨てる(tooling: `@wcstack/state/parser` の clearParserCaches) */\nexport function clearFilterResolutionCache(): void {\n resolvedByKey.clear();\n}\n\n/**\n * 実関数を引く(束縛計画の段)。未知のフィルタはここで落ちる — 解析の段では落とさない。\n * 文言は lint の `wcs/filter-unknown` と同じ語彙・同じ did-you-mean 規準(三面同語彙)。\n */\nexport function resolveFilterFn(\n filterName: string,\n args: string[],\n filterIOType: FilterIOType,\n literals: readonly unknown[] = args,\n): FilterFn {\n // 引数は型付きの値の構造のまま鍵にする(`join('a,b')` と `join(a,b)` — 要件 B3、`eq(1)` と\n // `eq('1')` — 要件 B9 を取り違えない)\n const key = `${filterName}${JSON.stringify(literals)}:${filterIOType}`;\n const resolved = resolvedByKey.get(key);\n if (typeof resolved !== \"undefined\") {\n return resolved;\n }\n // 旧名(`uc` / `fix` …)は正式名で引く — 3.x の間のエイリアス(要件 B12、4.0 で外す)\n const canonical = aliasesByIOType[filterIOType].get(filterName) ?? filterName;\n const factory = registries[filterIOType].get(canonical) ?? CORE_FILTERS[canonical];\n if (typeof factory === \"undefined\") {\n raiseError(`[wcs/filter-unknown] filter not found: ${filterName}.${didYouMean(filterName, knownFilterNames(filterIOType))}${LINT_HINT}`);\n }\n const bounds = aritiesByIOType[filterIOType].get(canonical);\n if (typeof bounds !== \"undefined\" && (args.length < bounds[0] || args.length > bounds[1])) {\n // lint の wcs/filter-arity と同じ語彙\n raiseError(args.length < bounds[0]\n ? `[wcs/filter-arity] filter \"${filterName}\" requires at least ${bounds[0]} argument(s) (${args.length} given).${LINT_HINT}`\n : `[wcs/filter-arity] filter \"${filterName}\" accepts at most ${bounds[1]} argument(s) (${args.length} given).${LINT_HINT}`);\n }\n const filterFn = factory(args, literals);\n resolvedByKey.set(key, filterFn);\n return filterFn;\n}\n"],"names":["SSR_SNAPSHOT_BUILDER_KEY","Symbol","for","isOrchestratedSsr","html","document","documentElement","getAttribute","inSsr","hasAttribute","_config","bindAttributeName","commentTextPrefix","commentForPrefix","commentIfPrefix","commentElseIfPrefix","commentElsePrefix","tagNames","state","ssr","locale","debug","enableMustache","enableDirectionalInitialSync","enablePropagationContext","enableContractAnalyzer","sameValueGuard","config","getConfig","setConfig","partialConfig","Object","assign","CORE_FILTERS","not","value","registries","input","Map","output","aritiesByIOType","aliasesByIOType","resolvedByKey","registerFilters","filterIOType","filters","arity","aliases","registry","arities","name","keys","set","bounds","delete","alias","clear","resolveFilterFn","filterName","args","literals","key","JSON","stringify","resolved","get","canonical","factory","raiseError","didYouMean","Set","knownFilterNames","LINT_HINT","length","filterFn"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{D as t,j as e,g as n,W as r,R as o}from"./pathDiagnostics.js";import{aS as s}from"./binder.js";import{r as a}from"./errorGuidance.js";function i(t,e){if(e.length>t.length)return!1;for(let n=0;n<e.length;n++)if(t[n]!==e[n])return!1;return!0}function c(t,e){for(const n of t)if(i(e,n.innerSegments))return n;return null}function l(e,n){const r=n.slice(e.innerSegments.length);return 0===r.length?e.outerPathInfo.path:e.outerPathInfo.path+t+r.join(t)}let u=0;const d=["$watch","$streams","$scan","$listKeys","$updatedCallback","$commandTokens","$eventTokens","$on","$recursion","$errorCallback"],f=new Set;function h(t){const e=d.filter(e=>void 0!==t.stateObject[e]);for(const n of t.getterKeys)-1!==n.indexOf(o)&&e.push(`"${n}"`);if(0===e.length)return;const n=`${t.component.tagName.toLowerCase()}|${t.stateProp}`;f.has(n)||(f.add(n),console.warn(`[@wcstack/state] [wcs/mount-dollar-declaration] <${n.split("|")[0]}>.${t.stateProp} declares ${e.join(", ")}, which mounted components do not run. Declare them on the root state instead (a volume <wcs-state mount="..."> can host $watch / $listKeys / $updatedCallback; $recursion and "**" getters expand against the root tree). See docs/state-mount-design.md §4-6.`))}function m(t,e){const n=t.stateObject[e];if("function"==typeof n)try{const r=t.component[t.stateProp],o=n.call(r);o instanceof Promise&&o.catch(n=>{console.error(`[@wcstack/state] mounted <${t.component.tagName.toLowerCase()}> ${e} failed.`,n)})}catch(n){console.error(`[@wcstack/state] mounted <${t.component.tagName.toLowerCase()}> ${e} failed.`,n)}}function p(n,r,o,s,i,c=new Set){0===o.length&&a(`Cannot build a mount record without host bindings for "${r}".`);const l=[],d=new Set;let f=null;for(const n of o){const r=n.propSegments.slice(1),o=r.join(t);d.has(o)&&a("Duplicate mapping rule for web component."),d.add(o);const s={innerSegments:r,outerPathInfo:n.statePathInfo,readonly:!0===n.propModifiers?.includes(e)};l.push(s),0===r.length&&(f=s)}l.sort((t,e)=>e.innerSegments.length-t.innerSegments.length);const h=++u,m=`#m${h}`,{getterKeys:p,setterKeys:g}=function(t){const e=new Set,n=new Set,r=Object.getOwnPropertyDescriptors(t);for(const[t,o]of Object.entries(r))"function"==typeof o.get&&e.add(t),"function"==typeof o.set&&n.add(t);return{getterKeys:e,setterKeys:n}}(i),y=new Set;for(const t of l)t.innerSegments.length>0&&y.add(t.innerSegments[0]);const w={};for(const t of Object.keys(i))t.startsWith("$")||p.has(t)||g.has(t)||"function"!=typeof i[t]&&(c.has(t)||y.has(t)||(w[t]=i[t]));return{id:h,marker:m,component:n,stateProp:r,parentStateElement:s,entries:l,rootEntry:f,delta:null===f?0:f.outerPathInfo.wildcardCount,markerBasePath:null===f?m:f.outerPathInfo.path+t+m,stateObject:i,getterKeys:p,setterKeys:g,injectedKeys:c,mappedKeys:y,privateSnapshot:w,accessorBySuffixByMarkerParent:new Map,indexShiftByLoopElementPath:new Map,addedGetterPaths:new Set,exports:new Map}}function g(e){const n=e.indexOf(t);return-1===n?e:e.slice(0,n)}function y(t,e){const n=c(t.entries,e);return null===n?null:l(n,e)}function w(e,n){return e.markerBasePath+t+n}function $(e,r,o,s){let a=e.accessorBySuffixByMarkerParent.get(r);void 0===a&&(a=new Map,e.accessorBySuffixByMarkerParent.set(r,a));const i=r+t+o;a.set(o,{accessorName:s,indexShift:n(i).wildcardCount-n(s).wildcardCount}),e.getterKeys.has(s)&&(e.parentStateElement.getterPaths?.add(i),e.addedGetterPaths.add(i))}function P(t,e){return!t.injectedKeys.has(e)&&!t.mappedKeys.has(e)&&("function"==typeof t.stateObject[e]&&!t.getterKeys.has(e)||Object.prototype.hasOwnProperty.call(t.stateObject,e)&&!t.getterKeys.has(e)&&!t.setterKeys.has(e))}function k(e,n){return`Path "${n}" in <${e.component.tagName.toLowerCase()}> does not resolve: it is not an own key of the component state and no mount entry covers it (mounted prefixes: ${e.entries.filter(t=>t.innerSegments.length>0).map(e=>e.innerSegments.join(t)).join(", ")}). Mount it from the host ("${e.stateProp}: path" / "${e.stateProp}.${g(n)}: path") or declare the key on the component state.`}const S=/^\$(\d+)$/;function v(e,n){const r=n[0];if("$"===r||"#"===r)return null;if(e.getterKeys.has(n)||e.setterKeys.has(n))return null;const o=g(n);if(e.getterKeys.has(o)||e.setterKeys.has(o)||P(e,o))return null;const s=function(t,e){return c(t.entries,e)}(e,n.split(t));return null!==s&&s.readonly?s:null}function b(n,r){const o=v(n,r);if(null!==o){const s=0===o.innerSegments.length?"":t+o.innerSegments.join(t);a(`[wcs/mount-readonly] <${n.component.tagName.toLowerCase()}> cannot write "${r}": it is mounted read-only ("${n.stateProp}${s}#${e}: ${o.outerPathInfo.path}"). Write it on the host, or drop #${e} from the mount.`)}return K(n,r)}function K(e,n){const o=n[0];if("$"===o||"#"===o)return n;if(e.getterKeys.has(n)||e.setterKeys.has(n))return function(e,n){const o=n.split(t);let s=-1;for(let t=o.length-1;t>=0;t--)if(o[t]===r){s=t;break}if(-1===s)return $(e,e.markerBasePath,n,n),w(e,n);if(P(e,o[0]))return $(e,e.markerBasePath,n,n),w(e,n);const i=o.slice(0,s+1),c=o.slice(s+1),l=y(e,i);null===l&&a(k(e,n)),0===c.length&&a(`Wildcard-terminal accessor "${n}" on mounted <${e.component.tagName.toLowerCase()}> is not supported: it translates to a marker-terminal path where the mount overlay would shadow the accessor. Declare the row value as a getter on the host tree, or back it with a component-own array (a private key makes the whole path private).`);const u=l+t+e.marker;return $(e,u,c.join(t),n),u+t+c.join(t)}(e,n);const s=g(n);if(e.getterKeys.has(s)||e.setterKeys.has(s))return w(e,n);if(P(e,s))return w(e,n);const i=y(e,n.split(t));if(null!==i)return i;a(k(e,n))}function j(o,s,a){let i;const c=S.exec(s.statePathName);if(null!==c){const t=function(t,e){if(void 0===e)return t.delta;const r=K(t,e);return n(r).wildcardCount-n(e).wildcardCount}(o,a);i=0===t?s.statePathName:`$${Number(c[1])+t}`}else if(i=K(o,s.statePathName),"for"===s.bindingType){const e=n(i).wildcardCount-s.statePathInfo.wildcardCount;o.indexShiftByLoopElementPath.set(i+t+r,e)}const l=s.propModifiers,u=null===c&&Array.isArray(l)&&!l.includes(e)&&null!==v(o,s.statePathName);return i!==s.statePathName||u?{...s,statePathName:i,statePathInfo:n(i),...u?{propModifiers:[...l,e]}:{}}:s}function x(e,n){const r=n.lastIndexOf("#");if(-1===r)return e.delta;const o=n.indexOf(t,r);if((-1===o?n.slice(r):n.slice(r,o))!==e.marker)return e.delta;const s=-1===o?n:n.slice(0,o),a=-1===o?"":n.slice(o+1),i=e.accessorBySuffixByMarkerParent.get(s)?.get(a);return void 0!==i?i.indexShift:e.delta}function M(t,e,r,o,s){if(void 0===o)return;const i=n(r).wildcardCount-n(e).wildcardCount;return 0===i?o:(s.length<i&&a(`Cannot resolve "${e}" from this mount: the mounted prefix has ${i} wildcard(s) but the host context provides only ${s.length} index(es).`),[...s.slice(0,i),...o])}const C=new WeakMap,O=new WeakMap;function N(t,e){return O.get(t)?.get(e)??null}const B=new WeakMap;const I=new FinalizationRegistry(function(t){const e=t.byMarker.get(t.marker);if(void 0!==e&&void 0===e.deref()&&t.byMarker.delete(t.marker),void 0!==t.getterPaths)for(const e of t.addedGetterPaths)t.getterPaths.delete(e)}),E=new WeakMap;function W(t){return E.get(t)??null}let L=!1;function D(t,e){L||(L=!0,s(t=>C.has(t)?t.host:null)),C.set(t,e),E.set(e,t);let n=B.get(e.parentStateElement);void 0===n&&(n=new Map,B.set(e.parentStateElement,n)),n.set(e.marker,new WeakRef(e));let r=O.get(e.component);void 0===r&&(r=new Map,O.set(e.component,r));const o=r.get(e.stateProp)!==e;r.set(e.stateProp,e),o&&I.register(e,{byMarker:n,marker:e.marker,getterPaths:e.parentStateElement.getterPaths,addedGetterPaths:e.addedGetterPaths}),e.parentStateElement.markHasMounts?.()}function G(t){return C.get(t)??null}function R(t,e){let n=t;for(;null!==n&&n!==e;){const t=C.get(n);if(void 0!==t)return t;n=n.parentNode}return C.get(e)??null}function T(t){const e=B.get(t);if(void 0===e)return[];const n=[];for(const t of e.values()){const e=t.deref();void 0!==e&&n.push(e)}return n}function z(t){const e=B.get(t);return void 0!==e&&e.size>0}function A(e,n){const r=n.lastIndexOf("#");if(-1===r)return null;const o=B.get(e);if(void 0===o)return null;const s=n.indexOf(t,r),a=-1===s?n.slice(r):n.slice(r,s),i=o.get(a);if(void 0===i)return null;const c=i.deref();return void 0===c?(o.delete(a),null):c}export{K as a,b,M as c,W as d,G as e,c as f,T as g,j as h,R as i,A as j,x as k,m as l,N as m,p as n,D as r,z as s,l as t,h as w};
1
+ import{D as t,j as e,g as n,W as r,a as o,o as s,R as a}from"./pathDiagnostics.js";import{aS as i}from"./binder.js";import{n as c}from"./declarationAliases.js";import{r as l}from"./errorGuidance.js";function u(t,e){if(e.length>t.length)return!1;for(let n=0;n<e.length;n++)if(t[n]!==e[n])return!1;return!0}function d(t,e){for(const n of t)if(u(e,n.innerSegments))return n;return null}function f(e,n){const r=n.slice(e.innerSegments.length);return 0===r.length?e.outerPathInfo.path:e.outerPathInfo.path+t+r.join(t)}let h=0;const m=["$watch",o,"$scan","$listKeys",s,"$commandTokens","$eventTokens","$on","$recursion","$errorCallback"],p=new Set;function g(t){const e=m.filter(e=>void 0!==t.stateObject[e]);for(const n of t.getterKeys)-1!==n.indexOf(a)&&e.push(`"${n}"`);if(0===e.length)return;const n=`${t.component.tagName.toLowerCase()}|${t.stateProp}`;p.has(n)||(p.add(n),console.warn(`[@wcstack/state] [wcs/mount-dollar-declaration] <${n.split("|")[0]}>.${t.stateProp} declares ${e.join(", ")}, which mounted components do not run. Declare them on the root state instead (a volume <wcs-state mount="..."> can host $watch / $listKeys / $updatedCallback; $recursion and "**" getters expand against the root tree). See docs/state-mount-design.md §4-6.`))}function y(t,e){const n=t.stateObject[e];if("function"==typeof n)try{const r=t.component[t.stateProp],o=n.call(r);o instanceof Promise&&o.catch(n=>{console.error(`[@wcstack/state] mounted <${t.component.tagName.toLowerCase()}> ${e} failed.`,n)})}catch(n){console.error(`[@wcstack/state] mounted <${t.component.tagName.toLowerCase()}> ${e} failed.`,n)}}function w(n,r,o,s,a,i=new Set){0===o.length&&l(`Cannot build a mount record without host bindings for "${r}".`),c(a);const u=[],d=new Set;let f=null;for(const n of o){const r=n.propSegments.slice(1),o=r.join(t);d.has(o)&&l("Duplicate mapping rule for web component."),d.add(o);const s={innerSegments:r,outerPathInfo:n.statePathInfo,readonly:!0===n.propModifiers?.includes(e)};u.push(s),0===r.length&&(f=s)}u.sort((t,e)=>e.innerSegments.length-t.innerSegments.length);const m=++h,p=`#m${m}`,{getterKeys:g,setterKeys:y}=function(t){const e=new Set,n=new Set,r=Object.getOwnPropertyDescriptors(t);for(const[t,o]of Object.entries(r))"function"==typeof o.get&&e.add(t),"function"==typeof o.set&&n.add(t);return{getterKeys:e,setterKeys:n}}(a),w=new Set;for(const t of u)t.innerSegments.length>0&&w.add(t.innerSegments[0]);const $={};for(const t of Object.keys(a))t.startsWith("$")||g.has(t)||y.has(t)||"function"!=typeof a[t]&&(i.has(t)||w.has(t)||($[t]=a[t]));return{id:m,marker:p,component:n,stateProp:r,parentStateElement:s,entries:u,rootEntry:f,delta:null===f?0:f.outerPathInfo.wildcardCount,markerBasePath:null===f?p:f.outerPathInfo.path+t+p,stateObject:a,getterKeys:g,setterKeys:y,injectedKeys:i,mappedKeys:w,privateSnapshot:$,accessorBySuffixByMarkerParent:new Map,indexShiftByLoopElementPath:new Map,addedGetterPaths:new Set,exports:new Map}}function $(e){const n=e.indexOf(t);return-1===n?e:e.slice(0,n)}function P(t,e){const n=d(t.entries,e);return null===n?null:f(n,e)}function k(e,n){return e.markerBasePath+t+n}function S(e,r,o,s){let a=e.accessorBySuffixByMarkerParent.get(r);void 0===a&&(a=new Map,e.accessorBySuffixByMarkerParent.set(r,a));const i=r+t+o;a.set(o,{accessorName:s,indexShift:n(i).wildcardCount-n(s).wildcardCount}),e.getterKeys.has(s)&&(e.parentStateElement.getterPaths?.add(i),e.addedGetterPaths.add(i))}function v(t,e){return!t.injectedKeys.has(e)&&!t.mappedKeys.has(e)&&("function"==typeof t.stateObject[e]&&!t.getterKeys.has(e)||Object.prototype.hasOwnProperty.call(t.stateObject,e)&&!t.getterKeys.has(e)&&!t.setterKeys.has(e))}function j(e,n){return`Path "${n}" in <${e.component.tagName.toLowerCase()}> does not resolve: it is not an own key of the component state and no mount entry covers it (mounted prefixes: ${e.entries.filter(t=>t.innerSegments.length>0).map(e=>e.innerSegments.join(t)).join(", ")}). Mount it from the host ("${e.stateProp}: path" / "${e.stateProp}.${$(n)}: path") or declare the key on the component state.`}const K=/^\$(\d+)$/;function b(e,n){const r=n[0];if("$"===r||"#"===r)return null;if(e.getterKeys.has(n)||e.setterKeys.has(n))return null;const o=$(n);if(e.getterKeys.has(o)||e.setterKeys.has(o)||v(e,o))return null;const s=function(t,e){return d(t.entries,e)}(e,n.split(t));return null!==s&&s.readonly?s:null}function x(n,r){const o=b(n,r);if(null!==o){const s=0===o.innerSegments.length?"":t+o.innerSegments.join(t);l(`[wcs/mount-readonly] <${n.component.tagName.toLowerCase()}> cannot write "${r}": it is mounted read-only ("${n.stateProp}${s}#${e}: ${o.outerPathInfo.path}"). Write it on the host, or drop #${e} from the mount.`)}return M(n,r)}function M(e,n){const o=n[0];if("$"===o||"#"===o)return n;if(e.getterKeys.has(n)||e.setterKeys.has(n))return function(e,n){const o=n.split(t);let s=-1;for(let t=o.length-1;t>=0;t--)if(o[t]===r){s=t;break}if(-1===s)return S(e,e.markerBasePath,n,n),k(e,n);if(v(e,o[0]))return S(e,e.markerBasePath,n,n),k(e,n);const a=o.slice(0,s+1),i=o.slice(s+1),c=P(e,a);null===c&&l(j(e,n)),0===i.length&&l(`Wildcard-terminal accessor "${n}" on mounted <${e.component.tagName.toLowerCase()}> is not supported: it translates to a marker-terminal path where the mount overlay would shadow the accessor. Declare the row value as a getter on the host tree, or back it with a component-own array (a private key makes the whole path private).`);const u=c+t+e.marker;return S(e,u,i.join(t),n),u+t+i.join(t)}(e,n);const s=$(n);if(e.getterKeys.has(s)||e.setterKeys.has(s))return k(e,n);if(v(e,s))return k(e,n);const a=P(e,n.split(t));if(null!==a)return a;l(j(e,n))}function C(o,s,a){let i;const c=K.exec(s.statePathName);if(null!==c){const t=function(t,e){if(void 0===e)return t.delta;const r=M(t,e);return n(r).wildcardCount-n(e).wildcardCount}(o,a);i=0===t?s.statePathName:`$${Number(c[1])+t}`}else if(i=M(o,s.statePathName),"for"===s.bindingType){const e=n(i).wildcardCount-s.statePathInfo.wildcardCount;o.indexShiftByLoopElementPath.set(i+t+r,e)}const l=s.propModifiers,u=null===c&&Array.isArray(l)&&!l.includes(e)&&null!==b(o,s.statePathName);return i!==s.statePathName||u?{...s,statePathName:i,statePathInfo:n(i),...u?{propModifiers:[...l,e]}:{}}:s}function O(e,n){const r=n.lastIndexOf("#");if(-1===r)return e.delta;const o=n.indexOf(t,r);if((-1===o?n.slice(r):n.slice(r,o))!==e.marker)return e.delta;const s=-1===o?n:n.slice(0,o),a=-1===o?"":n.slice(o+1),i=e.accessorBySuffixByMarkerParent.get(s)?.get(a);return void 0!==i?i.indexShift:e.delta}function N(t,e,r,o,s){if(void 0===o)return;const a=n(r).wildcardCount-n(e).wildcardCount;return 0===a?o:(s.length<a&&l(`Cannot resolve "${e}" from this mount: the mounted prefix has ${a} wildcard(s) but the host context provides only ${s.length} index(es).`),[...s.slice(0,a),...o])}const B=new WeakMap,I=new WeakMap;function E(t,e){return I.get(t)?.get(e)??null}const W=new WeakMap;const L=new FinalizationRegistry(function(t){const e=t.byMarker.get(t.marker);if(void 0!==e&&void 0===e.deref()&&t.byMarker.delete(t.marker),void 0!==t.getterPaths)for(const e of t.addedGetterPaths)t.getterPaths.delete(e)}),D=new WeakMap;function G(t){return D.get(t)??null}let A=!1;function R(t,e){A||(A=!0,i(t=>B.has(t)?t.host:null)),B.set(t,e),D.set(e,t);let n=W.get(e.parentStateElement);void 0===n&&(n=new Map,W.set(e.parentStateElement,n)),n.set(e.marker,new WeakRef(e));let r=I.get(e.component);void 0===r&&(r=new Map,I.set(e.component,r));const o=r.get(e.stateProp)!==e;r.set(e.stateProp,e),o&&L.register(e,{byMarker:n,marker:e.marker,getterPaths:e.parentStateElement.getterPaths,addedGetterPaths:e.addedGetterPaths}),e.parentStateElement.markHasMounts?.()}function T(t){return B.get(t)??null}function z(t,e){let n=t;for(;null!==n&&n!==e;){const t=B.get(n);if(void 0!==t)return t;n=n.parentNode}return B.get(e)??null}function F(t){const e=W.get(t);if(void 0===e)return[];const n=[];for(const t of e.values()){const e=t.deref();void 0!==e&&n.push(e)}return n}function H(t){const e=W.get(t);return void 0!==e&&e.size>0}function q(e,n){const r=n.lastIndexOf("#");if(-1===r)return null;const o=W.get(e);if(void 0===o)return null;const s=n.indexOf(t,r),a=-1===s?n.slice(r):n.slice(r,s),i=o.get(a);if(void 0===i)return null;const c=i.deref();return void 0===c?(o.delete(a),null):c}export{M as a,x as b,N as c,G as d,T as e,d as f,F as g,C as h,z as i,q as j,O as k,y as l,E as m,w as n,R as r,H as s,f as t,g as w};
2
2
  //# sourceMappingURL=mount.js.map