@wcstack/state 1.20.0 → 1.21.1
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 +64 -3
- package/README.md +65 -3
- package/dist/index.d.ts +333 -3
- package/dist/index.esm.js +2215 -817
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/package.json +2 -1
package/README.ja.md
CHANGED
|
@@ -324,6 +324,10 @@ property[#modifier]: path[@state][|filter[|filter(args)...]]
|
|
|
324
324
|
| `#prevent` | イベントハンドラで `event.preventDefault()` を呼び出す |
|
|
325
325
|
| `#stop` | イベントハンドラで `event.stopPropagation()` を呼び出す |
|
|
326
326
|
| `#onchange` | 双方向バインディングで `input` の代わりに `change` イベントを使用 |
|
|
327
|
+
| `#init=<authority>` | バインディングの authority / 初期同期の向き — [バインディング authority](#バインディング-authority-init--sync) 参照 |
|
|
328
|
+
| `#sync=<timing>` | 要素スナップショットの読み取りタイミング — [バインディング authority](#バインディング-authority-init--sync) 参照 |
|
|
329
|
+
|
|
330
|
+
複数の修飾子は 1 つの `#` の後にカンマ区切りで書きます: `value#ro,init=none: path`
|
|
327
331
|
|
|
328
332
|
### 双方向バインディング
|
|
329
333
|
|
|
@@ -338,6 +342,46 @@ property[#modifier]: path[@state][|filter[|filter(args)...]]
|
|
|
338
342
|
|
|
339
343
|
`<input type="button">` は除外されます。`#ro` で無効化、`#onchange` でイベントを変更できます。
|
|
340
344
|
|
|
345
|
+
### バインディング authority (`#init=` / `#sync=`)
|
|
346
|
+
|
|
347
|
+
`static wcBindable` を宣言したカスタム要素への prop バインディングは、**authority**(そのワイヤをどちら側が所有するか)を解決します。authority は初期値の出所だけでなく、**そのバインディングの生存期間全体で state→element 書き込みが有効かどうか**を決めます。既定はメンバの宣言位置から導出されます(`enableDirectionalInitialSync` で既定 ON):
|
|
348
|
+
|
|
349
|
+
| メンバの宣言位置 | 既定 authority | 効果 |
|
|
350
|
+
|---|---|---|
|
|
351
|
+
| `properties` のみ(output-only) | `element` | 要素の値が state に流れる。**state からこのメンバへは書き込まれない** |
|
|
352
|
+
| `inputs` のみ | `state` | state が要素に書き込む |
|
|
353
|
+
| `properties` + `inputs`(双方向) | `state` | 従来挙動 — state が先に書き、以後は要素イベントが state を更新 |
|
|
354
|
+
| —(`wcBindable` 非宣言・素の HTML 要素) | `state` | 従来と不変 |
|
|
355
|
+
|
|
356
|
+
> **作法:** settable なメンバは **`properties` と `inputs` の両方**に宣言してください。`properties` にしか宣言されていないメンバは output-only 扱いになり、state→element 書き込みがバインディングの生存期間ずっと抑止され、要素側の初期値が state 側のシード値を上書きします(`@wcstack` の I/O ノード Shell と DCC の `$bindables` はこの作法に従っています)。
|
|
357
|
+
|
|
358
|
+
authority はバインディング単位で `#init=` により上書きできます:
|
|
359
|
+
|
|
360
|
+
| 値 | authority | 使える宣言 |
|
|
361
|
+
|---|---|---|
|
|
362
|
+
| `init=state` | state 所有:state → element(双方向メンバは要素イベントも引き続き受信) | inputs のみ・双方向 |
|
|
363
|
+
| `init=element` | element 所有:要素のスナップショットとイベント → state。state からの書き込みは抑止 | output-only・双方向 |
|
|
364
|
+
| `init=auto` | state スロットが未初期化なら `element`、それ以外は `state` | 双方向 |
|
|
365
|
+
| `init=none` | 初期同期なし(event バインディングはこの値のみ許可) | すべて |
|
|
366
|
+
|
|
367
|
+
`#sync=` は element authority のバインディングで要素スナップショットを**いつ**読むかを制御します:
|
|
368
|
+
|
|
369
|
+
| 値 | 意味 |
|
|
370
|
+
|---|---|
|
|
371
|
+
| `sync=call`(既定) | バインディングの attach 時に即読み取り |
|
|
372
|
+
| `sync=connect` | 要素が document に接続されるまで読み取りを保留 |
|
|
373
|
+
|
|
374
|
+
```html
|
|
375
|
+
<x-clock data-wcs="value#init=element: clock.now"></x-clock>
|
|
376
|
+
<x-input data-wcs="value#init=auto: form.name"></x-input>
|
|
377
|
+
<x-widget data-wcs="value#init=element,sync=connect: widget.snapshot"></x-widget>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
注意:
|
|
381
|
+
|
|
382
|
+
- `enableDirectionalInitialSync: false`(opt-out)のとき `#init=`/`#sync=` を書くと throw します。
|
|
383
|
+
- **1.20 以前からの移行:** output-only メンバに対して state 側に都合のよい初期値(`value: []`、`query: ""` 等)をシードしないでください — 要素側の実初期値(多くは `null`/`undefined`)がシードを置き換えます。シードは要素の実初期値に合わせ、表示用の値は派生 getter で null ガードしてください。
|
|
384
|
+
|
|
341
385
|
### ラジオボタンバインディング
|
|
342
386
|
|
|
343
387
|
`radio` でラジオボタングループを単一の状態値にバインドします:
|
|
@@ -1193,7 +1237,7 @@ class MyFetcher extends HTMLElement {
|
|
|
1193
1237
|
|
|
1194
1238
|
検証ルール(バインディング時に強制):
|
|
1195
1239
|
|
|
1196
|
-
- 要素は `protocol: "wc-bindable"`
|
|
1240
|
+
- 要素は `protocol: "wc-bindable"` かつ整数 `version` が `1` 以上(現行プロトコルは `1`。1 以上のすべてのバージョンが core 互換)の `static wcBindable` を公開するカスタム要素であること
|
|
1197
1241
|
- `methodName` は `wcBindable.commands` に(`name` で)現れること
|
|
1198
1242
|
- バインドされる値は `CommandToken` であること(token 以外の値の代入は throw する —— 例えば未宣言の名前 `$command.typo` は `undefined` に解決され、ここで拒否される)
|
|
1199
1243
|
|
|
@@ -1352,7 +1396,7 @@ class MyTarget extends HTMLElement {
|
|
|
1352
1396
|
|
|
1353
1397
|
検証ルール:
|
|
1354
1398
|
|
|
1355
|
-
- 要素は wc-bindable なカスタム要素であること(`static wcBindable`・`protocol: "wc-bindable"
|
|
1399
|
+
- 要素は wc-bindable なカスタム要素であること(`static wcBindable`・`protocol: "wc-bindable"`・整数 `version` が 1 以上。1 以上のすべてのバージョンが core 互換)。非 wc-bindable 要素は attach 時に拒否される。
|
|
1356
1400
|
- `<property>` は `wcBindable.properties` に現れること —— **attach 時** に検証(fail-fast。クラス参照のみで足り、DOM 接続に非依存)。
|
|
1357
1401
|
- `<tokenName>` は `$eventTokens` で宣言されていること —— **発火時** に検証。state はイベント発火時に要素の live root から解決されるため、attach 時にノードが detached になりうる `for` / `if` ブロック内や SSR ハイドレーション後でも機能する。
|
|
1358
1402
|
- 修飾子 `#prevent` / `#stop` は通常のイベントバインディングと同様に機能する: `eventToken.error#prevent: createFailed`。
|
|
@@ -1454,6 +1498,8 @@ error 時、プロパティは直前の fold 結果を保持し、エラーは `
|
|
|
1454
1498
|
|
|
1455
1499
|
`wcBindable.inputs` は一方向のプロパティ入力(state → 要素)を宣言します。エントリに `attribute` を設定すると、フレームワークはプロパティを書き込むたびにその値を当該 HTML 属性へも書き込むため、`attributeChangedCallback`・CSS の属性セレクタ・DevTools がすべてプロパティ値と同期し続けます。
|
|
1456
1500
|
|
|
1501
|
+
`inputs` は属性ミラーのためだけのメタデータではありません。方向認識初期同期(既定 ON)の下では、メンバが **state から settable であること**を示すのが `inputs` です。settable なのに `properties` にしか宣言されていないメンバは output-only 扱いになり、state からの書き込みが抑止されます — [バインディング authority](#バインディング-authority-init--sync) を参照してください。
|
|
1502
|
+
|
|
1457
1503
|
```javascript
|
|
1458
1504
|
class MyChip extends HTMLElement {
|
|
1459
1505
|
static wcBindable = {
|
|
@@ -1552,7 +1598,7 @@ export default {
|
|
|
1552
1598
|
|
|
1553
1599
|
これにより以下が生成されます:
|
|
1554
1600
|
|
|
1555
|
-
- クラスの `static wcBindable` —
|
|
1601
|
+
- クラスの `static wcBindable` — フレームワークアダプタ用のプロトコルメタデータ。各 `$bindables` メンバは `properties` と `inputs` の両方に宣言され(双方向)、方向認識初期同期の下でも親 state → DCC の書き込みが機能します — [バインディング authority](#バインディング-authority-init--sync) 参照
|
|
1556
1602
|
- プロトタイプの getter/setter — リアクティブプロキシ経由で読み書き
|
|
1557
1603
|
- `CustomEvent` のディスパッチ — 値が変更されるたびに `my-counter:count-changed` が発火
|
|
1558
1604
|
|
|
@@ -1671,6 +1717,21 @@ bootstrapState({
|
|
|
1671
1717
|
| `locale` | `'en'` | フィルタのデフォルトロケール |
|
|
1672
1718
|
| `debug` | `false` | デバッグモード |
|
|
1673
1719
|
| `enableMustache` | `true` | `{{ }}` 構文の有効化 |
|
|
1720
|
+
| `enableDirectionalInitialSync` | `true` | 方向認識のバインディング authority(`#init=` / `#sync=` バインド modifier)— [バインディング authority](#バインディング-authority-init--sync) 参照。既定 on。`false` で opt-out |
|
|
1721
|
+
| `enablePropagationContext` | `true` | バインド間の因果伝播トラッキング(echo/diamond のループ防止)。既定 on。`false` で opt-out |
|
|
1722
|
+
| `enableContractAnalyzer` | `false` | opt-in の開発時 contract analyzer(`analyzeContract` を公開) |
|
|
1723
|
+
|
|
1724
|
+
> この 3 つは **architecture-hardening** 機能で、規範は `docs/architecture-hardening/` に
|
|
1725
|
+
> あります。`enablePropagationContext` は**既定 on** — write-path コストは一方向バインドで
|
|
1726
|
+
> ほぼゼロ(echo しうる双方向 wire のみ因果 bookkeeping を行う)で、フラグは恒久的な
|
|
1727
|
+
> opt-out として残します。`enableDirectionalInitialSync` も**既定 on**: プロパティ単位で
|
|
1728
|
+
> 初期同期の authority を割り当てます(output-only な `wcBindable` メンバは初期値を
|
|
1729
|
+
> element→state で読み取り、双方向 / input メンバは state→element を維持)。setup-path
|
|
1730
|
+
> コストは初期 render の 5% 未満(producer-value observer は echo しうる双方向 wire にのみ
|
|
1731
|
+
> 登録)で、フラグは恒久的な opt-out として残します。`enableContractAnalyzer` は opt-in
|
|
1732
|
+
> (既定 `false`・無効時ランタイムコストゼロ)で、有効な場合、公開 API `analyzeContract()`
|
|
1733
|
+
> が稼働中の `static wcBindable` サーフェスと sidecar manifest の drift を開発時診断として
|
|
1734
|
+
> 報告します。
|
|
1674
1735
|
|
|
1675
1736
|
## TypeScript サポート
|
|
1676
1737
|
|
package/README.md
CHANGED
|
@@ -324,6 +324,10 @@ Multiple bindings separated by `;`:
|
|
|
324
324
|
| `#prevent` | Calls `event.preventDefault()` on event handlers |
|
|
325
325
|
| `#stop` | Calls `event.stopPropagation()` on event handlers |
|
|
326
326
|
| `#onchange` | Uses `change` event instead of `input` for two-way binding |
|
|
327
|
+
| `#init=<authority>` | Binding authority / initial sync direction — see [Binding Authority](#binding-authority-init--sync) |
|
|
328
|
+
| `#sync=<timing>` | Element snapshot timing — see [Binding Authority](#binding-authority-init--sync) |
|
|
329
|
+
|
|
330
|
+
Multiple modifiers are comma-separated after a single `#`: `value#ro,init=none: path`.
|
|
327
331
|
|
|
328
332
|
### Two-Way Binding
|
|
329
333
|
|
|
@@ -338,6 +342,46 @@ Automatically enabled for:
|
|
|
338
342
|
|
|
339
343
|
`<input type="button">` is excluded. Use `#ro` to disable, `#onchange` to change the event.
|
|
340
344
|
|
|
345
|
+
### Binding Authority (`#init=` / `#sync=`)
|
|
346
|
+
|
|
347
|
+
For custom elements that declare `static wcBindable`, every prop binding resolves an **authority** — which side owns the wire. The authority decides both where the initial value comes from *and* whether state→element writes are enabled, for the binding's whole lifetime. It is derived from where the member is declared (on by default via `enableDirectionalInitialSync`):
|
|
348
|
+
|
|
349
|
+
| Member declared in | Default authority | Effect |
|
|
350
|
+
|---|---|---|
|
|
351
|
+
| `properties` only (output-only) | `element` | The element's value flows into state; **state never writes this member** |
|
|
352
|
+
| `inputs` only | `state` | State writes the element |
|
|
353
|
+
| `properties` + `inputs` (two-way) | `state` | Classic behavior — state writes first, element events update state afterwards |
|
|
354
|
+
| — (no `wcBindable`; plain HTML elements) | `state` | Unchanged behavior |
|
|
355
|
+
|
|
356
|
+
> **Authoring rule:** declare every settable member in **both** `properties` and `inputs`. A member declared only in `properties` is output-only — state→element writes are suppressed for the life of the binding, and the element's own initial value overwrites whatever the state seeded. (`@wcstack` I/O node Shells and DCC `$bindables` follow this rule.)
|
|
357
|
+
|
|
358
|
+
Override the authority per binding with `#init=`:
|
|
359
|
+
|
|
360
|
+
| Value | Authority | Allowed on |
|
|
361
|
+
|---|---|---|
|
|
362
|
+
| `init=state` | State-owned: state → element (two-way members still receive element events) | inputs-only, two-way |
|
|
363
|
+
| `init=element` | Element-owned: element snapshot and events → state; state writes are suppressed | output-only, two-way |
|
|
364
|
+
| `init=auto` | `element` if the state slot is uninitialized, otherwise `state` | two-way |
|
|
365
|
+
| `init=none` | No initial sync (event bindings accept only this value) | any |
|
|
366
|
+
|
|
367
|
+
`#sync=` controls **when** the element snapshot is read for element-authority bindings:
|
|
368
|
+
|
|
369
|
+
| Value | Meaning |
|
|
370
|
+
|---|---|
|
|
371
|
+
| `sync=call` (default) | Read immediately when the binding attaches |
|
|
372
|
+
| `sync=connect` | Defer the read until the element is connected to the document |
|
|
373
|
+
|
|
374
|
+
```html
|
|
375
|
+
<x-clock data-wcs="value#init=element: clock.now"></x-clock>
|
|
376
|
+
<x-input data-wcs="value#init=auto: form.name"></x-input>
|
|
377
|
+
<x-widget data-wcs="value#init=element,sync=connect: widget.snapshot"></x-widget>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Notes:
|
|
381
|
+
|
|
382
|
+
- With `enableDirectionalInitialSync: false` (opt-out), writing `#init=`/`#sync=` throws.
|
|
383
|
+
- **Migrating from ≤ 1.20:** do not seed state with placeholder values (`value: []`, `query: ""`) for output-only members — the element's real initial value (often `null`/`undefined`) replaces the seed. Match the seed to the element's actual initial value and null-guard display values with a derived getter.
|
|
384
|
+
|
|
341
385
|
### Radio Binding
|
|
342
386
|
|
|
343
387
|
Bind a radio button group to a single state value with `radio`:
|
|
@@ -1194,7 +1238,7 @@ class MyFetcher extends HTMLElement {
|
|
|
1194
1238
|
|
|
1195
1239
|
Validation rules (enforced at binding time):
|
|
1196
1240
|
|
|
1197
|
-
- The element must be a custom element exposing `static wcBindable` with `protocol: "wc-bindable"` and `version
|
|
1241
|
+
- The element must be a custom element exposing `static wcBindable` with `protocol: "wc-bindable"` and an integer `version` of `1` or later (the current protocol version is `1`; all versions ≥ 1 are core-compatible)
|
|
1198
1242
|
- `methodName` must appear (by `name`) in `wcBindable.commands`
|
|
1199
1243
|
- The bound value must be a `CommandToken` (assigning a non-token value throws — for example, an undeclared name like `$command.typo` resolves to `undefined` and is rejected here)
|
|
1200
1244
|
|
|
@@ -1353,7 +1397,7 @@ class MyTarget extends HTMLElement {
|
|
|
1353
1397
|
|
|
1354
1398
|
Validation rules:
|
|
1355
1399
|
|
|
1356
|
-
- The element must be a wc-bindable custom element (`static wcBindable`, `protocol: "wc-bindable"`, `version
|
|
1400
|
+
- The element must be a wc-bindable custom element (`static wcBindable`, `protocol: "wc-bindable"`, integer `version` ≥ 1 — all versions ≥ 1 are core-compatible). A non-wc-bindable element is rejected at attach time.
|
|
1357
1401
|
- `<property>` must appear in `wcBindable.properties` — checked at **attach time** (fail-fast; needs only the class, not DOM connection).
|
|
1358
1402
|
- `<tokenName>` must be declared in `$eventTokens` — checked at **fire time**. State is resolved from the element's live root node when the event fires, so the binding also works inside `for` / `if` blocks and after SSR hydration, where the node may still be detached at attach time.
|
|
1359
1403
|
- Modifiers `#prevent` / `#stop` work as on any event binding: `eventToken.error#prevent: createFailed`.
|
|
@@ -1455,6 +1499,8 @@ See [docs/streams.md](docs/streams.md) for the full contract — lifecycle and o
|
|
|
1455
1499
|
|
|
1456
1500
|
`wcBindable.inputs` declares one-way property inputs (state → element). When an entry sets `attribute`, the framework writes the value to that HTML attribute every time it writes the property, so `attributeChangedCallback`, CSS attribute selectors, and DevTools all stay in sync with the property value.
|
|
1457
1501
|
|
|
1502
|
+
`inputs` is not just attribute-mirroring metadata: under directional initial sync (default on), it is what marks a member as **settable from state**. A settable member declared only in `properties` becomes output-only and state writes to it are suppressed — see [Binding Authority](#binding-authority-init--sync).
|
|
1503
|
+
|
|
1458
1504
|
```javascript
|
|
1459
1505
|
class MyChip extends HTMLElement {
|
|
1460
1506
|
static wcBindable = {
|
|
@@ -1553,7 +1599,7 @@ export default {
|
|
|
1553
1599
|
|
|
1554
1600
|
This generates:
|
|
1555
1601
|
|
|
1556
|
-
- `static wcBindable` on the class — protocol metadata for framework adapters
|
|
1602
|
+
- `static wcBindable` on the class — protocol metadata for framework adapters. Each `$bindables` member is declared in both `properties` and `inputs` (two-way), so parent-state → DCC writes keep working under directional initial sync — see [Binding Authority](#binding-authority-init--sync)
|
|
1557
1603
|
- Getter/setter on the prototype — reads/writes go through the reactive proxy
|
|
1558
1604
|
- `CustomEvent` dispatch — `my-counter:count-changed` fires on every mutation
|
|
1559
1605
|
|
|
@@ -1672,6 +1718,22 @@ All options with defaults:
|
|
|
1672
1718
|
| `locale` | `'en'` | Default locale for filters |
|
|
1673
1719
|
| `debug` | `false` | Debug mode |
|
|
1674
1720
|
| `enableMustache` | `true` | Enable `{{ }}` syntax |
|
|
1721
|
+
| `enableDirectionalInitialSync` | `true` | Direction-aware binding authority (`#init=` / `#sync=` binding modifiers) — see [Binding Authority](#binding-authority-init--sync). Default on; set `false` to opt out |
|
|
1722
|
+
| `enablePropagationContext` | `true` | Causal propagation tracking across bindings (echo/diamond loop prevention). Default on; set `false` to opt out |
|
|
1723
|
+
| `enableContractAnalyzer` | `false` | Opt-in dev-time contract analyzer (exposes `analyzeContract`) |
|
|
1724
|
+
|
|
1725
|
+
> These three are **architecture-hardening** features; their normative reference is
|
|
1726
|
+
> `docs/architecture-hardening/`. `enablePropagationContext` defaults **on** — its
|
|
1727
|
+
> write-path cost is near-zero for one-way bindings (only echo-capable two-way
|
|
1728
|
+
> wires do the causal bookkeeping) — with the flag kept as a permanent opt-out.
|
|
1729
|
+
> `enableDirectionalInitialSync` also defaults **on**: it assigns per-property
|
|
1730
|
+
> initial-sync authority (an output-only `wcBindable` member reads its initial value
|
|
1731
|
+
> element→state; two-way / input members keep state→element). Its setup-path cost is
|
|
1732
|
+
> under 5% of initial render (the producer-value observer is only registered for
|
|
1733
|
+
> echo-capable two-way wires), and the flag is a permanent opt-out. `enableContractAnalyzer`
|
|
1734
|
+
> is opt-in (default `false`, zero runtime cost when off); when on, the exported
|
|
1735
|
+
> `analyzeContract()` API reports drift between a live `static wcBindable` surface and
|
|
1736
|
+
> a sidecar manifest for dev-time diagnostics.
|
|
1675
1737
|
|
|
1676
1738
|
## TypeScript Support
|
|
1677
1739
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for hierarchical loop index management in nested loops.
|
|
3
|
+
* Tracks parent-child relationships, versions, and provides access to index hierarchy.
|
|
4
|
+
*/
|
|
5
|
+
interface IListIndex {
|
|
6
|
+
readonly parentListIndex: IListIndex | null;
|
|
7
|
+
readonly uuid: string;
|
|
8
|
+
readonly position: number;
|
|
9
|
+
readonly length: number;
|
|
10
|
+
index: number;
|
|
11
|
+
readonly version: number;
|
|
12
|
+
readonly dirty: boolean;
|
|
13
|
+
readonly indexes: number[];
|
|
14
|
+
readonly listIndexes: WeakRef<IListIndex>[];
|
|
15
|
+
readonly varName: string;
|
|
16
|
+
at(position: number): IListIndex | null;
|
|
17
|
+
}
|
|
18
|
+
interface ILoopContext extends IStateAddress {
|
|
19
|
+
readonly pathInfo: IPathInfo;
|
|
20
|
+
readonly listIndex: IListIndex;
|
|
21
|
+
}
|
|
22
|
+
interface ILoopContextStack {
|
|
23
|
+
createLoopContext(elementStateAddress: IStateAddress, callback: (loopContext: ILoopContext) => void | Promise<void>): void | Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare const setLoopContextAsyncSymbol: unique symbol;
|
|
27
|
+
declare const setLoopContextSymbol: unique symbol;
|
|
28
|
+
declare const getByAddressSymbol: unique symbol;
|
|
29
|
+
declare const hasByAddressSymbol: unique symbol;
|
|
30
|
+
declare const setByAddressSymbol: unique symbol;
|
|
31
|
+
declare const connectedCallbackSymbol: unique symbol;
|
|
32
|
+
declare const disconnectedCallbackSymbol: unique symbol;
|
|
33
|
+
declare const updatedCallbackSymbol: unique symbol;
|
|
34
|
+
|
|
35
|
+
interface IStateProxy extends IState {
|
|
36
|
+
[setLoopContextAsyncSymbol](loopContext: ILoopContext | null, callback: () => Promise<any>): Promise<any>;
|
|
37
|
+
[setLoopContextSymbol](loopContext: ILoopContext | null, callback: () => any): any;
|
|
38
|
+
[getByAddressSymbol](address: IStateAddress): any;
|
|
39
|
+
[hasByAddressSymbol](address: IStateAddress): boolean;
|
|
40
|
+
[setByAddressSymbol](address: IStateAddress, value: any): void;
|
|
41
|
+
[connectedCallbackSymbol](): Promise<void>;
|
|
42
|
+
[disconnectedCallbackSymbol](): void;
|
|
43
|
+
[updatedCallbackSymbol](updatedAbsAddressList: IAbsoluteStateAddress[]): void;
|
|
44
|
+
}
|
|
45
|
+
type Mutability = "readonly" | "writable";
|
|
46
|
+
|
|
47
|
+
interface IStateElement {
|
|
48
|
+
readonly name: string;
|
|
49
|
+
readonly initializePromise: Promise<void>;
|
|
50
|
+
readonly connectedCallbackPromise: Promise<void>;
|
|
51
|
+
readonly listPaths: Set<string>;
|
|
52
|
+
readonly elementPaths: Set<string>;
|
|
53
|
+
readonly getterPaths: Set<string>;
|
|
54
|
+
readonly setterPaths: Set<string>;
|
|
55
|
+
readonly loopContextStack: ILoopContextStack;
|
|
56
|
+
readonly dynamicDependency: Map<string, string[]>;
|
|
57
|
+
readonly staticDependency: Map<string, string[]>;
|
|
58
|
+
readonly version: number;
|
|
59
|
+
readonly rootNode: Node;
|
|
60
|
+
readonly boundComponentStateProp: string | null;
|
|
61
|
+
readonly bindableEventMap: Record<string, string>;
|
|
62
|
+
readonly commandTokenNames: ReadonlySet<string>;
|
|
63
|
+
readonly eventTokenNames: ReadonlySet<string>;
|
|
64
|
+
/**
|
|
65
|
+
* state が $updatedCallback を定義しているか。false のとき drain は更新
|
|
66
|
+
* アドレスの集計と最終の writable createState を丸ごとスキップできる。
|
|
67
|
+
* optional なのはテスト用モック互換のため(undefined は「不明=集計する」)。
|
|
68
|
+
*/
|
|
69
|
+
readonly hasUpdatedCallback?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* 他行を読む getter(隣接項目参照など)が検出されたリストパスの集合。
|
|
72
|
+
* これらのリストは walkDependency の diff-filter 展開の対象外(全行展開)。
|
|
73
|
+
* optional なのはテスト用モック互換のため(undefined は「検出なし」扱い)。
|
|
74
|
+
*/
|
|
75
|
+
readonly crossRowListPaths?: ReadonlySet<string>;
|
|
76
|
+
addCrossRowListPath?(path: string): void;
|
|
77
|
+
setPathInfo(path: string, bindingType: BindingType): void;
|
|
78
|
+
addStaticDependency(parentPath: string, childPath: string): boolean;
|
|
79
|
+
addDynamicDependency(fromPath: string, toPath: string): boolean;
|
|
80
|
+
createStateAsync(mutability: Mutability, callback: (state: IStateProxy) => Promise<void>): Promise<void>;
|
|
81
|
+
createState(mutability: Mutability, callback: (state: IStateProxy) => void): void;
|
|
82
|
+
nextVersion(): number;
|
|
83
|
+
bindProperty(prop: string, desc: PropertyDescriptor): void;
|
|
84
|
+
setInitialState(state: Record<string, any>): void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface IPathInfo {
|
|
88
|
+
readonly id: number;
|
|
89
|
+
readonly path: string;
|
|
90
|
+
readonly segments: string[];
|
|
91
|
+
readonly lastSegment: string;
|
|
92
|
+
readonly cumulativePaths: string[];
|
|
93
|
+
readonly cumulativePathSet: Set<string>;
|
|
94
|
+
readonly cumulativePathInfos: IPathInfo[];
|
|
95
|
+
readonly cumulativePathInfoSet: Set<IPathInfo>;
|
|
96
|
+
readonly parentPath: string | null;
|
|
97
|
+
readonly parentPathInfo: IPathInfo | null;
|
|
98
|
+
readonly wildcardPaths: string[];
|
|
99
|
+
readonly wildcardPathSet: Set<string>;
|
|
100
|
+
readonly indexByWildcardPath: Record<string, number>;
|
|
101
|
+
readonly wildcardPathInfos: IPathInfo[];
|
|
102
|
+
readonly wildcardPathInfoSet: Set<IPathInfo>;
|
|
103
|
+
readonly wildcardParentPaths: string[];
|
|
104
|
+
readonly wildcardParentPathSet: Set<string>;
|
|
105
|
+
readonly wildcardParentPathInfos: IPathInfo[];
|
|
106
|
+
readonly wildcardParentPathInfoSet: Set<IPathInfo>;
|
|
107
|
+
readonly wildcardPositions: number[];
|
|
108
|
+
readonly lastWildcardPath: string | null;
|
|
109
|
+
readonly lastWildcardInfo: IPathInfo | null;
|
|
110
|
+
readonly wildcardCount: number;
|
|
111
|
+
}
|
|
112
|
+
interface IStateAddress {
|
|
113
|
+
readonly pathInfo: IPathInfo;
|
|
114
|
+
readonly listIndex: IListIndex | null;
|
|
115
|
+
readonly parentAddress: IStateAddress | null;
|
|
116
|
+
}
|
|
117
|
+
interface IAbsolutePathInfo {
|
|
118
|
+
readonly stateName: string;
|
|
119
|
+
readonly stateElement: IStateElement;
|
|
120
|
+
readonly pathInfo: IPathInfo;
|
|
121
|
+
readonly parentAbsolutePathInfo: IAbsolutePathInfo | null;
|
|
122
|
+
}
|
|
123
|
+
interface IAbsoluteStateAddress {
|
|
124
|
+
readonly absolutePathInfo: IAbsolutePathInfo;
|
|
125
|
+
readonly listIndex: IListIndex | null;
|
|
126
|
+
readonly parentAbsoluteAddress: IAbsoluteStateAddress | null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Filter/types.ts
|
|
131
|
+
*
|
|
132
|
+
* Type definition file for filter functions.
|
|
133
|
+
*
|
|
134
|
+
* Main responsibilities:
|
|
135
|
+
* - Defines types for filter functions (FilterFn) and filter functions with options (FilterWithOptionsFn)
|
|
136
|
+
* - Type-safe management of filter name-to-function mappings (FilterWithOptions) and filter function arrays (Filters)
|
|
137
|
+
* - Defines types for retrieving filter functions from built-in filter collections
|
|
138
|
+
*
|
|
139
|
+
* Design points:
|
|
140
|
+
* - Type design enabling flexible filter design and extension
|
|
141
|
+
* - Supports filters with options and combinations of multiple filters
|
|
142
|
+
*/
|
|
143
|
+
type FilterFn<T = unknown> = (value: unknown) => T;
|
|
144
|
+
|
|
145
|
+
type BindingType = 'text' | 'prop' | 'event' | 'for' | 'if' | 'elseif' | 'else' | 'radio' | 'checkbox' | 'spread';
|
|
146
|
+
interface IFilterInfo {
|
|
147
|
+
readonly filterName: string;
|
|
148
|
+
readonly args: string[];
|
|
149
|
+
readonly filterFn: FilterFn;
|
|
150
|
+
}
|
|
151
|
+
interface IBindingInfo {
|
|
152
|
+
readonly propName: string;
|
|
153
|
+
readonly propSegments: string[];
|
|
154
|
+
readonly propModifiers: string[];
|
|
155
|
+
readonly statePathName: string;
|
|
156
|
+
readonly statePathInfo: IPathInfo;
|
|
157
|
+
readonly stateName: string;
|
|
158
|
+
readonly inFilters: IFilterInfo[];
|
|
159
|
+
readonly outFilters: IFilterInfo[];
|
|
160
|
+
readonly node: Node;
|
|
161
|
+
readonly replaceNode: Node;
|
|
162
|
+
readonly bindingType: BindingType;
|
|
163
|
+
readonly uuid?: string | null;
|
|
164
|
+
}
|
|
165
|
+
|
|
1
166
|
interface IState {
|
|
2
167
|
[key: string]: any;
|
|
3
168
|
}
|
|
@@ -20,6 +185,25 @@ interface IConfig {
|
|
|
20
185
|
readonly locale: string;
|
|
21
186
|
readonly debug: boolean;
|
|
22
187
|
readonly enableMustache: boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Enables direction-aware initial synchronization (`init=` / `sync=`).
|
|
190
|
+
* Disabled by default while Phase 2 is evaluated against existing snapshots.
|
|
191
|
+
*/
|
|
192
|
+
readonly enableDirectionalInitialSync: boolean;
|
|
193
|
+
/**
|
|
194
|
+
* Enables causal propagation tracking (transaction / edge provenance /
|
|
195
|
+
* write receipts). Disabled by default while Phase 3 runs as a shadow of
|
|
196
|
+
* the primitive same-value guard.
|
|
197
|
+
*/
|
|
198
|
+
readonly enablePropagationContext: boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Enables the opt-in dev-time contract analyzer (Phase 5b). When false
|
|
201
|
+
* (default), `analyzeContract()` is a no-op with zero cost — runtime
|
|
202
|
+
* behavior and allocation are unchanged. When true, it checks the actually
|
|
203
|
+
* loaded `static wcBindable` declarations against a supplied sidecar
|
|
204
|
+
* manifest and emits `contract:*` drift trace via the DevTools sink.
|
|
205
|
+
*/
|
|
206
|
+
readonly enableContractAnalyzer: boolean;
|
|
23
207
|
/**
|
|
24
208
|
* 同値ガード(**既定 true**・標準的リアクティブ挙動・`setConfig({ sameValueGuard: false })` で opt-out 可)。
|
|
25
209
|
* primitive 値の set で `Object.is` 同値なら更新を no-op にする
|
|
@@ -40,6 +224,9 @@ interface IWritableConfig {
|
|
|
40
224
|
locale?: string;
|
|
41
225
|
debug?: boolean;
|
|
42
226
|
enableMustache?: boolean;
|
|
227
|
+
enableDirectionalInitialSync?: boolean;
|
|
228
|
+
enablePropagationContext?: boolean;
|
|
229
|
+
enableContractAnalyzer?: boolean;
|
|
43
230
|
sameValueGuard?: boolean;
|
|
44
231
|
}
|
|
45
232
|
|
|
@@ -52,6 +239,13 @@ declare function getConfig(): IConfig;
|
|
|
52
239
|
*/
|
|
53
240
|
declare function getBindingsReady(rootNode: Node): Promise<void>;
|
|
54
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Browser builds use the native HTMLElement. Headless runtimes receive an
|
|
244
|
+
* inert base so the public module can be imported without installing DOM
|
|
245
|
+
* globals; constructing components remains a browser-only operation.
|
|
246
|
+
*/
|
|
247
|
+
declare const HTMLElementBase: typeof HTMLElement;
|
|
248
|
+
|
|
55
249
|
interface ISsrElement {
|
|
56
250
|
readonly name: string;
|
|
57
251
|
readonly version: string;
|
|
@@ -61,7 +255,7 @@ interface ISsrElement {
|
|
|
61
255
|
getTemplate(uuid: string): HTMLTemplateElement | null;
|
|
62
256
|
verifyVersion(): boolean;
|
|
63
257
|
}
|
|
64
|
-
declare class Ssr extends
|
|
258
|
+
declare class Ssr extends HTMLElementBase implements ISsrElement {
|
|
65
259
|
private _stateData;
|
|
66
260
|
private _templates;
|
|
67
261
|
private _hydrateProps;
|
|
@@ -425,5 +619,141 @@ interface IWcsManifest {
|
|
|
425
619
|
/** 機械可読な単一正本を返す。vscode-wcs はこれを消費する想定。 */
|
|
426
620
|
declare function getWcsManifest(): IWcsManifest;
|
|
427
621
|
|
|
428
|
-
|
|
429
|
-
|
|
622
|
+
/**
|
|
623
|
+
* devtools/types.ts
|
|
624
|
+
*
|
|
625
|
+
* DevTools Hook Protocol (docs/devtools-hook-protocol.md) の型定義。
|
|
626
|
+
*
|
|
627
|
+
* イベント payload はランタイム内部オブジェクト(IAbsoluteStateAddress /
|
|
628
|
+
* IBindingInfo 等)への生参照を含む(同一 realm・オーバーレイ前提、protocol 原則 4)。
|
|
629
|
+
* 消費者はこれらを変異してはならない。
|
|
630
|
+
*/
|
|
631
|
+
|
|
632
|
+
type DevtoolsEvent = {
|
|
633
|
+
readonly type: "state:element-registered";
|
|
634
|
+
readonly name: string;
|
|
635
|
+
readonly rootNode: Node;
|
|
636
|
+
readonly element: IStateElement;
|
|
637
|
+
} | {
|
|
638
|
+
readonly type: "state:element-unregistered";
|
|
639
|
+
readonly name: string;
|
|
640
|
+
readonly rootNode: Node;
|
|
641
|
+
readonly element: IStateElement;
|
|
642
|
+
} | {
|
|
643
|
+
readonly type: "state:write";
|
|
644
|
+
readonly absoluteAddress: IAbsoluteStateAddress;
|
|
645
|
+
readonly value: unknown;
|
|
646
|
+
/** same-value guard が既に取得していた場合のみ意味を持つ(protocol §4.2) */
|
|
647
|
+
readonly oldValue: unknown;
|
|
648
|
+
readonly hasOldValue: boolean;
|
|
649
|
+
} | {
|
|
650
|
+
readonly type: "state:update-batch";
|
|
651
|
+
readonly addresses: ReadonlySet<IAbsoluteStateAddress>;
|
|
652
|
+
} | {
|
|
653
|
+
readonly type: "state:binding-added";
|
|
654
|
+
readonly absoluteAddress: IAbsoluteStateAddress;
|
|
655
|
+
readonly binding: IBindingInfo;
|
|
656
|
+
} | {
|
|
657
|
+
readonly type: "state:binding-removed";
|
|
658
|
+
readonly absoluteAddress: IAbsoluteStateAddress;
|
|
659
|
+
readonly binding: IBindingInfo;
|
|
660
|
+
} | {
|
|
661
|
+
readonly type: "state:binding-cleared";
|
|
662
|
+
readonly absoluteAddress: IAbsoluteStateAddress;
|
|
663
|
+
} | {
|
|
664
|
+
readonly type: "state:token-emit";
|
|
665
|
+
readonly kind: "command" | "event";
|
|
666
|
+
readonly stateName: string | null;
|
|
667
|
+
readonly tokenName: string;
|
|
668
|
+
readonly args: readonly unknown[];
|
|
669
|
+
readonly subscriberCount: number;
|
|
670
|
+
} | {
|
|
671
|
+
readonly type: "propagation:suppressed";
|
|
672
|
+
readonly reason: "confirmation" | "visited-edge";
|
|
673
|
+
readonly transactionId: number;
|
|
674
|
+
readonly edgeId: number;
|
|
675
|
+
readonly node: Node;
|
|
676
|
+
readonly member: string;
|
|
677
|
+
} | {
|
|
678
|
+
readonly type: "propagation:coalesced";
|
|
679
|
+
readonly absoluteAddress: IAbsoluteStateAddress;
|
|
680
|
+
readonly droppedTransactionId: number;
|
|
681
|
+
readonly winnerTransactionId: number;
|
|
682
|
+
} | {
|
|
683
|
+
readonly type: "propagation:hop-limit";
|
|
684
|
+
readonly absoluteAddress: IAbsoluteStateAddress;
|
|
685
|
+
readonly transactionId: number;
|
|
686
|
+
readonly hop: number;
|
|
687
|
+
} | {
|
|
688
|
+
readonly type: "contract:manifest-read";
|
|
689
|
+
readonly tag: string;
|
|
690
|
+
/** 実行時に該当タグが登録済みか(未登録なら drift の起点)。 */
|
|
691
|
+
readonly loaded: boolean;
|
|
692
|
+
} | {
|
|
693
|
+
readonly type: "contract:unsupported-extension";
|
|
694
|
+
readonly namespace: string;
|
|
695
|
+
} | {
|
|
696
|
+
readonly type: "contract:drift";
|
|
697
|
+
readonly reason: "component-not-loaded" | "missing-member" | "event-mismatch";
|
|
698
|
+
readonly tag: string;
|
|
699
|
+
readonly member?: string;
|
|
700
|
+
/** event-mismatch のとき: sidecar 宣言 event / live event。 */
|
|
701
|
+
readonly sidecarEvent?: string;
|
|
702
|
+
readonly liveEvent?: string;
|
|
703
|
+
};
|
|
704
|
+
/** contract analyzer(Phase 5b)が生成しうる event だけの狭い union(公開 API の戻り型)。 */
|
|
705
|
+
type ContractEvent = Extract<DevtoolsEvent, {
|
|
706
|
+
readonly type: "contract:manifest-read" | "contract:unsupported-extension" | "contract:drift";
|
|
707
|
+
}>;
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* contract/types.ts
|
|
711
|
+
*
|
|
712
|
+
* Phase 5b(dev-time contract analyzer)が読む sidecar manifest の最小 subset。
|
|
713
|
+
* 完全な JSON-Schema subset 検証は CI 側(vscode-wcs の validator core)の責務であり、
|
|
714
|
+
* runtime analyzer は「実際に読み込まれた wcBindable 宣言との drift」照合に絞る。
|
|
715
|
+
*
|
|
716
|
+
* この型は vscode-wcs の `wcstack.types` を copy-distribution したもの(§14: ランタイム
|
|
717
|
+
* 依存を導入しない)。CI 側の全量型ではなく drift 照合に必要な形だけを持つ。
|
|
718
|
+
*/
|
|
719
|
+
interface IContractObservable {
|
|
720
|
+
readonly event?: string;
|
|
721
|
+
}
|
|
722
|
+
interface IContractComponent {
|
|
723
|
+
readonly observables?: Readonly<Record<string, IContractObservable>>;
|
|
724
|
+
readonly inputs?: Readonly<Record<string, unknown>>;
|
|
725
|
+
readonly commands?: Readonly<Record<string, unknown>>;
|
|
726
|
+
}
|
|
727
|
+
interface IContractManifest {
|
|
728
|
+
readonly manifestExtensions?: {
|
|
729
|
+
readonly "wcstack.types"?: {
|
|
730
|
+
readonly components?: Readonly<Record<string, IContractComponent>>;
|
|
731
|
+
};
|
|
732
|
+
readonly [namespace: string]: unknown;
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* contract/contractAnalyzer.ts
|
|
738
|
+
*
|
|
739
|
+
* Phase 5b(09-remediation-design.md §5b / §7.1 dev runtime / §6 contract trace)の
|
|
740
|
+
* opt-in dev-time analyzer。実際に登録済みの custom element の `static wcBindable`
|
|
741
|
+
* 宣言(= 実行時の正本)を、利用者が渡した sidecar manifest と突き合わせ、drift を
|
|
742
|
+
* DevTools trace(`contract:*`)へ流す。
|
|
743
|
+
*
|
|
744
|
+
* 完了条件「無効時の runtime 挙動・cost が不変」: `analyzeContract` は
|
|
745
|
+
* `config.enableContractAnalyzer` が false のとき即 return し、manifest を一切走査
|
|
746
|
+
* しない(hot path には一切フックしない — 純粋な on-demand API)。
|
|
747
|
+
*
|
|
748
|
+
* pure な core(`analyzeManifestContract`)は宣言解決と emit を注入で受けるためテスト可能。
|
|
749
|
+
*/
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* opt-in dev-time contract analysis。無効時はゼロコスト(即 return・manifest 非走査)。
|
|
753
|
+
* 有効時は live 宣言と manifest を突き合わせ、`contract:*` trace を返しつつ、DevTools
|
|
754
|
+
* sink が接続されていれば同時に流す。
|
|
755
|
+
*/
|
|
756
|
+
declare function analyzeContract(manifest: IContractManifest): readonly ContractEvent[];
|
|
757
|
+
|
|
758
|
+
export { Ssr, VERSION, WCS_MANIFEST_VERSION, analyzeContract, bootstrapState, buildBindings, builtinFilterMeta, defineState, getBindingsReady, getConfig, getWcsManifest };
|
|
759
|
+
export type { ContractEvent, FilterArgType, FilterResultType, IContractManifest, IFilterMeta, ISsrElement, IWcsManifest, IWritableConfig, IWritableTagNames, WcsPathValue, WcsPaths, WcsStateApi, WcsThis };
|