@wcstack/state 3.5.4 → 4.0.0-rc.2

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 (82) hide show
  1. package/README.ja.md +669 -428
  2. package/README.md +667 -426
  3. package/dist/auto.min.js +1 -2
  4. package/dist/define.d.ts +4 -8
  5. package/dist/define.js +1 -1
  6. package/dist/index.d.ts +139 -1483
  7. package/dist/index.esm.js +1 -2
  8. package/dist/manifest.d.ts +18 -59
  9. package/dist/manifest.esm.js +542 -1202
  10. package/dist/parser.d.ts +49 -103
  11. package/dist/parser.esm.js +662 -831
  12. package/dist/split/auto.js +1 -0
  13. package/dist/split/chunks/chunk-732CB4QN.js +1 -0
  14. package/dist/split/chunks/chunk-7PN5YNZZ.js +1 -0
  15. package/dist/split/chunks/chunk-FF6KHTYS.js +1 -0
  16. package/dist/split/chunks/chunk-HFKGUDDJ.js +1 -0
  17. package/dist/split/chunks/chunk-PKDBVBAP.js +1 -0
  18. package/dist/split/chunks/chunk-QI7P7YPP.js +1 -0
  19. package/dist/split/chunks/chunk-QQH6N5CJ.js +1 -0
  20. package/dist/split/chunks/chunk-UM3O6QXD.js +1 -0
  21. package/dist/split/chunks/chunk-WEL3YSG7.js +1 -0
  22. package/dist/split/chunks/chunk-WWJNAZVP.js +1 -0
  23. package/dist/split/chunks/chunk-YOPPVWPQ.js +1 -0
  24. package/dist/split/core.d.ts +114 -1168
  25. package/dist/split/core.js +1 -2
  26. package/dist/split/features/devtools.d.ts +23 -3
  27. package/dist/split/features/devtools.js +1 -2
  28. package/dist/split/features/diagnostics.d.ts +27 -5
  29. package/dist/split/features/diagnostics.js +1 -2
  30. package/dist/split/features/formats.d.ts +21 -5
  31. package/dist/split/features/formats.js +1 -2
  32. package/dist/split/features/list-keys.d.ts +24 -0
  33. package/dist/split/features/list-keys.js +1 -0
  34. package/dist/split/features/recursion.d.ts +23 -3
  35. package/dist/split/features/recursion.js +1 -2
  36. package/dist/split/features/scopes.d.ts +25 -4
  37. package/dist/split/features/scopes.js +1 -2
  38. package/dist/split/features/ssr.d.ts +23 -3
  39. package/dist/split/features/ssr.js +1 -2
  40. package/dist/split/features/temporal.d.ts +19 -4
  41. package/dist/split/features/temporal.js +1 -2
  42. package/dist/wcs-manifest.json +43 -49
  43. package/package.json +15 -17
  44. package/dist/auto.min.js.map +0 -1
  45. package/dist/index.esm.js.map +0 -1
  46. package/dist/split/chunks/binder.js +0 -2
  47. package/dist/split/chunks/binder.js.map +0 -1
  48. package/dist/split/chunks/checkDependency.js +0 -2
  49. package/dist/split/chunks/checkDependency.js.map +0 -1
  50. package/dist/split/chunks/declarationAliases.js +0 -2
  51. package/dist/split/chunks/declarationAliases.js.map +0 -1
  52. package/dist/split/chunks/declarationHooks.js +0 -2
  53. package/dist/split/chunks/declarationHooks.js.map +0 -1
  54. package/dist/split/chunks/diagnosticsHooks.js +0 -2
  55. package/dist/split/chunks/diagnosticsHooks.js.map +0 -1
  56. package/dist/split/chunks/errorGuidance.js +0 -2
  57. package/dist/split/chunks/errorGuidance.js.map +0 -1
  58. package/dist/split/chunks/featureBridge.js +0 -2
  59. package/dist/split/chunks/featureBridge.js.map +0 -1
  60. package/dist/split/chunks/features.d.ts +0 -29
  61. package/dist/split/chunks/filterRegistry.js +0 -2
  62. package/dist/split/chunks/filterRegistry.js.map +0 -1
  63. package/dist/split/chunks/getAllPropertyDescriptors.js +0 -2
  64. package/dist/split/chunks/getAllPropertyDescriptors.js.map +0 -1
  65. package/dist/split/chunks/lifecycleHooks.js +0 -2
  66. package/dist/split/chunks/lifecycleHooks.js.map +0 -1
  67. package/dist/split/chunks/pathDiagnostics.js +0 -2
  68. package/dist/split/chunks/pathDiagnostics.js.map +0 -1
  69. package/dist/split/chunks/registerComponents.js +0 -2
  70. package/dist/split/chunks/registerComponents.js.map +0 -1
  71. package/dist/split/chunks/setAll.js +0 -2
  72. package/dist/split/chunks/setAll.js.map +0 -1
  73. package/dist/split/chunks/version.js +0 -2
  74. package/dist/split/chunks/version.js.map +0 -1
  75. package/dist/split/core.js.map +0 -1
  76. package/dist/split/features/devtools.js.map +0 -1
  77. package/dist/split/features/diagnostics.js.map +0 -1
  78. package/dist/split/features/formats.js.map +0 -1
  79. package/dist/split/features/recursion.js.map +0 -1
  80. package/dist/split/features/scopes.js.map +0 -1
  81. package/dist/split/features/ssr.js.map +0 -1
  82. package/dist/split/features/temporal.js.map +0 -1
@@ -1,842 +1,137 @@
1
- export { I as IStateFeature, i as installFeatures } from './chunks/features.js';
2
-
3
- /**
4
- * list/listKeys.ts
5
- *
6
- * `$listKeys: { <listPath>: <fieldName | (row) => key> }` 宣言マップを解析し、
7
- * 「リストパス → キー指定」表を構築する(docs/state-list-key-design.md §3)。
8
- *
9
- * この表が存在するリストパスへの配列代入は、setByAddress でキー突合され、
10
- * 一致行は旧オブジェクトを据え置いたまま変化フィールドだけが per-path 書き込みで
11
- * 流し込まれる(§2)。未宣言なら書き込み経路は従来と完全に同一。
12
- *
13
- * 「そのパスが実際にリストか」は宣言時には判定できない(listPaths は
14
- * バインディング収集時に確定する)。実行時に配列でなければ経路に入らないだけで、
15
- * 宣言自体はエラーにしない。
16
- */
17
-
18
- /** キー指定: フラットなフィールド名、または行から複合キーを作る関数 */
19
- type ListKeySpec = string | ((row: any) => unknown);
20
- type ListKeyMap = ReadonlyMap<string, ListKeySpec>;
21
-
22
- /**
23
- * Interface for hierarchical loop index management in nested loops.
24
- * Tracks parent-child relationships, versions, and provides access to index hierarchy.
25
- */
26
- interface IListIndex {
27
- readonly parentListIndex: IListIndex | null;
28
- readonly uuid: string;
29
- readonly position: number;
30
- readonly length: number;
31
- index: number;
32
- readonly version: number;
33
- readonly dirty: boolean;
34
- readonly indexes: number[];
35
- readonly listIndexes: WeakRef<IListIndex>[];
36
- readonly varName: string;
37
- at(position: number): IListIndex | null;
38
- }
39
- interface ILoopContext extends IStateAddress {
40
- readonly pathInfo: IPathInfo;
41
- readonly listIndex: IListIndex;
42
- }
43
- interface ILoopContextStack {
44
- createLoopContext(elementStateAddress: IStateAddress, callback: (loopContext: ILoopContext) => void | Promise<void>): void | Promise<void>;
45
- }
46
-
47
- declare const setLoopContextSymbol: unique symbol;
48
- declare const getByAddressSymbol: unique symbol;
49
- declare const hasByAddressSymbol: unique symbol;
50
- declare const connectedCallbackSymbol: unique symbol;
51
- declare const disconnectedCallbackSymbol: unique symbol;
52
- declare const updatedCallbackSymbol: unique symbol;
53
- declare const errorCallbackSymbol: unique symbol;
54
-
55
- interface IStateHandler extends ProxyHandler<IState> {
56
- readonly stateElement: IStateElement;
57
- readonly addressStackLength: number;
58
- /**
59
- * アドレススタックの先頭(いま評価しているアドレス)。ループ文脈の無いスコープでは null。
60
- * 再帰の深さ解決(recursion/bind.ts)もここだけを見る — 添字の供給元
61
- * (getContextListIndex / `$getAll` の省略形)が先頭しか見ないので、深さだけを外側の
62
- * フレームから拾うと「深さはあるが行が無い」定義にない状態になる。
63
- */
64
- readonly lastAddressStack: IStateAddress | null;
65
- readonly loopContext: ILoopContext | null | undefined;
66
- /**
67
- * 依存追跡の抑止中か。$untrackDependency のスコープ内、および setter 実行中
68
- * (setter は命令的な代入であって派生ではないため、その中の読み取りで依存を
69
- * 張らない)は true。checkDependency / $1 インデックス依存の登録が抑止される。
70
- */
71
- readonly untracking: boolean;
72
- /**
73
- * このプロキシの書き込み能力。書き込み API の入口(proxy/assertWritable.ts)が検査する — set
74
- * トラップを通らない `$resolve` / `$setAll` も readonly では書けない(要件 B6)。省略は writable。
75
- */
76
- readonly mutability?: Mutability;
77
- pushAddress(address: IStateAddress | null): void;
78
- popAddress(): IStateAddress | null;
79
- setLoopContext(loopContext: ILoopContext | null): void;
80
- clearLoopContext(): void;
81
- beginUntrack(): void;
82
- endUntrack(): void;
83
- }
84
- interface IStateProxy extends IState {
85
- [setLoopContextSymbol](loopContext: ILoopContext | null, callback: () => any): any;
86
- [getByAddressSymbol](address: IStateAddress): any;
87
- [hasByAddressSymbol](address: IStateAddress): boolean;
88
- [connectedCallbackSymbol](): Promise<void>;
89
- [disconnectedCallbackSymbol](): void;
90
- [updatedCallbackSymbol](updatedAbsAddressList: IAbsoluteStateAddress[]): void;
91
- [errorCallbackSymbol](error: unknown, info: IBindingErrorInfo): void;
92
- }
93
- type Mutability = "readonly" | "writable";
94
-
95
- type ParseBindTextResult = IParsedBinding;
96
-
97
- /**
98
- * パース結果の変換フック(Phase 2 のマウント — impl-plan §3-0 の 1)。
99
- * マウントされたスコープの収集は、これで各パース結果を親ツリーの絶対パスへ書き換える。
100
- * `uuid` を持つエントリ(構造フラグメントの参照)には掛けない — フラグメント側の
101
- * パース結果は登録時(collectStructuralFragments)に変換済みで、二重に掛けると
102
- * 接頭辞が二重になる。
103
- */
104
- type ParseResultTransform = (parsed: ParseBindTextResult, forPath?: string) => ParseBindTextResult;
105
- interface IDeferredSpreadEntry {
106
- readonly node: Node;
107
- readonly tagName: string;
108
- readonly parseResults: ParseBindTextResult[];
109
- readonly transform?: ParseResultTransform;
110
- /** 予約中の定義待ちの取り消し(`scheduleDeferredSpreads` が持つ。行の活性化し直しで待ちを重ねない) */
111
- cancel?: () => void;
112
- }
113
-
114
- interface IContent {
115
- readonly firstNode: Node | null;
116
- readonly lastNode: Node | null;
117
- readonly mounted: boolean;
118
- appendTo(targetNode: Node): void;
119
- mountAfter(targetNode: Node): void;
120
- unmount(): void;
121
- /**
122
- * 自分のノードを DOM に残したままの解体(#4)。行の置き換えで `for` が同じ位置の Content を
123
- * 新しい行に使い回すとき、入力中の欄を DOM から外さずに unmount と同じ後始末をする。
124
- */
125
- unmountInPlace(): void;
126
- /**
127
- * wholesale 破棄: 全行クリアで再利用されない content の binding teardown
128
- * (listener 解除・アドレス台帳・loopContext 掃除)を省略し、ノード・binding
129
- * もろとも GC に任せる。定義待ち等の副作用がある場合は false を返し、呼び出し側が
130
- * 従来経路(deactivate + unmount)で解体する。
131
- */
132
- tryDestroy(): boolean;
133
- /**
134
- * まだ展開していない、未定義カスタム要素への spread(#330)。持つ行だけに付く(形を増やさない)。
135
- * 活性化のたびに定義待ちへ予約し、展開したものは外れる
136
- */
137
- spreads?: IDeferredSpreadEntry[];
138
- }
1
+ type BindingType = 'text' | 'prop' | 'event' | 'for' | 'if' | 'elseif' | 'else' | 'radio' | 'checkbox' | 'spread';
139
2
 
140
3
  /**
141
- * core/addressHooks.ts — 読み書き境界の受け口(設計案 H1、S3)。
142
- *
143
- * 機能は `install()` でレジストリに hook 実装を置く(hot path には触れない)。state 要素は
144
- * 宣言が要求する機能の hook だけを `attachAddressHooks` で自分に付け、core の各受け口は
145
- * `stateElement.addressHooks` が null なら判定 1 回で抜ける
146
- * (調査 §10.8: 大域配列の走査は読み +40%、state ごとの門なら +0.1 ns)。
147
- * 宣言が要求する機能が未 install なら `requireFeature` が宣言時に throw する(readiness barrier、D13)。
148
- *
149
- * 受け口(core 側の呼び出し点):
150
- * read getByAddress の先頭(キャッシュより前)。名前空間・マーカーなど raw state に無い値を答える
151
- * readMissing getByAddress で「ツリーにそのキーが無い」と分かった点(親の値つき。親が無ければ null)
152
- * write setByAddress の先頭。書き込みを奪うか、禁止して throw する
153
- * writeMissing setByAddress の fast path で「親にそのキーが無い」と分かった点(公開 getter への書き込み)
154
- * writeObserve 旧値が分かった点(同値ガードの直後)。旧値の台帳を持つ機能が読む
155
- * written 書き込み・`$postUpdate` の後。観測面へ通知する機能が読む
156
- * swapped 要素の入れ替えで行が動いた点(旧値の台帳を行に追従させる)
157
- * get get トラップの文字列プロパティ先頭。API・名前空間・パスの翻訳を答える
158
- * indexShift `$n` の解決点。スコープ相対の段ずれを足す
159
- * handlerScope イベントハンドラの添字の段数を決める点
160
- * updated `$updatedCallback` の後。相対配送する機能が読む
161
- * suppressPathDiagnostic 束縛時の未宣言パス診断を黙らせるか(予約済みスロットの配下など)
162
- * rowReused その場で使い回した行(DOM から外れない)の点。行の中のスコープを新しい listIndex へ張り直す
4
+ * The places the core calls into add-ons (docs/state-engine-rewrite/addons-plan.ja.md §1.2).
5
+ *
6
+ * Add-ons ship as separate entries of the same build as the core (`features/*`): they reach
7
+ * the core's internals directly, and the core only calls out through these slots. A slot is
8
+ * empty until an installed feature fills it (the object starts with none: a list of null slots
9
+ * would be the core's bytes), so a page without the feature pays one `?.` check. A feature
10
+ * restoring a slot may set it to null.
11
+ * Slots are added with the first feature that needs them; several features share one through
12
+ * chain / first. The slots `element` and `detail` keep their names (a DevTools-facing field and a DOM
13
+ * name share them); the others are shortened by the build. An empty slot is read through
14
+ * Object.prototype: a page that polluted it (a value under `element`, say) breaks the core loudly
15
+ * there, as it does wherever the core reads a plain object's keys (`$behavior`); not defended.
163
16
  */
164
17
 
165
- type ReadHook = (stateElement: IStateElement, address: IStateAddress, receiver: any, handler: IStateHandler) => unknown;
166
- type ReadMissingHook = (stateElement: IStateElement, address: IStateAddress, parentValue: object | null, receiver: any, handler: IStateHandler) => unknown;
167
- type WriteHook = (stateElement: IStateElement, address: IStateAddress, value: unknown, receiver: any, handler: IStateHandler) => unknown;
168
- type WriteMissingHook = (stateElement: IStateElement, address: IStateAddress, parentValue: object, key: PropertyKey, value: unknown, receiver: any, handler: IStateHandler) => unknown;
169
- type WriteObserveHook = (stateElement: IStateElement, path: string, absAddress: IAbsoluteStateAddress, oldValue: unknown, hasOldValue: boolean) => void;
170
- type WrittenHook = (stateElement: IStateElement, pathInfo: IPathInfo, detail?: {
171
- readonly value: unknown;
172
- }) => void;
173
- type SwappedHook = (stateElement: IStateElement, elementAbsAddress: IAbsoluteStateAddress, displacedAbsAddress: IAbsoluteStateAddress) => void;
174
- type GetHook = (handler: IStateHandler, prop: string, receiver: any, target: object) => unknown;
175
- type IndexShiftHook = (handler: IStateHandler, lastAddress: IStateAddress) => number;
176
- /**
177
- * ハンドラが受け取る添字の段数を決める(`event/handler.ts`)。
178
- *
179
- * **合成は未定義** — この hook は畳み込み(前の hook の結果が次の `wildcardCount` になる)で
180
- * 呼ばれるが、現行唯一の実装(`webComponent/addressHooks.ts` の scopes)は「記録が引けて
181
- * shift が無ければ 0」を返す**吸収型**で、可換でも結合的でもない。2 つ目の実装が足されると
182
- * `0 - shift` のような負値になりうる。**実装は 1 つに保つこと**。2 つ目が要るなら、先に
183
- * 「答えを持たない hook は次へ渡す」規約(`NOT_HANDLED` を返す形。`read` / `write` と同じ)
184
- * へ寄せてから足す。
185
- */
186
- type HandlerScopeHook = (stateElement: IStateElement, node: Node, rootNode: Node, loopContext: ILoopContext, wildcardCount: number) => number;
187
- type UpdatedHook = (stateElement: IStateElement, refs: IAbsoluteStateAddress[], receiver: any) => void;
188
- type SuppressPathDiagnosticHook = (stateElement: IStateElement, path: string) => boolean;
189
- type RowReusedHook = (stateElement: IStateElement, content: IContent) => void;
190
- interface IAddressHooks {
191
- readonly read?: ReadHook;
192
- readonly readMissing?: ReadMissingHook;
193
- readonly write?: WriteHook;
194
- readonly writeMissing?: WriteMissingHook;
195
- readonly writeObserve?: WriteObserveHook;
196
- readonly written?: WrittenHook;
197
- readonly swapped?: SwappedHook;
198
- readonly get?: GetHook;
199
- readonly indexShift?: IndexShiftHook;
200
- readonly handlerScope?: HandlerScopeHook;
201
- readonly updated?: UpdatedHook;
202
- readonly suppressPathDiagnostic?: SuppressPathDiagnosticHook;
203
- readonly rowReused?: RowReusedHook;
18
+ /** An add-on entry: `installFeatures([temporal, diagnostics])` before the state is defined. */
19
+ interface Feature {
20
+ readonly name: string;
21
+ install(): void;
204
22
  }
205
- /** state 要素に付いた hook 群(種類ごとに、機能の登録順) */
206
- type IAttachedHooks = {
207
- readonly [K in keyof Required<IAddressHooks>]: NonNullable<IAddressHooks[K]>[];
208
- };
23
+ declare function installFeatures(features: readonly Feature[]): void;
209
24
 
210
25
  /**
211
- * pathDiagnostics.ts — パスに関する**エラーの文言**(throw する側)と、診断の両面が共有する部品。
212
- *
213
- * 束縛時の存在検査(`user.nmae` のような打ち間違いを console.warn で知らせる開発時の診断)は
214
- * `features/diagnostics` へ分けた(diagnostics/pathChecks.ts)。core に残るのは、実行を止める
215
- * エラーの文言と、その did-you-mean が使う候補の集め方だけ。エラーは機能の有無に関わらず
216
- * 読める文言で落ちなければならないので、ここは core から外せない。
217
- *
218
- * 診断 code はコンソール → lint → IDE の三面で共有する(errorGuidance.ts の規約)。
26
+ * The public types of @wcstack/state (the same names as 3.x). The engine's own classes stay
27
+ * internal: a page reaches `<wcs-state>` through `IStateElement`.
219
28
  */
220
- /** `setPathInfo` の呼び出し元の種別。診断 code と適用範囲がこれで変わる */
221
- type PathInfoSource =
222
- /** data-wcs / mustache / コメントバインディング */
223
- "binding"
224
- /** `$watch` の宣言キー */
225
- | "watch"
226
- /** `$scan` の `from` / `resetOn`(docs/state-scan-design.md §2-4) */
227
- | "scan"
228
- /** ランタイム内部のパス翻訳(mapped な bind-component の外向き伝播)。検査しない */
229
- | "internal";
230
29
 
231
- /**
232
- * 単一の自己再帰宣言。初版はアンカーと反復サブパスとも「固定プロパティ列の末尾に
233
- * `.*` がひとつ」の形に限定する(docs/state-recursive-path-impl-plan.md §1-1)。
234
- *
235
- * 例: `$recursion = { "nodes.*": "children.*" }`
236
- * - `anchor` … `"nodes.*"`(深さ 0 のノードパス)
237
- * - `repeat` … `"children.*"`(1 段深くする相対サブパス)
238
- * - `recursiveAnchor` … `"nodes.**"`
239
- * - `anchorList` … `"nodes"`
240
- * - `repeatList` … `"children"`
241
- *
242
- * リスト側の 2 つは宣言時に確定させる(静的側の `RecursionSpec` と同じ構成)。
243
- * 各所で `lastIndexOf(DELIMITER)` の slice を繰り返すと、綴りの取り違えが分散する。
244
- */
245
- interface IRecursionSpec {
246
- readonly anchor: string;
247
- readonly repeat: string;
248
- /** `anchor` の `**` 形(`"nodes.**"`)。オーサリング層のパス解析で使う。 */
249
- readonly recursiveAnchor: string;
250
- /** `anchor` のリスト側(`"nodes"` — 末尾の `.*` を落とした形)。 */
251
- readonly anchorList: string;
252
- /** `repeat` のリスト側(`"children"`)。 */
253
- readonly repeatList: string;
30
+ /** An add-on entry (`@wcstack/state/features/*`), installed with `installFeatures([...])`. */
31
+ type IStateFeature = Feature;
32
+ interface IWritableTagNames {
33
+ state?: string;
34
+ ssr?: string;
254
35
  }
255
- /**
256
- * 展開済みの再帰 getter 1 本ぶんの素性。生成アクセサに紐づくメタデータで、
257
- * ランタイムが読むのは深さ(`**` の束縛)と元の宣言(診断の名指し)の 2 つだけ。
258
- * 具体パスは台帳のキー、`PathInfo` は読む側が intern 済みのものを持つので、ここには
259
- * 重ねて持たない。
260
- */
261
- interface IRecursionAccessor {
262
- /** 元の宣言(`"nodes.**.total"`) */
263
- readonly recursivePath: string;
264
- /** 反復の段数(0 origin) */
265
- readonly depth: number;
36
+ /** `bootstrapState(config)`: every option is optional (README "Configuration"). */
37
+ interface IWritableConfig {
38
+ bindAttributeName?: string;
39
+ commentForPrefix?: string;
40
+ commentIfPrefix?: string;
41
+ commentElseIfPrefix?: string;
42
+ commentElsePrefix?: string;
43
+ tagNames?: IWritableTagNames;
44
+ locale?: string;
45
+ enableContractAnalyzer?: boolean;
266
46
  }
267
-
268
- /**
269
- * recursion/registry.ts
270
- *
271
- * state 1 つぶんの再帰レジストリ。宣言・`**` getter の定義・展開済みアクセサの台帳を
272
- * 持ち、「具体パスを読む直前に、その深さのアクセサを生やす」遅延実体化を担う。
273
- *
274
- * 遅延であることは実装の**不変条件**である(Phase A の A6/A7)。そのパスを一度でも
275
- * 読んでから生やしても、`isCacheable` が `wildcardCount > 0` だけでキャッシュ可を返す
276
- * ため `undefined` が `dirty:false` で固定され、以後どう書いても回復しない。
277
- * したがって実体化は `getByAddress` のキャッシュ参照**前**に置く(E5)。
278
- *
279
- * 寿命は state の世代と共にする。`_state` の再セットで `getterPaths` / `listPaths` は
280
- * クリアされるので、レジストリも作り直す(§1-3)。ただし**生やしたアクセサは state
281
- * オブジェクトの側に残る**ので、同じ state を再セットすると `getStateInfo` がそれを
282
- * `getterPaths` に復元する。そのとき「もう生えているから何もしない」と早期 return して
283
- * しまうと `listPaths` の登録だけが抜け落ちるため、生成物は WeakSet で見分けて
284
- * 登録だけをやり直す。
285
- */
286
-
287
- declare class RecursionRegistry {
288
- readonly spec: IRecursionSpec;
289
- private readonly _definitions;
290
- private readonly _accessors;
291
- /**
292
- * `recursiveGetterOwning` の記憶。キーは添字を `*` に畳んだ形(`nodes.1.total` と `nodes.2.total`
293
- * は 1 つ)、値は「その具体パスを展開形(またはその値の内側)として持つ `**` getter」、
294
- * 無ければ null。
295
- *
296
- * 有界である: キーは添字を畳んだワイルドカード形のパス文字列で、`PathInfo` が intern する集合
297
- * (バインディング・getter・API 引数に綴られたパスと、その展開形)の部分集合にしかならない。
298
- * intern 済みパスの集合が有界であることは D10 で受け入れ済みなので、ここも同じ上限に収まる。
299
- * 文字列は WeakSet に入らないので、寿命はレジストリ(= state の世代)と共にする。
300
- */
301
- private readonly _ownerByPath;
302
- /**
303
- * 書き込みのホットパス(`setByAddress`)向けの記憶。キーは intern 済みの `PathInfo` なので
304
- * 寿命と上限は PathInfo の intern 集合と同じ(WeakMap)。畳み(split + Number + join)は
305
- * miss のときだけ払う — 宣言のある state では**アンカー外を含む全書き込み**がここを通る
306
- * (第 4 サイクルで実測: 畳みを毎回払うと `s.counter = i` で +100ns/書き込み)。
307
- */
308
- private readonly _ownerByPathInfo;
309
- /**
310
- * 読みのホットパス(`getByAddress`)向けの記憶。`_ownerByPathInfo` と対称で、キーは
311
- * intern 済みの `PathInfo`、値は「そのパスの展開アクセサ」、展開形でなければ null。
312
- * 宣言のある state では**アンカー外を含む全読み**(親ウォークの各段を含む)がここを
313
- * 通るので、文字列キーの `Map.get` + `Set.has` + `startsWith` を毎回払わせない
314
- * (第 5 サイクルで実測)。
315
- *
316
- * 読みの否定判定の記憶は**ここ 1 つ**(第 5 サイクル再検証で文字列キーの `_nonAccessors` を撤去 —
317
- * 前段にこの記憶を置いた後は、PathInfo とパス文字列が 1:1 なので二重に持つだけだった)。
318
- * 否定を記憶してよい根拠は、定義集合が state の世代内で不変であること —
319
- * 同じ `PathInfo` は同じパス文字列なので、いちど「展開形でない」と決まった PathInfo が
320
- * 後から実体化されることはない。実体化した側は `materializeForPathInfo` が
321
- * `_define` の戻り値でそのまま記憶を更新する(否定が実体化を隠さない)。
322
- */
323
- private readonly _accessorByPathInfo;
324
- /** `concretePathAt` の記憶(接尾辞 → 深さ順の具体パス)。 */
325
- private readonly _concreteBySuffix;
326
- private readonly _registeredListPaths;
327
- constructor(spec: IRecursionSpec, state: object);
328
- /**
329
- * 作者が手で書いた具体パス(`get "nodes.*.children.*.total"()` / データプロパティ)が、宣言済み
330
- * `**` getter の展開形と同名でないことを**構築時に**確かめる。
331
- *
332
- * `_define` の衝突検査は「その深さを最初に読んだとき」にしか走らないので、データが浅い間は
333
- * 通り、木が 1 段深くなった瞬間にバインディングが落ちていた(第 3 サイクルのレビューで実測)。
334
- * 前世代の生成物(own に残った生成 getter)は衝突ではない — 同じ state の再セットで必ず居る。
335
- */
336
- private _assertNoConcreteCollision;
337
- /**
338
- * 2 本の `**` getter が同じ具体パスへ展開しないことを、宣言だけから静的に確かめる。
339
- *
340
- * 衝突するのは「片方の接尾辞がもう片方の接尾辞の末尾で、差分が反復語の整数倍」の
341
- * ときだけ(`nodes.**.total` と `nodes.**.children.*.total` は深さ k と k+1 で
342
- * 同じ `nodes.*.children.*.total` になる)。検出しないと `_definitions` の挿入順で
343
- * 最初に一致した方が無言で勝つ。
344
- */
345
- private _assertNoColliding;
346
- /**
347
- * `**` getter を 1 本でも宣言しているか。
348
- * **テスト・診断専用**(ランタイムの経路は `_definitions.size` を直接見る)。
349
- */
350
- get hasDefinitions(): boolean;
351
- /**
352
- * その接尾辞が宣言済みの `**` getter と衝突するなら、その getter のパスを返す。
353
- *
354
- * 完全一致だけでは足りない。①反復語の整数倍だけ違う接尾辞は同じ族を指す
355
- * (`_assertNoColliding` が宣言どうしについて既に見ている条件)②getter の**下**を
356
- * 指す形(`nodes.**.total.x` / 反復語ぶんずれた `nodes.**.children.*.total.x`)は、
357
- * getter が返したオブジェクトへ書いてキャッシュを汚し、次の無効化で無言に戻る。
358
- * どちらも書き込みの入口(列挙より前)で止める — 述語は expand.ts の `coversSuffix`。
359
- */
360
- conflictingRecursiveGetter(suffix: string): string | null;
361
- /**
362
- * `recursiveGetterOwning` の intern 済み `PathInfo` 版(書き込みのホットパス用)。
363
- * WeakMap の hit なら畳みも照合も払わない。
364
- */
365
- recursiveGetterOwningPath(pathInfo: IPathInfo): string | null;
366
- /**
367
- * 具体パスを展開形(またはその値の内側)として持つ `**` getter のパス。無ければ null。
368
- * **実体化はしない。**
369
- *
370
- * `conflictingRecursiveGetter` の**具体パス版**で、`**` を経ない 2 つの入口が使う:
371
- *
372
- * - バインド確立時のパス存在検査(`checkDeclaredPath`)。あの時点ではまだ生えて
373
- * いないので、素の存在検査では必ず「解決できない」になる。展開形そのもの
374
- * (`nodes.*.total`)だけでなく、その値の中を指す形(`nodes.*.stats.count` で
375
- * `get "nodes.**.stats"()` がオブジェクトを返す)も、通常の getter の下と同じく
376
- * 評価しないと分からないので黙る側に倒す。
377
- * - 書き込みの入口(`setByAddress`)。`$setAll("nodes.*.children.*.total", [], v)` や
378
- * `this["nodes.1.total"] = v` は `**` を含まないので `setAllRecursive` の
379
- * 読み取り専用検査を通らず、未実体化なら fast path が行オブジェクトへ素の
380
- * プロパティとして書いてしまう(ノードを汚し、代入値が `dirty:false` で載って
381
- * 以後 getter が評価されない)。展開形への書き込みは、実体化の前後に関わらず
382
- * `wcs/recursion-readonly` で止める。
383
- */
384
- recursiveGetterOwning(concretePath: string): string | null;
385
- /** 具体パスが宣言済み `**` getter の展開形そのものなら、その getter のパス。 */
386
- private _matchExpansion;
387
- /**
388
- * `materializeFor` の `PathInfo` 版。**読みのホットパス(`getByAddress`)専用**で、
389
- * 判定そのものは `materializeFor` に委ね、結果(否定を含む)を PathInfo に記憶する。
390
- * 書き側の `recursiveGetterOwningPath` と対称。
391
- */
392
- materializeForPathInfo(stateElement: IStateElement, pathInfo: IPathInfo): IRecursionAccessor | null;
393
- /**
394
- * 具体パスが再帰 getter の展開形なら、そのアクセサを(未登録なら生やして)返す。
395
- * 該当しなければ null。読みは `materializeForPathInfo` を通るので、ここへ来るのは
396
- * 記憶が外れたときだけ — 判定は接頭辞 1 回で抜け、ここでは否定を記憶しない(記憶は
397
- * `materializeForPathInfo` の PathInfo キーの 1 か所)。
398
- * (`**` getter の無い空レジストリを弾くのは呼び出し側の役目。)
399
- */
400
- materializeFor(stateElement: IStateElement, concretePath: string): IRecursionAccessor | null;
401
- private _define;
402
- /**
403
- * 経路上のリストパスを `listPaths` に載せる(E4)。`setPathInfo(path, "for")` は
404
- * 使えない — あちらは `elementPaths` にも入れて `setByAddress` の swap 経路
405
- * (`isSwappable`)を変えてしまう。ここで要るのは「依存ウォークがこのパスを
406
- * リストとして展開する」ことだけ。
407
- */
408
- private _registerListPaths;
409
- /**
410
- * この世代が生やしたもの(own の生成アクセサ・依存辺・キャッシュ)を忘れる(state の
411
- * 再セット時、`getStateInfo` の再収集より**前**に呼ぶ)。実体は generation.ts。
412
- */
413
- forgetGenerated(stateElement: IStateElement, previousState: object): ReadonlySet<string>;
414
- /**
415
- * `**` 接尾辞の深さ `depth` の具体パス(`concretePathAt` の記憶付き版)。
416
- * 束縛形の読み(`this["nodes.**.value"]` / 省略形 `$getAll`)は再帰 getter の評価ごとに
417
- * ここを通るので、深さぶんの文字列連結とワイルドカード数えを毎回やり直さない。
418
- * 上限は「接尾辞の種類 × 128」で有界(上限超過は `concretePathAt` が throw するので載らない)。
419
- */
420
- concretePathAt(suffix: string, depth: number): string;
421
- /** 展開済みアクセサのメタデータ(深さ解決・診断・テスト用)。 */
422
- accessorFor(concretePath: string): IRecursionAccessor | null;
47
+ /** What `$errorCallback(error, info)` receives about the binding that failed. */
48
+ interface IBindingErrorInfo {
49
+ /** The bound state path as written in `data-wcs` (wildcards intact). */
50
+ readonly path: string;
51
+ /** The binding's type as the parser classifies it. */
52
+ readonly bindingType: BindingType;
423
53
  /**
424
- * これまでに実体化した具体パスの一覧。**テスト専用**(「読んだ深さだけが生える」という
425
- * 遅延実体化の不変条件を外から確かめる口。ランタイムはどの経路からも呼ばない)。
54
+ * The node the binding is on (the Text node for a text binding); null for a list no `for:`
55
+ * renders (one only `$getAll` or `$watch` keeps) that failed to read.
426
56
  */
427
- get materializedPaths(): ReadonlySet<string>;
57
+ readonly node: Node | null;
428
58
  }
429
-
430
- interface IStateElement {
431
- /** DOM connection state; optional for non-DOM state implementations. */
432
- readonly isConnected?: boolean;
433
- /**
434
- * state のロードが完了しているか。`initializePromise` の同期版で、
435
- * DCC のアクセサが「今すぐ読み書きしてよいか」を判断するのに使う。
436
- * optional なのはテスト用モック互換のため(undefined は「不明=未初期化扱い」)。
437
- */
438
- readonly initialized?: boolean;
59
+ /** `<wcs-state>` (README "IStateElement"). */
60
+ interface IStateElement extends HTMLElement {
61
+ /** Resolves when the state is loaded and the page bound — also when initialization fails. */
439
62
  readonly initializePromise: Promise<void>;
440
- readonly connectedCallbackPromise: Promise<void>;
441
- readonly listPaths: Set<string>;
442
- readonly elementPaths: Set<string>;
443
- readonly getterPaths: Set<string>;
444
- readonly setterPaths: Set<string>;
445
- readonly loopContextStack: ILoopContextStack;
446
- readonly dynamicDependency: Map<string, string[]>;
447
- readonly staticDependency: Map<string, string[]>;
448
- readonly version: number;
449
- /**
450
- * state の世代。`_state` の差し替えごとに 1 つ進み、キャッシュ項目の印になる
451
- * (cache/types.ts の `generation`)。`version`(更新サイクルの番号)とは別のカウンタ。
452
- * optional なのはテスト用モック互換のため(undefined のモックが載せた項目は
453
- * undefined 同士で一致し、従来どおりヒットする — `__tests__/proxy.getByAddress.test.ts`
454
- * の「キャッシュがある場合はキャッシュを返すこと」が固定する)。
455
- */
456
- readonly stateGeneration?: number;
457
- readonly rootNode: Node;
458
- readonly boundComponentStateProp: string | null;
459
- /**
460
- * `bind-component` で束ねられているコンポーネント要素(親スコープ側のノード)。
461
- * マッピング規則の引き当てに使う。optional なのはテスト用モック互換のため。
462
- */
463
- /**
464
- * この state の実体が innerState proxy(= 値の正本が親スコープの state にある
465
- * mapped な `bind-component`)か。真のときだけ越境アドレスの受け渡しと
466
- * リストパスの外向き伝播が働く(§1.8)。
467
- * optional なのはテスト用モック互換のため(undefined は plain 扱い)。
468
- */
469
- /**
470
- * この state element にマウント(Phase 2 の単一ツリー — webComponent/mount.ts)が
471
- * 1 つでも登録されているか。偽のとき getByAddress / isCacheable / `$n` 補正は
472
- * boolean 判定 1 個でオーバーレイ経路を抜ける(設計書 D18)。
473
- * optional なのはテスト用モック互換のため(undefined は「マウント無し」扱い)。
474
- */
475
- readonly hasMounts?: boolean;
476
- markHasMounts?(): void;
477
- /**
478
- * この state element に接ぎ木済みのボリューム(`mount=` — webComponent/volume.ts)が
479
- * 1 つでもあるか。偽のとき setByAddress の D22 後段ガード(マウントポイントを含む
480
- * 親の丸ごと書き検査)は boolean 判定 1 個で抜ける(設計書 D18 と同じ形)。
481
- * optional なのはテスト用モック互換のため(undefined は「ボリューム無し」扱い)。
482
- */
483
- readonly hasGraftedVolumes?: boolean;
484
- markHasGraftedVolumes?(): void;
485
- /** ボリュームがこのルートに予約・接ぎ木された: スコープ機能の hook を付ける(webComponent/addressHooks.ts) */
486
- markHasVolume?(): void;
487
- /**
488
- * ライフサイクル機能(core/lifecycleHooks.ts、設計案 H3)へ開く内部面。接続を引き取った機能が
489
- * 要素の初期化を所有するために要る最小限で、optional はテスト用モック互換のため。
490
- */
491
- /** いま接続している rootNode(未接続は null)。公開の `rootNode` と違い throw しない */
492
- readonly connectedRootNode?: Node | null;
493
- clearConnectedRootNode?(): void;
494
- /** 初期化完了の印(引き取った機能が自分の着地で立てる) */
495
- markInitialized?(): void;
496
- /** 初期化待ちの 3 つの promise を解決する(未解決のまま投げるとページが無言でウェッジする) */
497
- settleInitialization?(): void;
498
- /** `state` / `src` / 内包スクリプトからこの要素のソースを読む */
499
- loadStateFromSource?(): Promise<Record<string, any>>;
500
- /** 自分のツリーを持たずに初期化を終えた印(DCC 定義要素。再接続でこの rootNode のツリーとして登録し直さない) */
501
- markTreeless?(): void;
502
- /** 初期化失敗の着地(診断 1 件・connectedCallbackPromise の reject — #257)。常に throw する */
503
- failInitializeLoudly?(error: unknown): never;
504
- /** 接続の世代(接続の末尾の起動が、陳腐化した connect の再開を弾くために照合する) */
505
- readonly connectGeneration?: number;
506
- /** `$watch` / ボリュームの合流が決めた監視パス(setByAddress の旧値キャプチャのゲート) */
507
- setWatchPaths?(paths: ReadonlySet<string> | null): void;
508
- /** `$scan` の `from` パス(`watchPaths` と並ぶ旧値キャプチャのゲート) */
509
- setScanPaths?(paths: ReadonlySet<string> | null): void;
510
- /** 設定エラーの着地(初期化待ちの promise を解決し、二重着地の印を立てる) */
511
- landInitialization?(): void;
512
- /** `bind-component` が束ねた相手(`boundComponentStateProp` の答えになる) */
513
- setBoundComponent?(component: Element | null, stateProp: string | null): void;
514
- /** `$recursion` のレジストリ(宣言が無ければ null。`hasRecursion` の裏づけ) */
515
- setRecursionRegistry?(registry: RecursionRegistry | null): void;
516
- /** 前世代の再帰レジストリが生やした具体パス(経路情報の作り直しから除く) */
517
- addGeneratedPath?(path: string): void;
518
- /**
519
- * 読み書き境界の hook(core/addressHooks.ts、設計案 H1)。宣言が要求する機能の分だけ
520
- * `attachAddressHooks` で付く。無い state は null 判定 1 個で抜ける。optional はモック互換
521
- */
522
- readonly addressHooks?: IAttachedHooks | null;
523
- attachAddressHooks?(feature: string, declaration: string): void;
524
- /**
525
- * この state 要素に束ねられた(`setPathInfo` を通った)パスの集合。丸ごとマウント
526
- * (ルート規則)の親→子通知が「登録済みパス全部を読み直せ」を組み立てるのに使う
527
- * (webComponent/rootReloadPaths.ts)。
528
- * optional なのはテスト用モック互換のため(undefined は「登録なし」扱い)。
529
- */
530
- /**
531
- * DCC の `$bindables` から生成した「パス → 変更イベント名」表。
532
- * 唯一の書き手は defineDCC で、読み手は setByAddress。
533
- * getter だけを公開して setter をインターフェースから落としていたため
534
- * defineDCC が具象 State に依存していた(§3.5)。
535
- */
536
- readonly bindableEventMap: Record<string, string>;
537
- setBindableEventMap(map: Record<string, string>): void;
538
- readonly commandTokenNames: ReadonlySet<string>;
539
- readonly eventTokenNames: ReadonlySet<string>;
540
- /**
541
- * state が $updatedCallback を定義しているか。false のとき drain は更新
542
- * アドレスの集計と最終の writable createState を丸ごとスキップできる。
543
- * optional なのはテスト用モック互換のため(undefined は「不明=集計する」)。
544
- */
545
- readonly hasUpdatedCallback?: boolean;
546
- /**
547
- * state が $errorCallback を定義しているか。true のとき drain は隔離したバインディング
548
- * 適用失敗を console.error の代わりに $errorCallback へ配送する(devtools sink へは常に流す)。
549
- * optional なのはテスト用モック互換のため(undefined は「未定義=console.error」)。
550
- */
551
- readonly hasErrorCallback?: boolean;
552
63
  /**
553
- * 他行を読む getter(隣接項目参照など)が検出されたリストパスの集合。
554
- * これらのリストは walkDependency の diff-filter 展開の対象外(全行展開)。
555
- * optional なのはテスト用モック互換のため(undefined は「検出なし」扱い)。
64
+ * Resolves once `connectedCallback` completed (`$connectedCallback` run); a root that fails to
65
+ * initialize rejects it, and so does one whose `$connectedCallback` rejects (its page is bound), a
66
+ * DCC definition and a component mount (`bind-component`) that fail — a volume that fails resolves it.
556
67
  */
557
- readonly crossRowListPaths?: ReadonlySet<string>;
558
- addCrossRowListPath?(path: string): void;
559
- /**
560
- * 評価中に $1 等のインデックスを読んだ getter パスの集合(実行時検出)。
561
- * 位置だけが変わった行(listDiff.changeIndexSet)は index 以外の入力が不変なので、
562
- * walkDependency の静的子展開をこの集合の subtree に限定できる。
563
- * optional なのはテスト用モック互換のため(undefined は「検出なし」扱い)。
564
- */
565
- readonly indexDependentGetterPaths?: ReadonlySet<string>;
566
- addIndexDependentGetterPath?(path: string): void;
567
- /**
568
- * `$listKeys` 宣言から生成した「リストパス → キー指定」表。
569
- * 宣言が無ければ null / undefined で、setByAddress のキー突合経路に一切入らない
570
- * (docs/state-list-key-design.md §7-1 のゼロコスト契約)。
571
- * optional なのはテスト用モック互換のため(undefined は「宣言なし」扱い)。
572
- */
573
- readonly listKeys?: ListKeyMap | null;
574
- /**
575
- * `$watch` 宣言から生成した監視対象パスの集合。
576
- * 宣言が無ければ null / undefined で、setByAddress の旧値キャプチャには一切入らない
577
- * (docs/state-watch-hook-design.md §10 のゼロコスト契約)。
578
- * optional なのはテスト用モック互換のため(undefined は「宣言なし」扱い)。
579
- */
580
- readonly watchPaths?: ReadonlySet<string> | null;
581
- /**
582
- * `$scan` の `from` パスの集合(docs/state-scan-design.md §2-1)。`watchPaths` と並んで
583
- * setByAddress の旧値キャプチャのゲートになる(fold に `prev` を渡すため)。
584
- * 宣言が無ければ null / undefined。optional なのはテスト用モック互換のため。
585
- */
586
- readonly scanPaths?: ReadonlySet<string> | null;
587
- /**
588
- * パスを依存グラフへ登録する。DOM バインディング登録(BindingSession)のほか、
589
- * `$watch` 宣言(processWatchDeclaration)からも呼ばれる — 静的依存グラフに
590
- * 載るのがバインド済みパスだけだと headless 購読が成立しないため(設計書 §8)。
591
- *
592
- * `source` は存在検査の診断 code と適用範囲を決める(pathDiagnostics.ts)。
593
- * 省略時は `"binding"`(テスト用モック互換のため optional)。
594
- */
595
- /** ボリュームの宣言面の合流(webComponent/volume.ts 専用・実装は State のみ) */
596
- addVolumeWatchPaths?(paths: ReadonlySet<string>): void;
597
- mergeVolumeListKeys?(entries: ReadonlyMap<string, ListKeySpec>): void;
598
- enableUpdatedCallback?(): void;
599
- /** enable-ssr スナップショットから初期化されたか(D14)。 */
600
- readonly hydratedFromSsr?: boolean;
601
- /** ボリュームのアクセサ登録(webComponent/volume.ts 専用) */
602
- defineTreeAccessor(path: string, descriptor: PropertyDescriptor): void;
603
- /**
604
- * リストパスとしてだけ登録する(`listPaths` に足す)。`setPathInfo(path, "for")` は
605
- * `elementPaths` にも入れて `setByAddress` の swap 経路(`isSwappable`)を変えるので、
606
- * 「依存ウォークがこのパスをリストとして展開する」ことだけが要る用途には使えない
607
- * (docs/state-recursive-path-impl-plan.md §3-2 の E4)。
608
- */
609
- addListPath(path: string): void;
610
- /**
611
- * state オブジェクト自身+プロトタイプチェーンから descriptor を引く(生成物と作者定義の
612
- * 見分けに使う)。class 構文の getter は prototype に載るので own だけでは足りない。
613
- */
614
- findStateDescriptor(path: string): PropertyDescriptor | undefined;
615
- /**
616
- * この state に `$recursion` 宣言があるか。偽のとき getByAddress の遅延実体化と
617
- * get トラップの `**` 解決は boolean 判定 1 個で抜ける(hasMounts と同じ D18 の形)。
618
- * 読み手は必ずこのゲートを先に見て、真なら `recursionRegistry` を `!` で読む。
619
- * 必須メンバー(実装は `State` のみ)。`any` 型のテスト用モックがフィールドを持たなくても
620
- * 通るのは vitest が型検査をしないからで、その場合 `undefined === true` は偽なので再帰の
621
- * 経路に入らないだけ — 型としては必須。
622
- */
623
- readonly hasRecursion: boolean;
68
+ readonly connectedCallbackPromise: Promise<void>;
69
+ /** Runs `callback` with a state proxy; its writes are applied in the next drain. */
70
+ createState(mutability: "readonly" | "writable", callback: (state: Record<string, any>) => void): void;
71
+ /** `createState` whose callback may await; a readonly proxy stays readonly across its awaits. */
72
+ createStateAsync(mutability: "readonly" | "writable", callback: (state: Record<string, any>) => Promise<void>): Promise<void>;
624
73
  /**
625
- * 再帰レジストリ(宣言が無ければ null。`hasRecursion === true` なら非 null)。registry.ts は
626
- * このファイルの IStateElement を参照するが、`import type` どうしなので実行時の循環にはならない。
74
+ * Before initialization: the initial state. After: replaces the whole state and re-applies every
75
+ * binding — also on a root whose `$connectedCallback` rejected. Throws on a root that failed to
76
+ * initialize (#14), and on a loaded volume, component (`bind-component`) or DCC definition.
627
77
  */
628
- readonly recursionRegistry: RecursionRegistry | null;
629
- setPathInfo(path: string, bindingType: BindingType, source?: PathInfoSource): void;
630
- addStaticDependency(parentPath: string, childPath: string): boolean;
631
- addDynamicDependency(fromPath: string, toPath: string): boolean;
632
- createStateAsync(mutability: Mutability, callback: (state: IStateProxy) => Promise<void>): Promise<void>;
633
- createState(mutability: Mutability, callback: (state: IStateProxy) => void): void;
634
- nextVersion(): number;
635
78
  setInitialState(state: Record<string, any>): void;
636
79
  }
637
80
 
638
- interface IPathInfo {
639
- readonly id: number;
640
- readonly path: string;
641
- readonly segments: string[];
642
- readonly lastSegment: string;
643
- readonly cumulativePaths: string[];
644
- readonly cumulativePathSet: Set<string>;
645
- readonly cumulativePathInfos: IPathInfo[];
646
- readonly cumulativePathInfoSet: Set<IPathInfo>;
647
- readonly parentPath: string | null;
648
- readonly parentPathInfo: IPathInfo | null;
649
- readonly wildcardPaths: string[];
650
- readonly wildcardPathSet: Set<string>;
651
- readonly indexByWildcardPath: Record<string, number>;
652
- readonly wildcardPathInfos: IPathInfo[];
653
- readonly wildcardPathInfoSet: Set<IPathInfo>;
654
- readonly wildcardParentPaths: string[];
655
- readonly wildcardParentPathSet: Set<string>;
656
- readonly wildcardParentPathInfos: IPathInfo[];
657
- readonly wildcardParentPathInfoSet: Set<IPathInfo>;
658
- readonly wildcardPositions: number[];
659
- readonly lastWildcardPath: string | null;
660
- readonly lastWildcardInfo: IPathInfo | null;
661
- readonly wildcardCount: number;
662
- }
663
- interface IStateAddress {
664
- readonly pathInfo: IPathInfo;
665
- readonly listIndex: IListIndex | null;
666
- readonly parentAddress: IStateAddress | null;
667
- }
668
- interface ITreePath {
669
- readonly stateElement: IStateElement;
670
- readonly pathInfo: IPathInfo;
671
- }
672
- interface IAbsoluteStateAddress {
673
- readonly absolutePathInfo: ITreePath;
674
- readonly listIndex: IListIndex | null;
675
- }
676
-
677
- type BindingType = 'text' | 'prop' | 'event' | 'for' | 'if' | 'elseif' | 'else' | 'radio' | 'checkbox' | 'spread';
678
81
  /**
679
- * 文法の段が読むフィルタ(名前と引数だけ。要件 D16)。実関数は束縛計画の段で
680
- * 登録簿から解決される(`core/filterRegistry.ts`)ので、パース結果はここで止まる。
82
+ * Resolves when the bindings of `root` are built — by any `<wcs-state>` in it now, whatever its
83
+ * `$connectedCallback` does then, as 3.x — and rejects, with the first failure, only when every one
84
+ * failed before building them. A stray one (a second root, #47; one that fails to load) changes
85
+ * nothing while another binds the root; one taken out of the page (or moved to another root) counts
86
+ * no more (a root replaced: the new one decides). With none in it — none ever, or only ones that left —
87
+ * it resolves at once: there is nothing to wait for (a page without state).
681
88
  */
682
- interface IParsedFilter {
683
- readonly filterName: string;
684
- readonly args: string[];
685
- /**
686
- * 引数の型付きの値(要件 B9)。引用符の無い `true` / `false` / `null` / 数値は型付き、引用符付きは
687
- * 文字列のまま。`args` は引用符を外した原文(書式フィルタはこちらを読む)。組み立てた側が省略したら
688
- * `args` と同じ扱い。
689
- */
690
- readonly literals?: readonly unknown[];
691
- }
89
+ declare function getBindingsReady(root: Node): Promise<void>;
90
+ /** Applies `config` and registers `<wcs-state>`. The core only: install the add-ons first. */
91
+ declare function bootstrapState(partial?: IWritableConfig, registry?: CustomElementRegistry): void;
692
92
  /**
693
- * バインディング式のパース結果(DOM 非依存の部分)。`@wcstack/state/parser` の
694
- * ParseBindTextResult がこれをそのまま公開するため、Node 等の DOM lib 型を
695
- * ここに足してはならない(足すなら IBindingInfo 側へ)。
93
+ * Waits for the bindings under `root` (3.x built them here; the new engine builds them when the
94
+ * root's `<wcs-state>` loads its state, so this only waits — the same as getBindingsReady).
696
95
  */
697
- interface IParsedBinding {
698
- readonly propName: string;
699
- readonly propSegments: string[];
700
- readonly propModifiers: string[];
701
- readonly statePathName: string;
702
- readonly statePathInfo: IPathInfo;
703
- readonly inFilters: IParsedFilter[];
704
- readonly outFilters: IParsedFilter[];
705
- readonly bindingType: BindingType;
706
- readonly uuid?: string | null;
707
- }
96
+ declare const buildBindings: (root: Document | ShadowRoot) => Promise<void>;
708
97
 
709
- interface IState {
710
- [key: string]: any;
711
- }
712
- interface ITagNames {
713
- readonly state: string;
714
- readonly ssr: string;
715
- }
716
- interface IWritableTagNames {
717
- state?: string;
718
- ssr?: string;
719
- }
720
- interface IConfig {
721
- readonly bindAttributeName: string;
722
- readonly commentTextPrefix: string;
723
- readonly commentForPrefix: string;
724
- readonly commentIfPrefix: string;
725
- readonly commentElseIfPrefix: string;
726
- readonly commentElsePrefix: string;
727
- readonly tagNames: ITagNames;
728
- readonly locale: string;
729
- readonly debug: boolean;
730
- readonly enableMustache: boolean;
731
- /**
732
- * Enables direction-aware initial synchronization (`init=` / `sync=`).
733
- * Disabled by default while Phase 2 is evaluated against existing snapshots.
734
- */
735
- readonly enableDirectionalInitialSync: boolean;
736
- /**
737
- * Enables causal propagation tracking (transaction / edge provenance /
738
- * write receipts). Disabled by default while Phase 3 runs as a shadow of
739
- * the primitive same-value guard.
740
- */
741
- readonly enablePropagationContext: boolean;
742
- /**
743
- * Enables the opt-in dev-time contract analyzer (Phase 5b). When false
744
- * (default), `analyzeContract()` is a no-op with zero cost — runtime
745
- * behavior and allocation are unchanged. When true, it checks the actually
746
- * loaded `static wcBindable` declarations against a supplied sidecar
747
- * manifest and emits `contract:*` drift trace via the DevTools sink.
748
- */
749
- readonly enableContractAnalyzer: boolean;
750
- /**
751
- * 同値ガード(**既定 true**・標準的リアクティブ挙動・`setConfig({ sameValueGuard: false })` で opt-out 可)。
752
- * primitive 値の set で `Object.is` 同値なら更新を no-op にする
753
- * (enqueue / 依存 walk / DOM 適用 / $updatedCallback / DCC イベントを発火しない)。
754
- * 参照型(object/array)は in-place mutation 取りこぼし防止のため素通し。
755
- * 同値 set に副作用(同値時の $updatedCallback 等)を期待する場合は false にする。
756
- */
757
- readonly sameValueGuard: boolean;
758
- }
759
98
  /**
760
- * `$errorCallback(error, info)` の第 2 引数 — 適用に失敗したバインディングの識別情報。
761
- * 値と DOM は巻き戻されない(隔離規範)。作者はここで「どのバインディングが」を知り、
762
- * 自分の state にエラーを書いてページ内で受ける。
99
+ * Page-wide configuration (`bootstrapState(config)`): how this page spells wcstack markup (the tag
100
+ * names, the binding attribute, the anchor comments — fixed before the first definition, the same
101
+ * on the server) and the locale default. How a state tree behaves is its own `$behavior` (engine.ts).
763
102
  */
764
- interface IBindingErrorInfo {
765
- /** バインドされた state パス(`data-wcs` に書かれた形。`items.*.name` などワイルドカードのまま) */
766
- readonly path: string;
767
- /** バインディング種別(text / property / attribute / class / style / for / if …) */
768
- readonly bindingType: BindingType;
769
- /** バインディングが付いたノード(テキストバインドでは Text ノード) */
770
- readonly node: Node;
103
+ interface Config {
104
+ bindAttributeName: string;
105
+ /** The text of a `for` template's anchor comment. */
106
+ commentForPrefix: string;
107
+ /** The texts of an `if` / `elseif` / `else` chain's anchor comments. */
108
+ commentIfPrefix: string;
109
+ commentElseIfPrefix: string;
110
+ commentElsePrefix: string;
111
+ tagNames: {
112
+ state: string;
113
+ ssr: string;
114
+ };
115
+ locale: string;
116
+ /** Opt-in `analyzeContract()` (dev time); off, it returns at once. */
117
+ enableContractAnalyzer: boolean;
771
118
  }
772
- interface IWritableConfig {
773
- bindAttributeName?: string;
774
- /** @deprecated Removed in 4.0, whose `bootstrapState` throws on it (3.5 warns `wcs/v4-migration`). */
775
- commentTextPrefix?: string;
776
- commentForPrefix?: string;
777
- commentIfPrefix?: string;
778
- commentElseIfPrefix?: string;
779
- commentElsePrefix?: string;
780
- tagNames?: IWritableTagNames;
781
- locale?: string;
782
- /** @deprecated Removed in 4.0, whose `bootstrapState` throws on it (3.5 warns `wcs/v4-migration`). */
783
- debug?: boolean;
784
- /** Moves to the state's `$behavior` in 4.0 (3.5 warns `wcs/v4-migration`); in 3.x, set it here. */
785
- enableMustache?: boolean;
786
- /** Moves to the state's `$behavior` in 4.0 (3.5 warns `wcs/v4-migration`); in 3.x, set it here. */
787
- enableDirectionalInitialSync?: boolean;
788
- /** @deprecated Removed in 4.0, whose `bootstrapState` throws on it (3.5 warns `wcs/v4-migration`). */
789
- enablePropagationContext?: boolean;
790
- enableContractAnalyzer?: boolean;
791
- /** Moves to the state's `$behavior` in 4.0 (3.5 warns `wcs/v4-migration`); in 3.x, set it here. */
792
- sameValueGuard?: boolean;
793
- }
794
-
795
- declare function bootstrapCore(config?: IWritableConfig, registry?: CustomElementRegistry): void;
796
-
797
- declare function getConfig(): IConfig;
119
+ /** The current configuration (read-only view). */
120
+ declare const getConfig: () => Readonly<Config>;
798
121
 
799
122
  /**
800
- * 指定された rootNode のバインディング初期化が完了するまで待機する Promise を返す。
123
+ * Trusted Types for the HTML sinks (`html:`, `innerHTML:`, `outerHTML:`, `srcdoc:`).
124
+ * The policy lives on the same global slot as @wcstack/state, so a page that installed
125
+ * one for the current engine keeps working:
126
+ * globalThis[Symbol.for("wcstack.trustedTypes.policy")] = trustedTypes.createPolicy(…)
127
+ * No identity policy is ever created here — that would defeat the CSP.
801
128
  */
802
- declare function getBindingsReady(rootNode: Node): Promise<void>;
803
-
804
- declare function buildBindings(root: Document | ShadowRoot): Promise<void>;
805
-
806
- /**
807
- * Trusted Types (`require-trusted-types-for 'script'`) 対応。正本は docs/csp.md §7。
808
- *
809
- * state が HTML sink に流すのは **状態の値**(`innerHTML: path` などのプロパティ
810
- * バインド)で、ユーザー入力が混ざり得る文字列そのもの。ここに identity policy を
811
- * 噛ませて通すのは TT の無効化と同義なので、state は自前の policy を作らない。
812
- * 利用側が sanitizer を持つ policy を注入したときだけ通し、無ければ従来どおり
813
- * ブラウザに弾かせる(ただし何を設定すれば直るかは必ず言う)。
814
- *
815
- * 注入口は全 @wcstack パッケージ共通のグローバルスロット。buildless(CDN 一発)でも
816
- * inline script 1 本で差し込める:
817
- *
818
- * ```js
819
- * globalThis[Symbol.for("wcstack.trustedTypes.policy")] =
820
- * trustedTypes.createPolicy("my-app", { createHTML: (s) => DOMPurify.sanitize(s) });
821
- * ```
822
- *
823
- * バンドラ経由なら `setTrustedTypesPolicy()` を使う。値は毎回スロットから読むので
824
- * 後から差し替えても効く(identity policy を作る router / worker 側だけは
825
- * `createPolicy` の重複を避けるため生成結果をシングルトンで保持する)。
826
- */
827
- interface IWcsTrustedTypesPolicy {
129
+ interface TrustedTypesPolicy {
828
130
  createHTML?(input: string): unknown;
829
- createScriptURL?(input: string): unknown;
830
131
  }
831
- /** 利用側が policy を差し込むグローバルスロット(全 @wcstack パッケージ共通)。 */
832
132
  declare const TRUSTED_TYPES_POLICY_SLOT: unique symbol;
833
- /**
834
- * 利用側が注入した policy を返す。state はここに identity policy をフォールバック
835
- * させない(それをやると TT を無効化することになる)。
836
- */
837
- declare function getTrustedTypesPolicy(): IWcsTrustedTypesPolicy | null;
838
- /** 利用側 policy を設定する(`null` で解除)。最初のバインド適用前に呼ぶこと。 */
839
- declare function setTrustedTypesPolicy(policy: IWcsTrustedTypesPolicy | null): void;
133
+ declare function getTrustedTypesPolicy(): TrustedTypesPolicy | null;
134
+ declare function setTrustedTypesPolicy(policy: TrustedTypesPolicy | null): void;
840
135
 
841
136
  /**
842
137
  * defineState.ts
@@ -993,10 +288,6 @@ interface WcsStateApi {
993
288
  * 再評価させたくない場合に使う(該当行へ直接書き込む設計と組で用いる)。
994
289
  */
995
290
  $untracked<T>(fn: () => T): T;
996
- /** @deprecated `$dependOn` の旧名(3.x の間は動き、4.0 で外れる — 要件 B12) */
997
- $trackDependency(path: string): void;
998
- /** @deprecated `$untracked` の旧名(3.x の間は動き、4.0 で外れる — 要件 B12) */
999
- $untrackDependency<T>(fn: () => T): T;
1000
291
  /**
1001
292
  * 鍵付き購読: `path` の現在値が `key` に等しいかを返し、評価中のリスト行 getter を
1002
293
  * その鍵で購読する。`path` への書き込みは旧値・新値の鍵の行だけを再評価する
@@ -1075,7 +366,7 @@ type WcsThis<T> = T & WcsStateApi & WcsPathAccessor<T>;
1075
366
  *
1076
367
  * ### 基本的な使い方 (TypeScript)
1077
368
  * ```ts
1078
- * import { defineState } from '@wcstack/state';
369
+ * import { defineState } from '@wcstack/state/define';
1079
370
  *
1080
371
  * export default defineState({
1081
372
  * count: 0,
@@ -1094,7 +385,7 @@ type WcsThis<T> = T & WcsStateApi & WcsPathAccessor<T>;
1094
385
  *
1095
386
  * ### JavaScript (JSDoc)
1096
387
  * ```js
1097
- * import { defineState } from '@wcstack/state';
388
+ * import { defineState } from '@wcstack/state/define';
1098
389
  *
1099
390
  * export default defineState({
1100
391
  * count: 0,
@@ -1108,7 +399,7 @@ type WcsThis<T> = T & WcsStateApi & WcsPathAccessor<T>;
1108
399
  * ```html
1109
400
  * <wcs-state>
1110
401
  * <script type="module">
1111
- * import { defineState } from '@wcstack/state';
402
+ * import { defineState } from '@wcstack/state/define';
1112
403
  * export default defineState({
1113
404
  * count: 0,
1114
405
  * increment() { this.count++; }
@@ -1128,378 +419,33 @@ type WcsThis<T> = T & WcsStateApi & WcsPathAccessor<T>;
1128
419
  * // cleanup
1129
420
  * },
1130
421
  * $renderedCallback() {
1131
- * // called after the bindings are applied (old name: $updatedCallback)
422
+ * // called after the bindings are applied
1132
423
  * }
1133
424
  * });
1134
425
  * ```
1135
426
  */
1136
427
  declare function defineState<T extends Record<string, any>>(definition: T & ThisType<WcsThis<T>>): T;
1137
428
 
429
+ /** The package version: stamped into `<wcs-ssr>`, compared on hydration (major.minor), shown by DevTools. */
1138
430
  declare const VERSION: string;
1139
431
 
1140
432
  /**
1141
- * Browser builds use the native HTMLElement. Headless runtimes receive an
1142
- * inert base so the public module can be imported without installing DOM
1143
- * globals; constructing components remains a browser-only operation.
1144
- */
1145
- declare const HTMLElementBase: typeof HTMLElement;
1146
-
1147
- declare class State extends HTMLElementBase implements IStateElement {
1148
- static hasConnectedCallbackPromise: boolean;
1149
- static getBindingsReady(rootNode: Node): Promise<void>;
1150
- /**
1151
- * `mount` の動的変更は未サポート(再マウントは非目標 — 設計書 §4-7)。
1152
- * 初期化済み要素での変更は無言で捨てず warn で知らせる。初期化前の属性設定
1153
- * (パース時・接続前の setAttribute)は正規の使い方なので黙る。
1154
- * `name` は connectedCallback 冒頭で fail-fast 済みなので観測しない。
1155
- */
1156
- static get observedAttributes(): string[];
1157
- private __state;
1158
- private _hasUpdatedCallback;
1159
- /** $errorCallback の有無(_hasUpdatedCallback と同じく state セット時に確定。ルートのみ) */
1160
- private _hasErrorCallback;
1161
- /** enable-ssr のスナップショットから初期化された(D14: ボリュームはデータを採用する) */
1162
- private _hydratedFromSsr;
1163
- private _crossRowListPaths;
1164
- private _indexDependentGetterPaths;
1165
- private _initialized;
1166
- /**
1167
- * 初期化(`_initialize`)が失敗した(#257)。`_initialized` の裏返しではない —
1168
- * 「まだ初期化していない」と「もう初期化できない」を取り違えると、復旧不能の
1169
- * 要素に `setInitialState` が効いたように見える。真にするのは
1170
- * `_failInitializeLoudly` だけ。
1171
- */
1172
- private _initializeFailed;
1173
- /**
1174
- * この接続サイクルの失敗は**もう着地した**(#257)。設定エラーの fail-fast
1175
- * (`_failInitialization` と `initializeMountScope` の catch)は自分で promise を
1176
- * 解決してから raise する — connectedCallbackPromise を**解決**する側のクラスなので、
1177
- * 下の loud な着地(reject + 診断)に載せ替えると意味論が変わる。接続ごとに畳む。
1178
- */
1179
- private _initializationLanded;
1180
- private _initializePromise;
1181
- private _resolveInitialize;
1182
- private _connectedCallbackPromise;
1183
- private _resolveConnectedCallback;
1184
- private _rejectConnectedCallback;
1185
- private _loadingPromise;
1186
- private _resolveLoading;
1187
- private _setStatePromise;
1188
- private _resolveSetState;
1189
- private _listPaths;
1190
- private _listKeys;
1191
- private _recursionRegistry;
1192
- private _elementPaths;
1193
- private _getterPaths;
1194
- private _setterPaths;
1195
- private _loopContextStack;
1196
- private _dynamicDependency;
1197
- private _staticDependency;
1198
- private _pathSet;
1199
- /**
1200
- * state の世代(issue #258 の X10)。`_state` の差し替えごとに 1 つ進み、キャッシュ項目の
1201
- * 印になる(cache/types.ts の `generation`)。`_version`(更新サイクルの番号)とは別の
1202
- * カウンタで、増える条件が違う — 再セットは世代だけを進める
1203
- * (`__tests__/integration.stateGenerationReset.test.ts` の
1204
- * 「再セットは世代だけを進め、version は動かさない」が固定する)。
1205
- */
1206
- private _stateGeneration;
1207
- /**
1208
- * この要素が受け取った `setPathInfo` の台帳(issue #258 の X7)。パス → 種別と呼び出し元。
1209
- *
1210
- * `_pathSet` / `_listPaths` / `_elementPaths` は再セットでクリアされるが、それらを登録した
1211
- * バインドは生き残る(再セットは DOM を作り直さない)。台帳が空のままだと依存ウォークが
1212
- * `items` をリストとして展開できず、全リスト書き込みが「非リストのアドレスにワイルドカードを
1213
- * 展開できない」で恒久的に throw する(値と DOM は追従するが、書き込みのたびに投げ続ける)。
1214
- * クリアのあと、この台帳から作り直す(`_rebuildPathInfo`)。
1215
- *
1216
- * `source === "internal"`(再帰の生成アクセサ・ボリュームのツリーアクセサ)は載せない。
1217
- * あれらは生やした機構が新しい世代で登録し直す(recursion/registry.ts の `_define`)。
1218
- *
1219
- * `bound` はバインドがいちどでも登録したか(#270)。作り直しで存在検査をやり直すのはバインドの
1220
- * パスだけ — `$watch` / `$scan` の登録は前の世代の宣言の残骸でもあり、今の世代の宣言は
1221
- * 作り直しの後で自分の検査をする(`processWatchDeclaration` / `registerScans`)。残骸まで
1222
- * 検査すると、新しい宣言から外したパスを「存在しない」と誤って報告する。
1223
- */
1224
- private _pathRegistrations;
1225
- /**
1226
- * 切断中の再セットで適用し直せなかったトップレベルのパス(#267)。再適用には rootNode が要るので、
1227
- * 再接続で `reapplyStateBindings` に渡す。切断中に何度入れ直しても和集合で持つ
1228
- * (前の世代にしか無いキーのバインドも失敗として報告させるため)。
1229
- */
1230
- private _pendingReapplyPaths;
1231
- /**
1232
- * これまでのどの世代かで再帰レジストリが実体化した具体パス(`nodes.*.total` 等)の累積。
1233
- * 再セットのたびに `forgetGenerated` の戻り値を足し、`_rebuildPathInfo` の除外に使う。
1234
- *
1235
- * 除外は**世代を跨いで累積する**。読みを挟まない連続した再セットでも、生成アクセサの具体パスを
1236
- * 指す静的辺(`nodes.*` → `nodes.*.total`)が戻らないことは、
1237
- * `__tests__/integration.stateGenerationReset.test.ts` の
1238
- * 「読みを挟まない 3 連続の再セットで静的辺が戻らない」が固定する。
1239
- */
1240
- private _generatedPaths;
1241
- private _watchPaths;
1242
- private _scanPaths;
1243
- private _version;
1244
- private _rootNode;
1245
- private _boundComponent;
1246
- private _boundComponentStateProp;
1247
- private _hasMounts;
1248
- private _hasGraftedVolumes;
1249
- /** 読み書き境界の hook(設計案 H1)。宣言が要求する機能の分だけ付く */
1250
- private _addressHooks;
1251
- private _bindableEventMap;
1252
- private _commandTokenNames;
1253
- private _eventTokenNames;
1254
- private _treeless;
1255
- private _connectGeneration;
1256
- constructor();
1257
- private get _state();
1258
- private set _state(value);
1259
- attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void;
1260
- private _loadFromSsrElement;
1261
- /**
1262
- * state / src / json / inner <script> / API set のソース解決(_initialize とボリュームで共用)。
1263
- *
1264
- * A load failure is not wrapped, here or in the loaders: what was thrown propagates as is —
1265
- * the SyntaxError of `JSON.parse` (`state=` / `json=`), the `import()` rejection or the
1266
- * module's own throw (`src="*.js"`), the inline-script loader's Error (the import failure is
1267
- * its `cause`), the unsupported-extension error, or a non-Error value. On a root,
1268
- * `_failInitializeLoudly` logs it under the element's "failed to initialize" line and rejects
1269
- * connectedCallbackPromise with that same value (the README contract); on a volume,
1270
- * volumeLifecycle logs it under its "failed to load" line. The old wrappers
1271
- * (`Failed to initialize state: ${e}` here, `Failed to load script file` /
1272
- * `Failed to parse JSON from script element` in the loaders) were new Errors that kept the
1273
- * original only as text, losing its type, stack and cause.
1274
- *
1275
- * Two sources do not fail at all in 3.x (README): `src="*.json"` that cannot be fetched or
1276
- * parsed, and `state=` naming no JSON script. Both log and start with an empty state.
1277
- */
1278
- private _loadStateFromSource;
1279
- /**
1280
- * 初回マウントのロードと登録。戻り値は「この接続で初期化を**完了**したか」で、
1281
- * `false` は失敗ではなく**中断**(ロード中に要素が剥がされた — 下の注記)。
1282
- */
1283
- private _initialize;
1284
- /**
1285
- * 設定エラーでの fail-fast。initializePromise 等を解決してから raise する —
1286
- * 未解決のまま投げると waitForStateInitialize(ホストの buildBindings)が
1287
- * この要素を待ち続け、**ページ全体が無言でウェッジする**(1 つの設定ミスが
1288
- * 無関係なバインディングまで道連れにする)。エラー自体は unhandled rejection
1289
- * として loud に残る。
1290
- */
1291
- private _failInitialization;
1292
- /**
1293
- * `_initialize` の失敗の着地(#257)。旧挙動は「throw が connectedCallback の外へ
1294
- * 出るだけ」で、`_initializePromise` も `_connectedCallbackPromise` も永久に未解決の
1295
- * まま残り、作者が受け取るのは診断ではなく無言のハングだった。載るのは
1296
- * `_initialize` が投げうるもの全部 — `_state` セッタの宣言検証($recursion /
1297
- * $commandTokens / $eventTokens / $on / $streams / $listKeys / $watch)、
1298
- * `_loadStateFromSource` のロード失敗(src の拡張子・json のパース・内包スクリプト・
1299
- * 外部モジュール)、SSR データの merge、そして `setStateElement` の
1300
- * 「1 rootNode 1 ツリー」違反(**別の**要素が 2 本目に来た形 — 同じ要素の再登録は
1301
- * 冪等なので、ロード中の remove → append はここへ来ない)。
1302
- *
1303
- * 載**らない**もの: ロード中に要素が剥がされた形。作者のミスが 1 つも無いので
1304
- * 初期化失敗ではなく中断として扱う(`_initialize` が `false` を返す)。
1305
- *
1306
- * もう 1 つ載らないのがボリューム(`mount=`)の失敗。`_initializeVolume` の catch が
1307
- * 3 つの promise(initialize / loading / connectedCallback)を自分で解決してから raise し、
1308
- * `connectedCallback` のボリューム分岐はそれを包まないので、ボリュームはこの着地に
1309
- * 載らず connectedCallbackPromise を**拒否しない**。自前の報告が出るかどうかは失敗の
1310
- * 種類による。報告が無い形では、逃げ方は `name=`(`_failInitialization` の注記)と
1311
- * 同じで、throw はカスタム要素リアクションが捨てる戻り Promise へ出ていく
1312
- * (ブラウザのコンソールには "Uncaught (in promise)" として残るが、promise を待つ側
1313
- * = renderToString・mount・テストレシピには届かない)。失敗箇所ごとの正確な挙動は
1314
- * `__tests__/integration.initFailureDiagnostics.test.ts` が固定している。挙動はこの
1315
- * PR では変えない(枠の寿命は別 Issue)。
1316
- *
1317
- * `connectedCallback` が `_initialize` より前に await する 2 つ
1318
- * (DCC の接続 — dcc/dccLifecycle.ts が内部面の `failInitializeLoudly` で載せる — と、
1319
- * `bind-component` の preparing)の raise も同じ着地に載る。
1320
- * 特に「初期化に失敗した要素の再接続」は `bindWebComponent` → `setInitialState` の
1321
- * 復旧不能 raise でそこへ来るので、包まないと診断ゼロで素通りする。
1322
- *
1323
- * `_failInitialization`(設定エラーの fail-fast)との違いは 1 つ:
1324
- * **connectedCallbackPromise を reject する**。ここまで来た要素は state を 1 つも
1325
- * 持たない = このツリーは存在しない。resolve すると、それを待つ消費者
1326
- * (@wcstack/server の renderToString・@wcstack/testing の mount・README の
1327
- * テストレシピ)に「準備完了」と嘘をつく。下の SSR 経路(_rejectConnectedCallback)と
1328
- * 同じ規範で、そちらと同じく**元のエラーをそのまま**投げ直す。
1329
- *
1330
- * `initializePromise` は従来どおり**解決**する(reject しない)。
1331
- * `waitForStateInitialize` はページ中の全 `<wcs-state>` の initializePromise を
1332
- * `Promise.all` で待つので、reject にすると 1 要素の設定ミスが無関係な
1333
- * バインディングまで道連れになる(_failInitialization の注記と同じ理由)。
1334
- *
1335
- * 後始末はしない: `_initialized` を立てないので `disconnectedCallback` は初期化前
1336
- * ガードで抜ける。`$listKeys` / `$watch` のようにセッタの後半で落ちた形では
1337
- * `$on` の購読と stream registry が残るが、この要素は復旧不能(setInitialState が
1338
- * throw する)なので、残骸は要素ごと捨てる前提で放置する。
1339
- *
1340
- * `ownsTree` が false の着地は、この要素だけを失敗させてルートに触らない。`mount=` の
1341
- * readiness barrier(要件 D23)が使う: ボリュームはツリーの持ち主ではなく、ルートより先に
1342
- * 接続したボリュームでは rootNode にまだ誰も居ないので、既定の着地だとまだ来ていないルートの
1343
- * ノードを利用不能と印付けし、保留中の他のボリュームまで落としてしまう。
1344
- */
1345
- private _failInitializeLoudly;
1346
- private _callStateConnectedCallback;
1347
- private _callStateDisconnectedCallback;
1348
- connectedCallback(): Promise<void>;
1349
- disconnectedCallback(): void;
1350
- get initialized(): boolean;
1351
- /**
1352
- * ライフサイクル機能(core/lifecycleHooks.ts、設計案 H3)へ開く内部面。接続を引き取った機能が
1353
- * 要素の初期化を所有するために要る最小限。
1354
- */
1355
- get connectedRootNode(): Node | null;
1356
- clearConnectedRootNode(): void;
1357
- markInitialized(): void;
1358
- settleInitialization(): void;
1359
- loadStateFromSource(): Promise<Record<string, any>>;
1360
- markTreeless(): void;
1361
- /** 初期化失敗の着地(`_failInitializeLoudly`)。接続を引き取った機能が自分の失敗を載せる */
1362
- failInitializeLoudly(error: unknown): never;
1363
- get connectGeneration(): number;
1364
- setWatchPaths(paths: ReadonlySet<string> | null): void;
1365
- setScanPaths(paths: ReadonlySet<string> | null): void;
1366
- /** 設定エラーの着地(`_failInitialization` の raise を除いた部分)。引き取った機能が使う */
1367
- landInitialization(): void;
1368
- setRecursionRegistry(registry: RecursionRegistry | null): void;
1369
- addGeneratedPath(path: string): void;
1370
- setBoundComponent(component: Element | null, stateProp: string | null): void;
1371
- get initializePromise(): Promise<void>;
1372
- get connectedCallbackPromise(): Promise<void>;
1373
- get listPaths(): Set<string>;
1374
- get listKeys(): ListKeyMap | null;
1375
- get hasRecursion(): boolean;
1376
- get recursionRegistry(): RecursionRegistry | null;
1377
- get watchPaths(): ReadonlySet<string> | null;
1378
- get scanPaths(): ReadonlySet<string> | null;
1379
- get elementPaths(): Set<string>;
1380
- /**
1381
- * ボリューム(webComponent/volume.ts)のアクセサ登録: ツリーパスをキーにした
1382
- * quoted-path アクセサを state オブジェクトに定義し、getter / setter 台帳と
1383
- * 依存グラフに載せる。ルートのワイルドカード getter(`"children.*.label"`)と
1384
- * 同じ機構に乗るので、評価は pushAddress 下・依存はグラフに載る。
1385
- */
1386
- /** ボリュームの watch パスをホットパス用ゲート(watchPaths)へ合流させる。 */
1387
- addVolumeWatchPaths(paths: ReadonlySet<string>): void;
1388
- /** ボリュームの $listKeys(接頭辞翻訳済み)をルートの表へ合流させる。衝突は設定ミス。 */
1389
- mergeVolumeListKeys(entries: ReadonlyMap<string, ListKeySpec>): void;
1390
- /** ボリュームが $updatedCallback を持つとき、収集ゲートを開ける(apply/applyChange.ts)。 */
1391
- enableUpdatedCallback(): void;
1392
- /** enable-ssr スナップショットから初期化されたか(D14 — webComponent/volume.ts が読む)。 */
1393
- get hydratedFromSsr(): boolean;
1394
- addListPath(path: string): void;
1395
- findStateDescriptor(path: string): PropertyDescriptor | undefined;
1396
- defineTreeAccessor(path: string, descriptor: PropertyDescriptor): void;
1397
- get getterPaths(): Set<string>;
1398
- get setterPaths(): Set<string>;
1399
- get loopContextStack(): ILoopContextStack;
1400
- get dynamicDependency(): Map<string, string[]>;
1401
- get staticDependency(): Map<string, string[]>;
1402
- get version(): number;
1403
- /** state の世代(キャッシュ項目の印の正本 — cache/types.ts の `generation`)。 */
1404
- get stateGeneration(): number;
1405
- get rootNode(): Node;
1406
- get boundComponentStateProp(): string | null;
1407
- get hasMounts(): boolean;
1408
- /** 唯一の呼び手は webComponent/mount.ts の registerMountRecord(Phase 2)。 */
1409
- markHasMounts(): void;
1410
- get hasGraftedVolumes(): boolean;
1411
- get addressHooks(): IAttachedHooks | null;
1412
- /** 宣言 `declaration` が要求する機能 `feature` の hook をこの state に付ける(未 install なら throw、D13) */
1413
- attachAddressHooks(feature: string, declaration: string): void;
1414
- /** 唯一の呼び手は webComponent/volume.ts の graftVolume(D22 後段のガードが読む)。 */
1415
- markHasGraftedVolumes(): void;
1416
- /** ボリュームがこのルートに予約された(接ぎ木前でも、予約下の読みは undefined が正 — D22) */
1417
- markHasVolume(): void;
1418
- /** スコープ機能(マウント・ボリューム)の hook をこの state に付ける(冪等) */
1419
- private _attachScopeHooks;
1420
- get bindableEventMap(): Record<string, string>;
1421
- get commandTokenNames(): ReadonlySet<string>;
1422
- get eventTokenNames(): ReadonlySet<string>;
1423
- setBindableEventMap(map: Record<string, string>): void;
1424
- private _addDependency;
1425
- /**
1426
- * source, target
1427
- *
1428
- * products.*.price => products.*.tax
1429
- * get "products.*.tax"() { return this["products.*.price"] * 0.1; }
1430
- *
1431
- * products.*.price => products.summary
1432
- * get "products.summary"() { return this.$getAll("products.*.price", []).reduce(sum); }
1433
- *
1434
- * categories.*.name => categories.*.products.*.categoryName
1435
- * get "categories.*.products.*.categoryName"() { return this["categories.*.name"]; }
1436
- *
1437
- * @param sourcePath
1438
- * @param targetPath
1439
- */
1440
- addDynamicDependency(sourcePath: string, targetPath: string): boolean;
1441
- /**
1442
- * source, target
1443
- * products => products.*
1444
- * products.* => products.*.price
1445
- * products.* => products.*.name
1446
- *
1447
- * @param sourcePath
1448
- * @param targetPath
1449
- */
1450
- addStaticDependency(sourcePath: string, targetPath: string): boolean;
1451
- setPathInfo(path: string, bindingType: BindingType, source?: PathInfoSource): void;
1452
- /**
1453
- * 再セットで消した経路情報を、生き残ったバインドの登録から作り直す(issue #258 の X7)。
1454
- *
1455
- * `_pathSet.clear()` は残す。あのクリアには、`forgetGeneration` が外した静的辺を「行が
1456
- * 作り直されたときに登録し直させる」自己修復が乗っている(辺が戻ることは
1457
- * `__tests__/integration.stateGenerationReset.test.ts` の
1458
- * 「静的な辺 nodes.* → nodes.*.total が戻る」が固定する)。消したうえで、生きているバインド
1459
- * ぶんだけ `setPathInfo` をやり直す — `$recursion` のアンカーで既に同じことをしている手口を、
1460
- * バインド全体へ広げたもの。
1461
- *
1462
- * `_generatedPaths`(これまでの世代の生成アクセサの具体パス)は張り直さない。行バインドが
1463
- * 名指していても、新しい世代ではまだ実体化されていないため(recursion/generation.ts)。読みが
1464
- * 実体化したときに `defineTreeAccessor` が登録し直す。
1465
- *
1466
- * 作り直すのはバインドが登録したパスだけ(`_pathRegistrations` の `bound`・#270)。`$watch` /
1467
- * `$scan` だけの登録は飛ばす — 作り直しの後で今の世代の宣言が自分で登録し直し、そこで存在も
1468
- * 検査される。ここで `_pathSet` に入れてしまうと、宣言側の `setPathInfo` が `_pathSet.has` で
1469
- * 素通りし、両方の世代で宣言し続けたパスが新しい state で消えても報告されない。
1470
- *
1471
- * 反復中に `setPathInfo` が台帳へ書き戻す(既存キーの上書きのみで新キーは増えない)ので、
1472
- * 誤解を避けるためスナップショットを取ってから回す。
1473
- */
1474
- private _rebuildPathInfo;
1475
- private _createState;
1476
- createStateAsync(mutability: Mutability, callback: (state: IStateProxy) => Promise<void>): Promise<void>;
1477
- createState(mutability: Mutability, callback: (state: IStateProxy) => void): void;
1478
- nextVersion(): number;
1479
- get hasUpdatedCallback(): boolean;
1480
- get hasErrorCallback(): boolean;
1481
- get crossRowListPaths(): ReadonlySet<string>;
1482
- addCrossRowListPath(path: string): void;
1483
- get indexDependentGetterPaths(): ReadonlySet<string>;
1484
- addIndexDependentGetterPath(path: string): void;
1485
- setInitialState(state: Record<string, any>): void;
1486
- /** 確立済みのバインドを今の世代で適用し直す(#267)。行のバインドは経路情報の台帳のパスから引く。 */
1487
- private _reapplyBindings;
1488
- }
1489
-
1490
- /**
1491
- * entries/core.ts — `@wcstack/state/core`(設計案 §4)。
433
+ * `@wcstack/state/core` — the core of the split build (`dist/split/core.js`): a page that installs
434
+ * only the add-ons it uses imports this and `features/<name>` from the same build:
435
+ *
436
+ * import { bootstrapState, installFeatures } from "@wcstack/state/core";
437
+ * import temporal from "@wcstack/state/features/temporal";
438
+ * installFeatures([temporal]);
439
+ * bootstrapState();
1492
440
  *
1493
- * core だけの公開面。機能(watch / scan / streams・スコープ・再帰・SSR・devtools)は 1 つも
1494
- * import しない — ページが `installFeatures([...])` で入れる。full(`@wcstack/state`)は
1495
- * この面に機能と `Ssr` などを足したもので、公開 API は従来どおり。
441
+ * The same exports as 3.x's `/core`.
1496
442
  */
1497
443
 
1498
444
  declare global {
1499
445
  interface HTMLElementTagNameMap {
1500
- "wcs-state": State;
446
+ "wcs-state": IStateElement;
1501
447
  }
1502
448
  }
1503
449
 
1504
- export { TRUSTED_TYPES_POLICY_SLOT, VERSION, bootstrapCore as bootstrapState, buildBindings, defineState, getBindingsReady, getConfig, getTrustedTypesPolicy, setTrustedTypesPolicy };
1505
- export type { IBindingErrorInfo, IWcsTrustedTypesPolicy, IWritableConfig, IWritableTagNames, WcsPathValue, WcsPaths, WcsStateApi, WcsThis };
450
+ export { TRUSTED_TYPES_POLICY_SLOT, VERSION, bootstrapState, buildBindings, defineState, getBindingsReady, getConfig, getTrustedTypesPolicy, installFeatures, setTrustedTypesPolicy };
451
+ export type { IBindingErrorInfo, IStateFeature, TrustedTypesPolicy as IWcsTrustedTypesPolicy, IWritableConfig, IWritableTagNames, WcsPathValue, WcsPaths, WcsStateApi, WcsThis };