@wcstack/state 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +305 -18
- package/README.md +305 -18
- package/dist/auto.min.js +1 -1
- package/dist/auto.min.js.map +1 -1
- package/dist/index.d.ts +375 -4
- package/dist/index.esm.js +3797 -257
- package/dist/index.esm.js.map +1 -1
- package/dist/manifest.esm.js +4 -0
- package/dist/parser.esm.js +21 -4
- package/dist/wcs-manifest.json +2 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -68,9 +68,210 @@ type PathInfoSource =
|
|
|
68
68
|
"binding"
|
|
69
69
|
/** `$watch` の宣言キー */
|
|
70
70
|
| "watch"
|
|
71
|
+
/** `$scan` の `from` / `resetOn`(docs/state-scan-design.md §2-4) */
|
|
72
|
+
| "scan"
|
|
71
73
|
/** ランタイム内部のパス翻訳(mapped な bind-component の外向き伝播)。検査しない */
|
|
72
74
|
| "internal";
|
|
73
75
|
|
|
76
|
+
/**
|
|
77
|
+
* 単一の自己再帰宣言。初版はアンカーと反復サブパスとも「固定プロパティ列の末尾に
|
|
78
|
+
* `.*` がひとつ」の形に限定する(docs/state-recursive-path-impl-plan.md §1-1)。
|
|
79
|
+
*
|
|
80
|
+
* 例: `$recursion = { "nodes.*": "children.*" }`
|
|
81
|
+
* - `anchor` … `"nodes.*"`(深さ 0 のノードパス)
|
|
82
|
+
* - `repeat` … `"children.*"`(1 段深くする相対サブパス)
|
|
83
|
+
* - `recursiveAnchor` … `"nodes.**"`
|
|
84
|
+
* - `anchorList` … `"nodes"`
|
|
85
|
+
* - `repeatList` … `"children"`
|
|
86
|
+
*
|
|
87
|
+
* リスト側の 2 つは宣言時に確定させる(静的側の `RecursionSpec` と同じ構成)。
|
|
88
|
+
* 各所で `lastIndexOf(DELIMITER)` の slice を繰り返すと、綴りの取り違えが分散する。
|
|
89
|
+
*/
|
|
90
|
+
interface IRecursionSpec {
|
|
91
|
+
readonly anchor: string;
|
|
92
|
+
readonly repeat: string;
|
|
93
|
+
/** `anchor` の `**` 形(`"nodes.**"`)。オーサリング層のパス解析で使う。 */
|
|
94
|
+
readonly recursiveAnchor: string;
|
|
95
|
+
/** `anchor` のリスト側(`"nodes"` — 末尾の `.*` を落とした形)。 */
|
|
96
|
+
readonly anchorList: string;
|
|
97
|
+
/** `repeat` のリスト側(`"children"`)。 */
|
|
98
|
+
readonly repeatList: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 展開済みの再帰 getter 1 本ぶんの素性。生成アクセサに紐づくメタデータで、
|
|
102
|
+
* ランタイムが読むのは深さ(`**` の束縛)と元の宣言(診断の名指し)の 2 つだけ。
|
|
103
|
+
* 具体パスは台帳のキー、`PathInfo` は読む側が intern 済みのものを持つので、ここには
|
|
104
|
+
* 重ねて持たない。
|
|
105
|
+
*/
|
|
106
|
+
interface IRecursionAccessor {
|
|
107
|
+
/** 元の宣言(`"nodes.**.total"`) */
|
|
108
|
+
readonly recursivePath: string;
|
|
109
|
+
/** 反復の段数(0 origin) */
|
|
110
|
+
readonly depth: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* recursion/registry.ts
|
|
115
|
+
*
|
|
116
|
+
* state 1 つぶんの再帰レジストリ。宣言・`**` getter の定義・展開済みアクセサの台帳を
|
|
117
|
+
* 持ち、「具体パスを読む直前に、その深さのアクセサを生やす」遅延実体化を担う。
|
|
118
|
+
*
|
|
119
|
+
* 遅延であることは実装の**不変条件**である(Phase A の A6/A7)。そのパスを一度でも
|
|
120
|
+
* 読んでから生やしても、`isCacheable` が `wildcardCount > 0` だけでキャッシュ可を返す
|
|
121
|
+
* ため `undefined` が `dirty:false` で固定され、以後どう書いても回復しない。
|
|
122
|
+
* したがって実体化は `getByAddress` のキャッシュ参照**前**に置く(E5)。
|
|
123
|
+
*
|
|
124
|
+
* 寿命は state の世代と共にする。`_state` の再セットで `getterPaths` / `listPaths` は
|
|
125
|
+
* クリアされるので、レジストリも作り直す(§1-3)。ただし**生やしたアクセサは state
|
|
126
|
+
* オブジェクトの側に残る**ので、同じ state を再セットすると `getStateInfo` がそれを
|
|
127
|
+
* `getterPaths` に復元する。そのとき「もう生えているから何もしない」と早期 return して
|
|
128
|
+
* しまうと `listPaths` の登録だけが抜け落ちるため、生成物は WeakSet で見分けて
|
|
129
|
+
* 登録だけをやり直す。
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
declare class RecursionRegistry {
|
|
133
|
+
readonly spec: IRecursionSpec;
|
|
134
|
+
private readonly _definitions;
|
|
135
|
+
private readonly _accessors;
|
|
136
|
+
/**
|
|
137
|
+
* `recursiveGetterOwning` の記憶。キーは添字を `*` に畳んだ形(`nodes.1.total` と `nodes.2.total`
|
|
138
|
+
* は 1 つ)、値は「その具体パスを展開形(またはその値の内側)として持つ `**` getter」、
|
|
139
|
+
* 無ければ null。
|
|
140
|
+
*
|
|
141
|
+
* 有界である: キーは添字を畳んだワイルドカード形のパス文字列で、`PathInfo` が intern する集合
|
|
142
|
+
* (バインディング・getter・API 引数に綴られたパスと、その展開形)の部分集合にしかならない。
|
|
143
|
+
* intern 済みパスの集合が有界であることは D10 で受け入れ済みなので、ここも同じ上限に収まる。
|
|
144
|
+
* 文字列は WeakSet に入らないので、寿命はレジストリ(= state の世代)と共にする。
|
|
145
|
+
*/
|
|
146
|
+
private readonly _ownerByPath;
|
|
147
|
+
/**
|
|
148
|
+
* 書き込みのホットパス(`setByAddress`)向けの記憶。キーは intern 済みの `PathInfo` なので
|
|
149
|
+
* 寿命と上限は PathInfo の intern 集合と同じ(WeakMap)。畳み(split + Number + join)は
|
|
150
|
+
* miss のときだけ払う — 宣言のある state では**アンカー外を含む全書き込み**がここを通る
|
|
151
|
+
* (第 4 サイクルで実測: 畳みを毎回払うと `s.counter = i` で +100ns/書き込み)。
|
|
152
|
+
*/
|
|
153
|
+
private readonly _ownerByPathInfo;
|
|
154
|
+
/**
|
|
155
|
+
* 読みのホットパス(`getByAddress`)向けの記憶。`_ownerByPathInfo` と対称で、キーは
|
|
156
|
+
* intern 済みの `PathInfo`、値は「そのパスの展開アクセサ」、展開形でなければ null。
|
|
157
|
+
* 宣言のある state では**アンカー外を含む全読み**(親ウォークの各段を含む)がここを
|
|
158
|
+
* 通るので、文字列キーの `Map.get` + `Set.has` + `startsWith` を毎回払わせない
|
|
159
|
+
* (第 5 サイクルで実測)。
|
|
160
|
+
*
|
|
161
|
+
* 読みの否定判定の記憶は**ここ 1 つ**(第 5 サイクル再検証で文字列キーの `_nonAccessors` を撤去 —
|
|
162
|
+
* 前段にこの記憶を置いた後は、PathInfo とパス文字列が 1:1 なので二重に持つだけだった)。
|
|
163
|
+
* 否定を記憶してよい根拠は、定義集合が state の世代内で不変であること —
|
|
164
|
+
* 同じ `PathInfo` は同じパス文字列なので、いちど「展開形でない」と決まった PathInfo が
|
|
165
|
+
* 後から実体化されることはない。実体化した側は `materializeForPathInfo` が
|
|
166
|
+
* `_define` の戻り値でそのまま記憶を更新する(否定が実体化を隠さない)。
|
|
167
|
+
*/
|
|
168
|
+
private readonly _accessorByPathInfo;
|
|
169
|
+
/** `concretePathAt` の記憶(接尾辞 → 深さ順の具体パス)。 */
|
|
170
|
+
private readonly _concreteBySuffix;
|
|
171
|
+
private readonly _registeredListPaths;
|
|
172
|
+
constructor(spec: IRecursionSpec, state: object);
|
|
173
|
+
/**
|
|
174
|
+
* 作者が手で書いた具体パス(`get "nodes.*.children.*.total"()` / データプロパティ)が、宣言済み
|
|
175
|
+
* `**` getter の展開形と同名でないことを**構築時に**確かめる。
|
|
176
|
+
*
|
|
177
|
+
* `_define` の衝突検査は「その深さを最初に読んだとき」にしか走らないので、データが浅い間は
|
|
178
|
+
* 通り、木が 1 段深くなった瞬間にバインディングが落ちていた(第 3 サイクルのレビューで実測)。
|
|
179
|
+
* 前世代の生成物(own に残った生成 getter)は衝突ではない — 同じ state の再セットで必ず居る。
|
|
180
|
+
*/
|
|
181
|
+
private _assertNoConcreteCollision;
|
|
182
|
+
/**
|
|
183
|
+
* 2 本の `**` getter が同じ具体パスへ展開しないことを、宣言だけから静的に確かめる。
|
|
184
|
+
*
|
|
185
|
+
* 衝突するのは「片方の接尾辞がもう片方の接尾辞の末尾で、差分が反復語の整数倍」の
|
|
186
|
+
* ときだけ(`nodes.**.total` と `nodes.**.children.*.total` は深さ k と k+1 で
|
|
187
|
+
* 同じ `nodes.*.children.*.total` になる)。検出しないと `_definitions` の挿入順で
|
|
188
|
+
* 最初に一致した方が無言で勝つ。
|
|
189
|
+
*/
|
|
190
|
+
private _assertNoColliding;
|
|
191
|
+
/**
|
|
192
|
+
* `**` getter を 1 本でも宣言しているか。
|
|
193
|
+
* **テスト・診断専用**(ランタイムの経路は `_definitions.size` を直接見る)。
|
|
194
|
+
*/
|
|
195
|
+
get hasDefinitions(): boolean;
|
|
196
|
+
/**
|
|
197
|
+
* その接尾辞が宣言済みの `**` getter と衝突するなら、その getter のパスを返す。
|
|
198
|
+
*
|
|
199
|
+
* 完全一致だけでは足りない。①反復語の整数倍だけ違う接尾辞は同じ族を指す
|
|
200
|
+
* (`_assertNoColliding` が宣言どうしについて既に見ている条件)②getter の**下**を
|
|
201
|
+
* 指す形(`nodes.**.total.x` / 反復語ぶんずれた `nodes.**.children.*.total.x`)は、
|
|
202
|
+
* getter が返したオブジェクトへ書いてキャッシュを汚し、次の無効化で無言に戻る。
|
|
203
|
+
* どちらも書き込みの入口(列挙より前)で止める — 述語は expand.ts の `coversSuffix`。
|
|
204
|
+
*/
|
|
205
|
+
conflictingRecursiveGetter(suffix: string): string | null;
|
|
206
|
+
/**
|
|
207
|
+
* `recursiveGetterOwning` の intern 済み `PathInfo` 版(書き込みのホットパス用)。
|
|
208
|
+
* WeakMap の hit なら畳みも照合も払わない。
|
|
209
|
+
*/
|
|
210
|
+
recursiveGetterOwningPath(pathInfo: IPathInfo): string | null;
|
|
211
|
+
/**
|
|
212
|
+
* 具体パスを展開形(またはその値の内側)として持つ `**` getter のパス。無ければ null。
|
|
213
|
+
* **実体化はしない。**
|
|
214
|
+
*
|
|
215
|
+
* `conflictingRecursiveGetter` の**具体パス版**で、`**` を経ない 2 つの入口が使う:
|
|
216
|
+
*
|
|
217
|
+
* - バインド確立時のパス存在検査(`checkDeclaredPath`)。あの時点ではまだ生えて
|
|
218
|
+
* いないので、素の存在検査では必ず「解決できない」になる。展開形そのもの
|
|
219
|
+
* (`nodes.*.total`)だけでなく、その値の中を指す形(`nodes.*.stats.count` で
|
|
220
|
+
* `get "nodes.**.stats"()` がオブジェクトを返す)も、通常の getter の下と同じく
|
|
221
|
+
* 評価しないと分からないので黙る側に倒す。
|
|
222
|
+
* - 書き込みの入口(`setByAddress`)。`$setAll("nodes.*.children.*.total", [], v)` や
|
|
223
|
+
* `this["nodes.1.total"] = v` は `**` を含まないので `setAllRecursive` の
|
|
224
|
+
* 読み取り専用検査を通らず、未実体化なら fast path が行オブジェクトへ素の
|
|
225
|
+
* プロパティとして書いてしまう(ノードを汚し、代入値が `dirty:false` で載って
|
|
226
|
+
* 以後 getter が評価されない)。展開形への書き込みは、実体化の前後に関わらず
|
|
227
|
+
* `wcs/recursion-readonly` で止める。
|
|
228
|
+
*/
|
|
229
|
+
recursiveGetterOwning(concretePath: string): string | null;
|
|
230
|
+
/** 具体パスが宣言済み `**` getter の展開形そのものなら、その getter のパス。 */
|
|
231
|
+
private _matchExpansion;
|
|
232
|
+
/**
|
|
233
|
+
* `materializeFor` の `PathInfo` 版。**読みのホットパス(`getByAddress`)専用**で、
|
|
234
|
+
* 判定そのものは `materializeFor` に委ね、結果(否定を含む)を PathInfo に記憶する。
|
|
235
|
+
* 書き側の `recursiveGetterOwningPath` と対称。
|
|
236
|
+
*/
|
|
237
|
+
materializeForPathInfo(stateElement: IStateElement, pathInfo: IPathInfo): IRecursionAccessor | null;
|
|
238
|
+
/**
|
|
239
|
+
* 具体パスが再帰 getter の展開形なら、そのアクセサを(未登録なら生やして)返す。
|
|
240
|
+
* 該当しなければ null。読みは `materializeForPathInfo` を通るので、ここへ来るのは
|
|
241
|
+
* 記憶が外れたときだけ — 判定は接頭辞 1 回で抜け、ここでは否定を記憶しない(記憶は
|
|
242
|
+
* `materializeForPathInfo` の PathInfo キーの 1 か所)。
|
|
243
|
+
* (`**` getter の無い空レジストリを弾くのは呼び出し側の役目。)
|
|
244
|
+
*/
|
|
245
|
+
materializeFor(stateElement: IStateElement, concretePath: string): IRecursionAccessor | null;
|
|
246
|
+
private _define;
|
|
247
|
+
/**
|
|
248
|
+
* 経路上のリストパスを `listPaths` に載せる(E4)。`setPathInfo(path, "for")` は
|
|
249
|
+
* 使えない — あちらは `elementPaths` にも入れて `setByAddress` の swap 経路
|
|
250
|
+
* (`isSwappable`)を変えてしまう。ここで要るのは「依存ウォークがこのパスを
|
|
251
|
+
* リストとして展開する」ことだけ。
|
|
252
|
+
*/
|
|
253
|
+
private _registerListPaths;
|
|
254
|
+
/**
|
|
255
|
+
* この世代が生やしたもの(own の生成アクセサ・依存辺・キャッシュ)を忘れる(state の
|
|
256
|
+
* 再セット時、`getStateInfo` の再収集より**前**に呼ぶ)。実体は generation.ts。
|
|
257
|
+
*/
|
|
258
|
+
forgetGenerated(stateElement: IStateElement, previousState: object): ReadonlySet<string>;
|
|
259
|
+
/**
|
|
260
|
+
* `**` 接尾辞の深さ `depth` の具体パス(`concretePathAt` の記憶付き版)。
|
|
261
|
+
* 束縛形の読み(`this["nodes.**.value"]` / 省略形 `$getAll`)は再帰 getter の評価ごとに
|
|
262
|
+
* ここを通るので、深さぶんの文字列連結とワイルドカード数えを毎回やり直さない。
|
|
263
|
+
* 上限は「接尾辞の種類 × 128」で有界(上限超過は `concretePathAt` が throw するので載らない)。
|
|
264
|
+
*/
|
|
265
|
+
concretePathAt(suffix: string, depth: number): string;
|
|
266
|
+
/** 展開済みアクセサのメタデータ(深さ解決・診断・テスト用)。 */
|
|
267
|
+
accessorFor(concretePath: string): IRecursionAccessor | null;
|
|
268
|
+
/**
|
|
269
|
+
* これまでに実体化した具体パスの一覧。**テスト専用**(「読んだ深さだけが生える」という
|
|
270
|
+
* 遅延実体化の不変条件を外から確かめる口。ランタイムはどの経路からも呼ばない)。
|
|
271
|
+
*/
|
|
272
|
+
get materializedPaths(): ReadonlySet<string>;
|
|
273
|
+
}
|
|
274
|
+
|
|
74
275
|
declare const setLoopContextSymbol: unique symbol;
|
|
75
276
|
declare const getByAddressSymbol: unique symbol;
|
|
76
277
|
declare const hasByAddressSymbol: unique symbol;
|
|
@@ -111,6 +312,14 @@ interface IStateElement {
|
|
|
111
312
|
readonly dynamicDependency: Map<string, string[]>;
|
|
112
313
|
readonly staticDependency: Map<string, string[]>;
|
|
113
314
|
readonly version: number;
|
|
315
|
+
/**
|
|
316
|
+
* state の世代。`_state` の差し替えごとに 1 つ進み、キャッシュ項目の印になる
|
|
317
|
+
* (cache/types.ts の `generation`)。`version`(更新サイクルの番号)とは別のカウンタ。
|
|
318
|
+
* optional なのはテスト用モック互換のため(undefined のモックが載せた項目は
|
|
319
|
+
* undefined 同士で一致し、従来どおりヒットする — `__tests__/proxy.getByAddress.test.ts`
|
|
320
|
+
* の「キャッシュがある場合はキャッシュを返すこと」が固定する)。
|
|
321
|
+
*/
|
|
322
|
+
readonly stateGeneration?: number;
|
|
114
323
|
readonly rootNode: Node;
|
|
115
324
|
readonly boundComponentStateProp: string | null;
|
|
116
325
|
/**
|
|
@@ -196,6 +405,12 @@ interface IStateElement {
|
|
|
196
405
|
* optional なのはテスト用モック互換のため(undefined は「宣言なし」扱い)。
|
|
197
406
|
*/
|
|
198
407
|
readonly watchPaths?: ReadonlySet<string> | null;
|
|
408
|
+
/**
|
|
409
|
+
* `$scan` の `from` パスの集合(docs/state-scan-design.md §2-1)。`watchPaths` と並んで
|
|
410
|
+
* setByAddress の旧値キャプチャのゲートになる(fold に `prev` を渡すため)。
|
|
411
|
+
* 宣言が無ければ null / undefined。optional なのはテスト用モック互換のため。
|
|
412
|
+
*/
|
|
413
|
+
readonly scanPaths?: ReadonlySet<string> | null;
|
|
199
414
|
/**
|
|
200
415
|
* パスを依存グラフへ登録する。DOM バインディング登録(BindingSession)のほか、
|
|
201
416
|
* `$watch` 宣言(processWatchDeclaration)からも呼ばれる — 静的依存グラフに
|
|
@@ -212,6 +427,32 @@ interface IStateElement {
|
|
|
212
427
|
readonly hydratedFromSsr?: boolean;
|
|
213
428
|
/** ボリュームのアクセサ登録(webComponent/volume.ts 専用) */
|
|
214
429
|
defineTreeAccessor(path: string, descriptor: PropertyDescriptor): void;
|
|
430
|
+
/**
|
|
431
|
+
* リストパスとしてだけ登録する(`listPaths` に足す)。`setPathInfo(path, "for")` は
|
|
432
|
+
* `elementPaths` にも入れて `setByAddress` の swap 経路(`isSwappable`)を変えるので、
|
|
433
|
+
* 「依存ウォークがこのパスをリストとして展開する」ことだけが要る用途には使えない
|
|
434
|
+
* (docs/state-recursive-path-impl-plan.md §3-2 の E4)。
|
|
435
|
+
*/
|
|
436
|
+
addListPath(path: string): void;
|
|
437
|
+
/**
|
|
438
|
+
* state オブジェクト自身+プロトタイプチェーンから descriptor を引く(生成物と作者定義の
|
|
439
|
+
* 見分けに使う)。class 構文の getter は prototype に載るので own だけでは足りない。
|
|
440
|
+
*/
|
|
441
|
+
findStateDescriptor(path: string): PropertyDescriptor | undefined;
|
|
442
|
+
/**
|
|
443
|
+
* この state に `$recursion` 宣言があるか。偽のとき getByAddress の遅延実体化と
|
|
444
|
+
* get トラップの `**` 解決は boolean 判定 1 個で抜ける(hasMounts と同じ D18 の形)。
|
|
445
|
+
* 読み手は必ずこのゲートを先に見て、真なら `recursionRegistry` を `!` で読む。
|
|
446
|
+
* 必須メンバー(実装は `State` のみ)。`any` 型のテスト用モックがフィールドを持たなくても
|
|
447
|
+
* 通るのは vitest が型検査をしないからで、その場合 `undefined === true` は偽なので再帰の
|
|
448
|
+
* 経路に入らないだけ — 型としては必須。
|
|
449
|
+
*/
|
|
450
|
+
readonly hasRecursion: boolean;
|
|
451
|
+
/**
|
|
452
|
+
* 再帰レジストリ(宣言が無ければ null。`hasRecursion === true` なら非 null)。registry.ts は
|
|
453
|
+
* このファイルの IStateElement を参照するが、`import type` どうしなので実行時の循環にはならない。
|
|
454
|
+
*/
|
|
455
|
+
readonly recursionRegistry: RecursionRegistry | null;
|
|
215
456
|
setPathInfo(path: string, bindingType: BindingType, source?: PathInfoSource): void;
|
|
216
457
|
addStaticDependency(parentPath: string, childPath: string): boolean;
|
|
217
458
|
addDynamicDependency(fromPath: string, toPath: string): boolean;
|
|
@@ -683,6 +924,8 @@ interface WcsStateApi {
|
|
|
683
924
|
readonly $7: number;
|
|
684
925
|
readonly $8: number;
|
|
685
926
|
readonly $9: number;
|
|
927
|
+
readonly [key: `${string}.**.${string}`]: any;
|
|
928
|
+
readonly [key: `${string}.**`]: any;
|
|
686
929
|
}
|
|
687
930
|
/**
|
|
688
931
|
* state定義オブジェクト内の `this` の型。
|
|
@@ -933,9 +1176,13 @@ type DevtoolsEvent = {
|
|
|
933
1176
|
readonly stateElement?: IStateElement;
|
|
934
1177
|
} | {
|
|
935
1178
|
readonly type: "state:watch-error";
|
|
936
|
-
/**
|
|
937
|
-
|
|
938
|
-
|
|
1179
|
+
/**
|
|
1180
|
+
* throw 元。cur の評価(getter)とハンドラ本体では原因も直し方も違う。
|
|
1181
|
+
* `$scan`(scan/scanReport.ts)は `evaluate`(source / 出力の読み)・`fold`(fold の throw・
|
|
1182
|
+
* Promise の戻り値・接ぎ木で getter になった from)・`write`(出力の書き込み)を使う
|
|
1183
|
+
*/
|
|
1184
|
+
readonly phase: "prime" | "evaluate" | "handler" | "fold" | "write";
|
|
1185
|
+
/** `$watch` の宣言キー(ワイルドカードを含む生のパス)。`$scan` は `$scan.<出力名>` */
|
|
939
1186
|
readonly path: string;
|
|
940
1187
|
readonly error: unknown;
|
|
941
1188
|
} | {
|
|
@@ -957,7 +1204,7 @@ type DevtoolsEvent = {
|
|
|
957
1204
|
} | {
|
|
958
1205
|
readonly type: "state:path-unresolved";
|
|
959
1206
|
/** 書き手が書いた面。診断 code が binding / watch で変わる */
|
|
960
|
-
readonly source: "binding" | "watch";
|
|
1207
|
+
readonly source: "binding" | "watch" | "scan";
|
|
961
1208
|
/** 宣言されたパス(ワイルドカードを含む生の文字列) */
|
|
962
1209
|
readonly path: string;
|
|
963
1210
|
/** 解決に失敗したセグメント */
|
|
@@ -1075,6 +1322,20 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1075
1322
|
private _crossRowListPaths;
|
|
1076
1323
|
private _indexDependentGetterPaths;
|
|
1077
1324
|
private _initialized;
|
|
1325
|
+
/**
|
|
1326
|
+
* 初期化(`_initialize`)が失敗した(#257)。`_initialized` の裏返しではない —
|
|
1327
|
+
* 「まだ初期化していない」と「もう初期化できない」を取り違えると、復旧不能の
|
|
1328
|
+
* 要素に `setInitialState` が効いたように見える。真にするのは
|
|
1329
|
+
* `_failInitializeLoudly` だけ。
|
|
1330
|
+
*/
|
|
1331
|
+
private _initializeFailed;
|
|
1332
|
+
/**
|
|
1333
|
+
* この接続サイクルの失敗は**もう着地した**(#257)。設定エラーの fail-fast
|
|
1334
|
+
* (`_failInitialization` と `initializeMountScope` の catch)は自分で promise を
|
|
1335
|
+
* 解決してから raise する — connectedCallbackPromise を**解決**する側のクラスなので、
|
|
1336
|
+
* 下の loud な着地(reject + 診断)に載せ替えると意味論が変わる。接続ごとに畳む。
|
|
1337
|
+
*/
|
|
1338
|
+
private _initializationLanded;
|
|
1078
1339
|
private _initializePromise;
|
|
1079
1340
|
private _resolveInitialize;
|
|
1080
1341
|
private _connectedCallbackPromise;
|
|
@@ -1086,6 +1347,7 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1086
1347
|
private _resolveSetState;
|
|
1087
1348
|
private _listPaths;
|
|
1088
1349
|
private _listKeys;
|
|
1350
|
+
private _recursionRegistry;
|
|
1089
1351
|
private _elementPaths;
|
|
1090
1352
|
private _getterPaths;
|
|
1091
1353
|
private _setterPaths;
|
|
@@ -1093,7 +1355,39 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1093
1355
|
private _dynamicDependency;
|
|
1094
1356
|
private _staticDependency;
|
|
1095
1357
|
private _pathSet;
|
|
1358
|
+
/**
|
|
1359
|
+
* state の世代(issue #258 の X10)。`_state` の差し替えごとに 1 つ進み、キャッシュ項目の
|
|
1360
|
+
* 印になる(cache/types.ts の `generation`)。`_version`(更新サイクルの番号)とは別の
|
|
1361
|
+
* カウンタで、増える条件が違う — 再セットは世代だけを進める
|
|
1362
|
+
* (`__tests__/integration.stateGenerationReset.test.ts` の
|
|
1363
|
+
* 「再セットは世代だけを進め、version は動かさない」が固定する)。
|
|
1364
|
+
*/
|
|
1365
|
+
private _stateGeneration;
|
|
1366
|
+
/**
|
|
1367
|
+
* この要素が受け取った `setPathInfo` の台帳(issue #258 の X7)。パス → 種別と呼び出し元。
|
|
1368
|
+
*
|
|
1369
|
+
* `_pathSet` / `_listPaths` / `_elementPaths` は再セットでクリアされるが、それらを登録した
|
|
1370
|
+
* バインドは生き残る(再セットは DOM を作り直さない)。台帳が空のままだと依存ウォークが
|
|
1371
|
+
* `items` をリストとして展開できず、全リスト書き込みが「非リストのアドレスにワイルドカードを
|
|
1372
|
+
* 展開できない」で恒久的に throw する(値と DOM は追従するが、書き込みのたびに投げ続ける)。
|
|
1373
|
+
* クリアのあと、この台帳から作り直す(`_rebuildPathInfo`)。
|
|
1374
|
+
*
|
|
1375
|
+
* `source === "internal"`(再帰の生成アクセサ・ボリュームのツリーアクセサ)は載せない。
|
|
1376
|
+
* あれらは生やした機構が新しい世代で登録し直す(recursion/registry.ts の `_define`)。
|
|
1377
|
+
*/
|
|
1378
|
+
private _pathRegistrations;
|
|
1379
|
+
/**
|
|
1380
|
+
* これまでのどの世代かで再帰レジストリが実体化した具体パス(`nodes.*.total` 等)の累積。
|
|
1381
|
+
* 再セットのたびに `forgetGenerated` の戻り値を足し、`_rebuildPathInfo` の除外に使う。
|
|
1382
|
+
*
|
|
1383
|
+
* 除外は**世代を跨いで累積する**。読みを挟まない連続した再セットでも、生成アクセサの具体パスを
|
|
1384
|
+
* 指す静的辺(`nodes.*` → `nodes.*.total`)が戻らないことは、
|
|
1385
|
+
* `__tests__/integration.stateGenerationReset.test.ts` の
|
|
1386
|
+
* 「読みを挟まない 3 連続の再セットで静的辺が戻らない」が固定する。
|
|
1387
|
+
*/
|
|
1388
|
+
private _generatedPaths;
|
|
1096
1389
|
private _watchPaths;
|
|
1390
|
+
private _scanPaths;
|
|
1097
1391
|
private _version;
|
|
1098
1392
|
private _rootNode;
|
|
1099
1393
|
private _boundComponent;
|
|
@@ -1125,6 +1419,10 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1125
1419
|
* ルートより先に接続されてもよい — ルート登録が保留分を引き取る(V5)。
|
|
1126
1420
|
*/
|
|
1127
1421
|
private _initializeVolume;
|
|
1422
|
+
/**
|
|
1423
|
+
* 初回マウントのロードと登録。戻り値は「この接続で初期化を**完了**したか」で、
|
|
1424
|
+
* `false` は失敗ではなく**中断**(ロード中に要素が剥がされた — 下の注記)。
|
|
1425
|
+
*/
|
|
1128
1426
|
private _initialize;
|
|
1129
1427
|
/**
|
|
1130
1428
|
* 設定エラーでの fail-fast。initializePromise 等を解決してから raise する —
|
|
@@ -1134,6 +1432,54 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1134
1432
|
* として loud に残る。
|
|
1135
1433
|
*/
|
|
1136
1434
|
private _failInitialization;
|
|
1435
|
+
/**
|
|
1436
|
+
* `_initialize` の失敗の着地(#257)。旧挙動は「throw が connectedCallback の外へ
|
|
1437
|
+
* 出るだけ」で、`_initializePromise` も `_connectedCallbackPromise` も永久に未解決の
|
|
1438
|
+
* まま残り、作者が受け取るのは診断ではなく無言のハングだった。載るのは
|
|
1439
|
+
* `_initialize` が投げうるもの全部 — `_state` セッタの宣言検証($recursion /
|
|
1440
|
+
* $commandTokens / $eventTokens / $on / $streams / $listKeys / $watch)、
|
|
1441
|
+
* `_loadStateFromSource` のロード失敗(src の拡張子・json のパース・内包スクリプト・
|
|
1442
|
+
* 外部モジュール)、SSR データの merge、そして `setStateElement` の
|
|
1443
|
+
* 「1 rootNode 1 ツリー」違反(**別の**要素が 2 本目に来た形 — 同じ要素の再登録は
|
|
1444
|
+
* 冪等なので、ロード中の remove → append はここへ来ない)。
|
|
1445
|
+
*
|
|
1446
|
+
* 載**らない**もの: ロード中に要素が剥がされた形。作者のミスが 1 つも無いので
|
|
1447
|
+
* 初期化失敗ではなく中断として扱う(`_initialize` が `false` を返す)。
|
|
1448
|
+
*
|
|
1449
|
+
* もう 1 つ載らないのがボリューム(`mount=`)の失敗。`_initializeVolume` の catch が
|
|
1450
|
+
* 3 つの promise(initialize / loading / connectedCallback)を自分で解決してから raise し、
|
|
1451
|
+
* `connectedCallback` のボリューム分岐はそれを包まないので、ボリュームはこの着地に
|
|
1452
|
+
* 載らず connectedCallbackPromise を**拒否しない**。自前の報告が出るかどうかは失敗の
|
|
1453
|
+
* 種類による。報告が無い形では、逃げ方は `name=`(`_failInitialization` の注記)と
|
|
1454
|
+
* 同じで、throw はカスタム要素リアクションが捨てる戻り Promise へ出ていく
|
|
1455
|
+
* (ブラウザのコンソールには "Uncaught (in promise)" として残るが、promise を待つ側
|
|
1456
|
+
* = renderToString・mount・テストレシピには届かない)。失敗箇所ごとの正確な挙動は
|
|
1457
|
+
* `__tests__/integration.initFailureDiagnostics.test.ts` が固定している。挙動はこの
|
|
1458
|
+
* PR では変えない(枠の寿命は別 Issue)。
|
|
1459
|
+
*
|
|
1460
|
+
* `connectedCallback` が `_initialize` より前に await する 2 つ
|
|
1461
|
+
* (`_initializeDCC` / `_initializeBindWebComponent`)の raise も同じ着地に載る。
|
|
1462
|
+
* 特に「初期化に失敗した要素の再接続」は `bindWebComponent` → `setInitialState` の
|
|
1463
|
+
* 復旧不能 raise でそこへ来るので、包まないと診断ゼロで素通りする。
|
|
1464
|
+
*
|
|
1465
|
+
* `_failInitialization`(設定エラーの fail-fast)との違いは 1 つ:
|
|
1466
|
+
* **connectedCallbackPromise を reject する**。ここまで来た要素は state を 1 つも
|
|
1467
|
+
* 持たない = このツリーは存在しない。resolve すると、それを待つ消費者
|
|
1468
|
+
* (@wcstack/server の renderToString・@wcstack/testing の mount・README の
|
|
1469
|
+
* テストレシピ)に「準備完了」と嘘をつく。下の SSR 経路(_rejectConnectedCallback)と
|
|
1470
|
+
* 同じ規範で、そちらと同じく**元のエラーをそのまま**投げ直す。
|
|
1471
|
+
*
|
|
1472
|
+
* `initializePromise` は従来どおり**解決**する(reject しない)。
|
|
1473
|
+
* `waitForStateInitialize` はページ中の全 `<wcs-state>` の initializePromise を
|
|
1474
|
+
* `Promise.all` で待つので、reject にすると 1 要素の設定ミスが無関係な
|
|
1475
|
+
* バインディングまで道連れになる(_failInitialization の注記と同じ理由)。
|
|
1476
|
+
*
|
|
1477
|
+
* 後始末はしない: `_initialized` を立てないので `disconnectedCallback` は初期化前
|
|
1478
|
+
* ガードで抜ける。`$listKeys` / `$watch` のようにセッタの後半で落ちた形では
|
|
1479
|
+
* `$on` の購読と stream registry が残るが、この要素は復旧不能(setInitialState が
|
|
1480
|
+
* throw する)なので、残骸は要素ごと捨てる前提で放置する。
|
|
1481
|
+
*/
|
|
1482
|
+
private _failInitializeLoudly;
|
|
1137
1483
|
private _initializeBindWebComponent;
|
|
1138
1484
|
private _callStateConnectedCallback;
|
|
1139
1485
|
private _initializeDCC;
|
|
@@ -1145,7 +1491,10 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1145
1491
|
get connectedCallbackPromise(): Promise<void>;
|
|
1146
1492
|
get listPaths(): Set<string>;
|
|
1147
1493
|
get listKeys(): ListKeyMap | null;
|
|
1494
|
+
get hasRecursion(): boolean;
|
|
1495
|
+
get recursionRegistry(): RecursionRegistry | null;
|
|
1148
1496
|
get watchPaths(): ReadonlySet<string> | null;
|
|
1497
|
+
get scanPaths(): ReadonlySet<string> | null;
|
|
1149
1498
|
get elementPaths(): Set<string>;
|
|
1150
1499
|
/**
|
|
1151
1500
|
* ボリューム(webComponent/volume.ts)のアクセサ登録: ツリーパスをキーにした
|
|
@@ -1161,6 +1510,8 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1161
1510
|
enableUpdatedCallback(): void;
|
|
1162
1511
|
/** enable-ssr スナップショットから初期化されたか(D14 — webComponent/volume.ts が読む)。 */
|
|
1163
1512
|
get hydratedFromSsr(): boolean;
|
|
1513
|
+
addListPath(path: string): void;
|
|
1514
|
+
findStateDescriptor(path: string): PropertyDescriptor | undefined;
|
|
1164
1515
|
defineTreeAccessor(path: string, descriptor: PropertyDescriptor): void;
|
|
1165
1516
|
get getterPaths(): Set<string>;
|
|
1166
1517
|
get setterPaths(): Set<string>;
|
|
@@ -1168,6 +1519,8 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1168
1519
|
get dynamicDependency(): Map<string, string[]>;
|
|
1169
1520
|
get staticDependency(): Map<string, string[]>;
|
|
1170
1521
|
get version(): number;
|
|
1522
|
+
/** state の世代(キャッシュ項目の印の正本 — cache/types.ts の `generation`)。 */
|
|
1523
|
+
get stateGeneration(): number;
|
|
1171
1524
|
get rootNode(): Node;
|
|
1172
1525
|
get boundComponentStateProp(): string | null;
|
|
1173
1526
|
get hasMounts(): boolean;
|
|
@@ -1208,6 +1561,24 @@ declare class State extends HTMLElementBase implements IStateElement {
|
|
|
1208
1561
|
*/
|
|
1209
1562
|
addStaticDependency(sourcePath: string, targetPath: string): boolean;
|
|
1210
1563
|
setPathInfo(path: string, bindingType: BindingType, source?: PathInfoSource): void;
|
|
1564
|
+
/**
|
|
1565
|
+
* 再セットで消した経路情報を、生き残ったバインドの登録から作り直す(issue #258 の X7)。
|
|
1566
|
+
*
|
|
1567
|
+
* `_pathSet.clear()` は残す。あのクリアには、`forgetGeneration` が外した静的辺を「行が
|
|
1568
|
+
* 作り直されたときに登録し直させる」自己修復が乗っている(辺が戻ることは
|
|
1569
|
+
* `__tests__/integration.stateGenerationReset.test.ts` の
|
|
1570
|
+
* 「静的な辺 nodes.* → nodes.*.total が戻る」が固定する)。消したうえで、生きているバインド
|
|
1571
|
+
* ぶんだけ `setPathInfo` をやり直す — `$recursion` のアンカーで既に同じことをしている手口を、
|
|
1572
|
+
* バインド全体へ広げたもの。
|
|
1573
|
+
*
|
|
1574
|
+
* `_generatedPaths`(これまでの世代の生成アクセサの具体パス)は張り直さない。行バインドが
|
|
1575
|
+
* 名指していても、新しい世代ではまだ実体化されていないため(recursion/generation.ts)。読みが
|
|
1576
|
+
* 実体化したときに `defineTreeAccessor` が登録し直す。
|
|
1577
|
+
*
|
|
1578
|
+
* 反復中に `setPathInfo` が台帳へ書き戻す(既存キーの上書きのみで新キーは増えない)ので、
|
|
1579
|
+
* 誤解を避けるためスナップショットを取ってから回す。
|
|
1580
|
+
*/
|
|
1581
|
+
private _rebuildPathInfo;
|
|
1211
1582
|
private _createState;
|
|
1212
1583
|
createStateAsync(mutability: Mutability, callback: (state: IStateProxy) => Promise<void>): Promise<void>;
|
|
1213
1584
|
createState(mutability: Mutability, callback: (state: IStateProxy) => void): void;
|