@wcstack/state 4.3.0 → 4.5.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 +17 -8
- package/README.md +17 -7
- package/dist/auto.min.js +1 -1
- package/dist/auto.min.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/split/auto.js +1 -1
- package/dist/split/chunks/chunk-467LYA2V.js +2 -0
- package/dist/split/chunks/chunk-467LYA2V.js.map +1 -0
- package/dist/split/chunks/{chunk-6I7GGFNJ.js → chunk-CBMAVRYG.js} +2 -2
- package/dist/split/chunks/{chunk-2RBI6DVS.js → chunk-FBBHQU7S.js} +2 -2
- package/dist/split/chunks/chunk-GBBBRPZR.js +2 -0
- package/dist/split/chunks/{chunk-DOWWBZVS.js.map → chunk-GBBBRPZR.js.map} +1 -1
- package/dist/split/chunks/chunk-JWRQLKOQ.js +2 -0
- package/dist/split/chunks/chunk-NITPUCPA.js +2 -0
- package/dist/split/chunks/{chunk-CIBJLAA3.js → chunk-T3FN37DU.js} +2 -2
- package/dist/split/chunks/{chunk-BGUJ3HF6.js → chunk-TNYNJXEC.js} +2 -2
- package/dist/split/chunks/chunk-TNYNJXEC.js.map +1 -0
- package/dist/split/chunks/{chunk-BKLFDVOP.js → chunk-ULMHOGJ2.js} +2 -2
- package/dist/split/chunks/chunk-VGHTB55B.js +2 -0
- package/dist/split/chunks/{chunk-M67C46SN.js.map → chunk-VGHTB55B.js.map} +1 -1
- package/dist/split/chunks/chunk-ZJYPRSTO.js +2 -0
- package/dist/split/core.js +1 -1
- package/dist/split/features/devtools.js +1 -1
- package/dist/split/features/diagnostics.js +1 -1
- package/dist/split/features/diagnostics.js.map +1 -1
- package/dist/split/features/formats.js +1 -1
- package/dist/split/features/list-keys.js +1 -1
- package/dist/split/features/native-commands.js +1 -1
- package/dist/split/features/recursion.js +1 -1
- package/dist/split/features/scopes.js +1 -1
- package/dist/split/features/scopes.js.map +1 -1
- package/dist/split/features/ssr.js +1 -1
- package/dist/split/features/ssr.js.map +1 -1
- package/dist/split/features/temporal.js +1 -1
- package/package.json +1 -1
- package/dist/split/chunks/chunk-5Q4M6A3C.js +0 -2
- package/dist/split/chunks/chunk-BGUJ3HF6.js.map +0 -1
- package/dist/split/chunks/chunk-DOWWBZVS.js +0 -2
- package/dist/split/chunks/chunk-F3DVWVH3.js +0 -2
- package/dist/split/chunks/chunk-M67C46SN.js +0 -2
- package/dist/split/chunks/chunk-NMOSSDKJ.js +0 -2
- package/dist/split/chunks/chunk-NMOSSDKJ.js.map +0 -1
- package/dist/split/chunks/chunk-UBGJGMSG.js +0 -2
- /package/dist/split/chunks/{chunk-6I7GGFNJ.js.map → chunk-CBMAVRYG.js.map} +0 -0
- /package/dist/split/chunks/{chunk-2RBI6DVS.js.map → chunk-FBBHQU7S.js.map} +0 -0
- /package/dist/split/chunks/{chunk-UBGJGMSG.js.map → chunk-JWRQLKOQ.js.map} +0 -0
- /package/dist/split/chunks/{chunk-5Q4M6A3C.js.map → chunk-NITPUCPA.js.map} +0 -0
- /package/dist/split/chunks/{chunk-CIBJLAA3.js.map → chunk-T3FN37DU.js.map} +0 -0
- /package/dist/split/chunks/{chunk-BKLFDVOP.js.map → chunk-ULMHOGJ2.js.map} +0 -0
- /package/dist/split/chunks/{chunk-F3DVWVH3.js.map → chunk-ZJYPRSTO.js.map} +0 -0
package/README.ja.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
> **この README は 4.0 を説明しています。** 3.x から移行する場合は、[3.x からのアップグレード](#3x-からのアップグレード) と [3.x → 4.0 移行ガイド](https://github.com/wcstack/wcstack/blob/main/docs/migration-v4.ja.md) を読んでください。
|
|
6
6
|
|
|
7
|
+
> **どこまで読めば足りるか。** 入力と、そこから計算した値でできたアプリ —— たいていのアプリ —— に要るのは 5 つの節です: [基本的な使い方](#基本的な使い方)、[状態の更新](#状態の更新)(パスへ代入する。配列はその場で変えず、新しい配列を代入する)、[バインディング構文](#バインディング構文)、[構造ディレクティブ](#構造ディレクティブ)、[パス getter](#パス-getter算出プロパティ)(ワイルドカード getter と `$getAll`)。カスタム要素や I/O ノード(`<wcs-fetch>`・`<wcs-storage>`)を配線するページでは [Web Component バインディング](#web-component-バインディング) と [バインディング authority](#バインディング-authority-init--sync) を、行が新しいオブジェクトとして戻ってくるとき(取り直し、ストレージからの復元)は [`$listKeys`](#listkeys--再取得された行の同一性) を足してください。テキストの `{{ }}` と `<!--@@: path -->` は同じバインドです: `<template>` の外ではコメントの形(state が読み込まれる前に何も見せない)、中ではどちらでもかまいません。残りはリファレンスです: [機能の表](#この原理から導かれる機能)が問いごとに節を示し、各節が注意する誤りのほとんどは `npx @wcstack/lint` が報告します。
|
|
8
|
+
|
|
7
9
|
**これは便利な既存FWの別実装ではありません。パスがビューとモデルの契約になる——フロントエンドの外で確立した系譜を、Web標準の上に持ち込む試みです。**
|
|
8
10
|
|
|
9
11
|
多くのライブラリは、UI・状態・コンポーネントの結合点を JavaScript の中に置きます。`@wcstack/state` はそこを選びません。仮想DOMも、コンパイルも、hook も、selector も前提にせず、HTML とパス文字列だけを契約として UI と状態を結びつけます。
|
|
@@ -191,9 +193,9 @@
|
|
|
191
193
|
<script type="importmap">
|
|
192
194
|
{
|
|
193
195
|
"imports": {
|
|
194
|
-
"@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
195
|
-
"@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
196
|
-
"@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
196
|
+
"@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/core.js",
|
|
197
|
+
"@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/features/temporal.js",
|
|
198
|
+
"@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/features/scopes.js"
|
|
197
199
|
}
|
|
198
200
|
}
|
|
199
201
|
</script>
|
|
@@ -253,7 +255,7 @@
|
|
|
253
255
|
ページが名指したアドオンを、同じビルドから読み込みます。
|
|
254
256
|
|
|
255
257
|
```html
|
|
256
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
258
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/auto.js"></script>
|
|
257
259
|
<wcs-state features="scopes diagnostics">
|
|
258
260
|
<script type="module">
|
|
259
261
|
export default {
|
|
@@ -601,6 +603,8 @@ class YenInput extends HTMLElement {
|
|
|
601
603
|
|
|
602
604
|
どちらを選んでも、`element.value` とイベントから取り出す値は同じ論理状態を表していなければなりません(プロトコルの *Producer State Consistency Invariant*)。初期同期はプロパティを読み、以後の更新はイベントを読むからです。wcstack 内部でも両形が使われています —— `<wcs-fetch>` の `loading` は `getter` なしで真偽値を `detail` に載せ、`value` は `getter` で `detail.value` を読みます。DCC の `$bindables` が `getter: (e) => e.target[name]` を宣言するのは、サブパス書き込みには `detail` に載せる単一の値が無いからです。既定そのものを変える予定はありません。`e.detail` はすべての wc-bindable アダプタに対する規範(`@wc-bindable/core` の `bind()` とフレームワークアダプタも同じ既定)で、プロトコル上、既定の変更は新しいプロトコル識別子を要する破壊的変更に分類されています。
|
|
603
605
|
|
|
606
|
+
**state 自身の書き込みが返ってくるとき。** state がメンバーに書き(`element.value = v`)、要素がその setter の中から同期的に、同じ値(`Object.is`)でそのメンバーのイベントを dispatch したとき、そのイベントは state 自身の書き込みが返ってきたもの(書き戻し)です。state へは書かれず、そこに束ねたアクセサペアの setter も走りません。これはバインディングの一部で、`$behavior` のどのキーでも変わりません。後から dispatch したイベント(`await` の後、タイマーから)や、別のオブジェクト(コピー、JSON を読み直したもの)を運ぶイベントは要素自身の書き込みです。state へ届き、アクセサペアの setter が走ります。そうした setter は、state がすでに持っている値を受け取っても何も変わらないように書いてください。`<wcs-storage>` と getter / setter の組でフォーム全体を保存する形は、この規則の上に立っています: [フォームを 1 つのオブジェクトとして永続化する](../storage/README.ja.md#2-フォームを-1-つのオブジェクトとして永続化する)。
|
|
607
|
+
|
|
604
608
|
authority はバインディング単位で `#init=` により上書きできます:
|
|
605
609
|
|
|
606
610
|
| 値 | 初期同期 | 使える宣言 |
|
|
@@ -1301,7 +1305,7 @@ export default {
|
|
|
1301
1305
|
| `this.$getAll(path, indexes?)` | ワイルドカードパスにマッチする全ての値を取得 |
|
|
1302
1306
|
| `this.$setAll(path, indexes, value, options?)` | ワイルドカードパスにマッチする全アドレスへ一括書き込み |
|
|
1303
1307
|
| `this.$resolve(path, indexes, value?)` | ワイルドカードパスを特定のインデックスで解決 |
|
|
1304
|
-
| `this.$postUpdate(path)` |
|
|
1308
|
+
| `this.$postUpdate(path)` | 指定パスの更新通知を手動で発行。中の `*` は getter と同じく今の行の添字で、行のイベントのハンドラから呼ぶと `$postUpdate("rows.*.cells.*.text")` はその行のセルだけを通知する。複数の行を通知するには、それぞれを名指す(`` this.$postUpdate(`rows.${r}.cells.${c}.text`) ``) |
|
|
1305
1309
|
| `this.$dependOn(path)` | キャッシュ無効化のための依存関係を手動で登録 |
|
|
1306
1310
|
| `this.$untracked(fn)` | fn 実行中の依存追跡を抑止して値を読む |
|
|
1307
1311
|
| `this.$eq(path, key)` / `$eqPath(path, keyPath)` / `$eqIndex(path, level?)` | 鍵付き購読: 「`path` の値はこの行の鍵か」([鍵付き選択](#鍵付き選択eq--eqpath--eqindex)) |
|
|
@@ -1654,7 +1658,7 @@ export default {
|
|
|
1654
1658
|
|
|
1655
1659
|
`click`・`dblclick`・`input`・`change`・`submit`・`keydown`・`keyup`・`mousedown`・`mouseup`・`pointerdown`・`pointerup` の `on*:` バインディングは**委譲**されます: イベント種別ごとに 1 つのリスナーが、state がバインドするルート —— document、shadow root、あるいは Light DOM のマウントされたコンポーネントの中ではホスト要素 —— に置かれ、イベントが通過した要素のハンドラを内側から順に実行します。そのため:
|
|
1656
1660
|
|
|
1657
|
-
- ハンドラの中の `event.currentTarget` は要素ではなく、そのルートです。要素は `event.target` から探すか(`event.target.closest("li")`)、ハンドラが受け取るループインデックスを使ってください(`removeItem(event, index)`)。
|
|
1661
|
+
- ハンドラの中の `event.currentTarget` は要素ではなく、そのルートです。要素は `event.target` から探すか(`event.target.closest("li")`)、ハンドラが受け取るループインデックスを使ってください(`removeItem(event, index)`)。4.4 から、ソースが `currentTarget` を読む委譲されたハンドラが throw すると(`getBoundingClientRect is not a function`)、`diagnostics` アドオンがエラーの後に、ハンドラごとに 1 回その理由を言います(`[wcs/delegated-current-target]`、lint と同じ code)。
|
|
1658
1662
|
- 内側の `on*:` バインディングの `#stop` は、それより外側の `on*:` ハンドラを止めます。自分のコードが祖先に付けたリスナー(クリックできるカードへの `addEventListener`)は止められません: そのリスナーは、イベントがルートに届く前に実行済みです。
|
|
1659
1663
|
- 自分のコードが祖先で `stopPropagation()` を呼ぶと(モーダルの中身がオーバーレイにクリックを渡さないようにする場合など)、その内側の委譲されたハンドラはまったく実行されません。
|
|
1660
1664
|
- 別のルートの下へ移した要素(shadow root から `document.body` へ移したダイアログ)では、委譲されたハンドラはもう実行されません。
|
|
@@ -2099,6 +2103,7 @@ export default {
|
|
|
2099
2103
|
- 予約名 `$command` 自体は配列に含められない
|
|
2100
2104
|
- token は `$command` 配下にまとめられるためトップレベルの state 名前空間を汚さない。token と同名のリアクティブプロパティが共存できる
|
|
2101
2105
|
- `$command` 上の未宣言の名前にアクセスする(例: `this.$command.typo`)と `undefined` が返る。typo はその後の `.emit()` 呼び出しで `TypeError` として、あるいはバインディングの右辺で使った場合は、バインディングの取り付け時に `[wcs/token-undeclared] "$command.typo" is not declared in $commandTokens.`(`#1302`、did-you-mean 付き)として表面化する
|
|
2106
|
+
- トークンは何も取っておかない: どの `command.` 束縛も購読していない間の emit は消える。4.4 から、`diagnostics` アドオンがトークンごとに 1 回警告する(`[wcs/command-token-unbound]`)。束縛がまだ付いていない — 要素の定義が遅れる(autoloader、遅い CDN)、または同じ更新で描かれる `if:` の枝の中にある(`$renderedCallback` から emit する) — か、束縛が無い(その場合は lint も報告する)
|
|
2102
2107
|
|
|
2103
2108
|
### `command.<methodName>:` バインディング
|
|
2104
2109
|
|
|
@@ -2640,6 +2645,7 @@ class MyChip extends HTMLElement {
|
|
|
2640
2645
|
|
|
2641
2646
|
- 3.x はプロパティの後に属性にも書いていました(`String(value)`、オブジェクトは JSON)。setter で反映する要素は影響を受けません。setter を持たず、binder が属性を書くことに頼っていた要素には、setter が要ります(移行ガイド §3.4)。
|
|
2642
2647
|
- ネイティブ HTML 要素は `inputs` を完全に無視します —— 宣言が効くのは `static wcBindable` を公開するカスタム要素だけです。
|
|
2648
|
+
- `for:` の行と `if:` の枝は、挿入する前にバインドします。その中のカスタム要素は、`connectedCallback` が走る前に入力(`element.value = …`)を受け取ります。ページの直下では、要素は先に接続されています。既定値から描く `connectedCallback` は、setter が受け取ったものを上書きします —— 今のプロパティから描いてください(外部の評価では、金額欄が、まだ採用していない入力をこれで失いました。ページを 3.x から 4.0 に上げたときです)。もう 1 つの早い書き込み、要素が定義される前に代入されたプロパティは、[I/O ノードのガイドライン](../../docs/async-io-node-guidelines.ja.md)の `upgradeProperties` の手順が扱います。
|
|
2643
2649
|
|
|
2644
2650
|
## コンポーネント機構の選び方
|
|
2645
2651
|
|
|
@@ -3026,11 +3032,14 @@ dropped. Validate statically: npx @wcstack/lint <file>.
|
|
|
3026
3032
|
| ネストしたパスの打ち間違い(`user.nmae`) | `console.warn`(`wcs/binding-path-missing`)。更新は届かないままなので、直すのは書き手 |
|
|
3027
3033
|
| トップレベルのパスの打ち間違い(`cout`) | 読み取り時に throw —— `[wcs/binding-path-missing] Path "cout" does not exist on the state tree. Did you mean "count"?`(`#301`)—— そのためバインディングの適用が失敗し、ほかのバインディングの失敗と同じく報告される |
|
|
3028
3034
|
| `$watch` のキーの打ち間違い | `console.warn`(`wcs/watch-path-missing`)。単一セグメントでも報告する |
|
|
3035
|
+
| `$postUpdate` のパスの打ち間違い(`cells` に対する `` `rows.${r}.cell.${c}.text` ``) | 4.4 から: `console.warn`(`wcs/post-update-path-missing`)。単一セグメントでも報告する — その呼び出しは何も更新しない |
|
|
3036
|
+
| state が宣言していないトップレベルのキーへの書き込みで、近い宣言済みのキーがあるもの(`this.selectedID` と `selectedId`) | 4.4 から: キーごとに 1 回 `console.warn`(`wcs/write-path-missing`) — 書き込みは新しいキーを作るだけで、宣言したキーに束ねたものは変わらない。近い名前の無い新しいキーは、state がわざと足すことがあるので報告しない |
|
|
3037
|
+
| getter が読むパスの打ち間違い(`this["items.*.pirce"]`、`this.$getAll("items.*.pirce", [])`) | 4.4 から: パスごとに 1 回、読んだ getter の名前を添えて `console.warn`(`wcs/read-path-missing`)。近い名前があるときだけ — 読みは `undefined` になり、getter がそこから計算する値は違う。getter がそのパスを読んだあとで調べるので、後から足した行は描かれたときに調べる。トップレベルのキー(`this.totl`)は読んだ時点で throw する(`#301`)。`@wcstack/lint` は getter とメソッドの中の同じ読みを、実行せずに検査する |
|
|
3029
3038
|
|
|
3030
3039
|
判定は**過小近似**です。静的に決められない形では黙ります —— 誤検知でページを騒がせないことを優先しているためで、以下はすべて警告しません:
|
|
3031
3040
|
|
|
3032
3041
|
- 親が `null` / `undefined`(初期値 `null` に後から代入する形)
|
|
3033
|
-
-
|
|
3042
|
+
- どの行にも無い行フィールド —— ただし、行に近い名前がある(打ち間違いらしい。`name` の横の `.nmae`)なら警告します。行はデータで、任意のフィールド(ページが自分の行に付ける `pending` など)はほかの行にはありません。4.4 から。ほかの行にあるフィールドが最初の行に無いだけの場合も警告しません(4.3 から)
|
|
3034
3043
|
- 途中の getter の戻り値のサブプロパティ
|
|
3035
3044
|
- まだ読み込み中のボリュームのマウントパス配下のパス —— ボリュームが決着した時点で検査される
|
|
3036
3045
|
- `$` 始まりの予約名前空間(`$command.*` など)
|
|
@@ -3588,7 +3597,7 @@ const html = await renderToString(template, {
|
|
|
3588
3597
|
|---------|------|
|
|
3589
3598
|
| **サーバー** | `renderToString()` が happy-dom でテンプレートを実行、`$connectedCallback`(`fetch()` 含む)を実行し、全バインディングを適用して、`<wcs-state>` の前に `<wcs-ssr>` 要素を置いたレンダリング済み HTML を出力。行・枝・テキストバインディングにはコメントで印を付ける。`$watch` のハンドラは実行されず、stream は `initial` の値のまま |
|
|
3590
3599
|
| **クライアント** | `<wcs-state enable-ssr>` は自分のソースから state を読み込み、その上にスナップショットのデータを被せ(getter・setter・メソッドは残る)、`$connectedCallback` をスキップし、サーバーの DOM をその場でバインドする。行と枝はその場で引き継がれ —— その中のカスタム要素は 1 回だけ接続され、切断・再接続されることはない —— バインディングを得る。テンプレートに合わなくなったサーバーの行はその場所で描画し直され、クライアントが描画しない行や枝(行が少ない、別の枝)はページがバインドされた時点で取り除かれる。ボリュームはマウントパスの位置でサーバーのデータを引き継ぐ |
|
|
3591
|
-
| **バージョン不一致** | `<wcs-ssr version>` の major.minor が違うスナップショットは `console.warn` を 1 回出して破棄され、ページはサーバーが無かったかのようにクライアントで描画される: state は自分のソースから読み込まれ、`$connectedCallback`
|
|
3600
|
+
| **バージョン不一致** | `<wcs-ssr version>` の major.minor が違うスナップショットは `console.warn` を 1 回出して破棄され、ページはサーバーが無かったかのようにクライアントで描画される: state は自分のソースから読み込まれ、`$connectedCallback` が走り、サーバーの行・枝・マーカーは、描画元のテンプレートに場所を譲る。4.4 から、場所を譲るのはページを計画できた後: このクライアントが計画できないマークアップ(新しい minor のフィルタ引数など)は、サーバーの HTML をそのまま残して初期化に失敗する |
|
|
3592
3601
|
|
|
3593
3602
|
### `enable-ssr` の動作
|
|
3594
3603
|
|
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
> **This README describes 4.0.** Coming from 3.x? Read [Upgrading from 3.x](#upgrading-from-3x) and the [3.x → 4.0 migration guide](https://github.com/wcstack/wcstack/blob/main/docs/migration-v4.md).
|
|
6
6
|
|
|
7
|
+
> **How much to read.** An app of inputs and the values computed from them — most apps — needs five sections: [Basic usage](#basic-usage), [Updating state](#updating-state) (assign to the path; a new array, not an array changed in place), [Binding syntax](#binding-syntax), [Structural directives](#structural-directives) and [Path getters](#path-getters-computed-properties) (wildcard getters and `$getAll`). Add [Web Component binding](#web-component-binding) and [Binding authority](#binding-authority-init--sync) when the page wires custom elements or I/O nodes (`<wcs-fetch>`, `<wcs-storage>`), and [`$listKeys`](#listkeys--identity-for-refetched-rows) when rows come back as new objects (a refetch, a restore from storage). For text, `{{ }}` and `<!--@@: path -->` are the same binding: write the comment form outside a `<template>` — it shows nothing before the state loads — and either inside. The rest is reference: the [features table](#features-derived-from-this-principle) names the section for each question, and `npx @wcstack/lint` reports most of the mistakes the sections warn about.
|
|
8
|
+
|
|
7
9
|
**This is not another convenient frontend framework. It brings a lineage established outside frontend development — the one where a path string is the contract between view and model — onto web standards.**
|
|
8
10
|
|
|
9
11
|
Most libraries place the coupling point between UI, state, and components inside JavaScript. `@wcstack/state` does not. It assumes no virtual DOM, no compilation step, no hooks, no selectors. UI and state are connected by HTML and path strings alone.
|
|
@@ -192,9 +194,9 @@ leaves features out, it can compose them instead:
|
|
|
192
194
|
<script type="importmap">
|
|
193
195
|
{
|
|
194
196
|
"imports": {
|
|
195
|
-
"@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
196
|
-
"@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
197
|
-
"@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
197
|
+
"@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/core.js",
|
|
198
|
+
"@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/features/temporal.js",
|
|
199
|
+
"@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/features/scopes.js"
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
</script>
|
|
@@ -254,7 +256,7 @@ receptacle the owning add-on fills on install).
|
|
|
254
256
|
core, and the add-ons the page names from the same build.
|
|
255
257
|
|
|
256
258
|
```html
|
|
257
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.
|
|
259
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.5.0/dist/split/auto.js"></script>
|
|
258
260
|
<wcs-state features="scopes diagnostics">
|
|
259
261
|
<script type="module">
|
|
260
262
|
export default {
|
|
@@ -607,6 +609,8 @@ class YenInput extends HTMLElement {
|
|
|
607
609
|
|
|
608
610
|
Whichever you pick, `element.value` and the value extracted from the event must be the same logical state (the protocol's *Producer State Consistency Invariant*): the initial sync reads the property, every later update reads the event. Both shapes are in use inside wcstack — `<wcs-fetch>`'s `loading` dispatches the boolean as `detail` with no `getter`, its `value` reads `detail.value` through one — and DCC `$bindables` declare `getter: (e) => e.target[name]` because a sub-path write has no single value to put in `detail`. The default itself is not going to change: it is normative for every wc-bindable adapter (`@wc-bindable/core`'s `bind()` and the framework adapters implement the same `e.detail`), and the protocol classes a different default as a breaking change requiring a new protocol identifier.
|
|
609
611
|
|
|
612
|
+
**State's own write coming back.** When state writes a member (`element.value = v`) and the element dispatches the member's event from that setter, synchronously, with the same value (`Object.is`), the event is state's own write echoing back: it is not written to the state, and the setter of an accessor pair bound there does not run. This is part of the binding — no `$behavior` key changes it. An event dispatched later (after an `await`, from a timer) or carrying another object (a copy, JSON parsed again) is the element's own write: it reaches the state, and an accessor pair's setter runs. Write such a setter so that receiving what the state already holds changes nothing. Persisting a whole form through `<wcs-storage>` with a getter / setter pair relies on this rule: [Persisting a form as one object](../storage/README.md#2-persisting-a-form-as-one-object).
|
|
613
|
+
|
|
610
614
|
Override the authority per binding with `#init=`:
|
|
611
615
|
|
|
612
616
|
| Value | Initial sync | Allowed on |
|
|
@@ -1307,7 +1311,7 @@ Inside state objects (getters / methods), the following APIs are available via `
|
|
|
1307
1311
|
| `this.$getAll(path, indexes?)` | Get all values matching a wildcard path |
|
|
1308
1312
|
| `this.$setAll(path, indexes, value, options?)` | Write to every address matching a wildcard path |
|
|
1309
1313
|
| `this.$resolve(path, indexes, value?)` | Resolve a wildcard path with specific indexes |
|
|
1310
|
-
| `this.$postUpdate(path)` | Manually trigger update notification for a path |
|
|
1314
|
+
| `this.$postUpdate(path)` | Manually trigger update notification for a path. A `*` in it is the current row's index, as in a getter: called from an event handler of a row, `$postUpdate("rows.*.cells.*.text")` announces that row's cell only. To announce several rows, name each one (`` this.$postUpdate(`rows.${r}.cells.${c}.text`) ``) |
|
|
1311
1315
|
| `this.$dependOn(path)` | Manually register a dependency for cache invalidation |
|
|
1312
1316
|
| `this.$untracked(fn)` | Read values inside fn without registering dependencies |
|
|
1313
1317
|
| `this.$eq(path, key)` / `$eqPath(path, keyPath)` / `$eqIndex(path, level?)` | Keyed subscription for "is this row's key the value of `path`?" (see [Keyed selection](#keyed-selection-eq--eqpath--eqindex)) |
|
|
@@ -1660,7 +1664,7 @@ export default {
|
|
|
1660
1664
|
|
|
1661
1665
|
`on*:` bindings for `click`, `dblclick`, `input`, `change`, `submit`, `keydown`, `keyup`, `mousedown`, `mouseup`, `pointerdown` and `pointerup` are **delegated**: one listener per event type sits on the root the state binds — the document, a shadow root, or, inside a Light DOM mounted component, the host element — and it runs the handlers of the elements the event passed, innermost first. So:
|
|
1662
1666
|
|
|
1663
|
-
- `event.currentTarget` in the handler is that root, not the element. Find the element from `event.target` (`event.target.closest("li")`), or use the loop index the handler receives (`removeItem(event, index)`).
|
|
1667
|
+
- `event.currentTarget` in the handler is that root, not the element. Find the element from `event.target` (`event.target.closest("li")`), or use the loop index the handler receives (`removeItem(event, index)`). Since 4.4, when a delegated handler whose source reads `currentTarget` throws (`getBoundingClientRect is not a function`), the `diagnostics` add-on says why after the error, once per handler (`[wcs/delegated-current-target]`, the lint's code).
|
|
1664
1668
|
- `#stop` on an inner `on*:` binding stops the `on*:` handlers further out. It cannot stop a listener your own code added to an ancestor (`addEventListener` on a clickable card): that listener ran before the event reached the root.
|
|
1665
1669
|
- Your code calling `stopPropagation()` on an ancestor (a modal's content keeping clicks from the overlay) keeps the delegated handlers inside it from running at all.
|
|
1666
1670
|
- An element moved under another root (a dialog moved from a shadow root to `document.body`) no longer runs its delegated handlers.
|
|
@@ -2109,6 +2113,7 @@ export default {
|
|
|
2109
2113
|
- The reserved name `$command` itself cannot appear in the array
|
|
2110
2114
|
- Tokens are gathered under `$command` so they do not pollute the top-level state namespace; reactive properties with the same name as a token can coexist
|
|
2111
2115
|
- Accessing an undeclared name on `$command` (e.g. `this.$command.typo`) returns `undefined`. The typo then surfaces as a `TypeError` on the subsequent `.emit()` call, or — when used as a binding right-hand side — as `[wcs/token-undeclared] "$command.typo" is not declared in $commandTokens.` (`#1302`, with a did-you-mean) when the binding is attached
|
|
2116
|
+
- A token keeps nothing: an emit while no `command.` binding subscribes to it is lost. Since 4.4 the `diagnostics` add-on warns about it once per token (`[wcs/command-token-unbound]`). The binding may not be attached yet — its element is defined later (an autoloader, a slow CDN), or it is in an `if:` branch the same update renders, so emit from `$renderedCallback` — or there may be none (the lint reports that case too)
|
|
2112
2117
|
|
|
2113
2118
|
### `command.<methodName>:` Binding
|
|
2114
2119
|
|
|
@@ -2650,6 +2655,7 @@ Notes:
|
|
|
2650
2655
|
|
|
2651
2656
|
- 3.x also wrote the attribute after the property (`String(value)`, JSON for an object). An element that reflects in its setter is unaffected; one that has no setter and relied on the binder to write its attribute needs a setter now (Migration guide, §3.4).
|
|
2652
2657
|
- Native HTML elements ignore `inputs` entirely — the declaration only applies to custom elements that expose `static wcBindable`.
|
|
2658
|
+
- A `for:` row or an `if:` branch is bound before it is inserted: a custom element in it receives its inputs (`element.value = …`) before its `connectedCallback` runs. At page level the element is connected first. A `connectedCallback` that renders from its defaults overwrites what the setters received — render from the current properties instead. (An outside evaluation's amount field lost an entry it had not accepted yet this way when its page moved from 3.x to 4.0.) The other early write, a property set before the element is defined, is the [I/O node guidelines](../../docs/async-io-node-guidelines.md)' `upgradeProperties` step.
|
|
2653
2659
|
|
|
2654
2660
|
## Choosing a Component Mechanism
|
|
2655
2661
|
|
|
@@ -3036,11 +3042,15 @@ dropped. Validate statically: npx @wcstack/lint <file>.
|
|
|
3036
3042
|
| Typo in a nested path (`user.nmae`) | `console.warn` (`wcs/binding-path-missing`). Updates still never arrive — you fix it |
|
|
3037
3043
|
| Typo in a top-level path (`cout`) | Throws on read — `[wcs/binding-path-missing] Path "cout" does not exist on the state tree. Did you mean "count"?` (`#301`) — so the binding fails to apply, reported like any binding failure |
|
|
3038
3044
|
| Typo in a `$watch` key | `console.warn` (`wcs/watch-path-missing`), reported even for a single segment |
|
|
3045
|
+
| Typo in a `$postUpdate` path (`` `rows.${r}.cell.${c}.text` `` for `cells`) | Since 4.4: `console.warn` (`wcs/post-update-path-missing`), a single segment too — the call updates nothing |
|
|
3046
|
+
| A write of a top-level key the state does not declare, beside a near one (`this.selectedID` and `selectedId`) | Since 4.4: `console.warn` (`wcs/write-path-missing`), once per key — the write creates a new key, and nothing bound to the declared one changes. A new key with no near name is not reported: a state may add keys on purpose |
|
|
3047
|
+
| Typo in a path a getter reads (`this["items.*.pirce"]`, `this.$getAll("items.*.pirce", [])`) | Since 4.4: `console.warn` (`wcs/read-path-missing`), once per path, naming the getter, beside a near name only — the read gives `undefined`, and what the getter computes from it is wrong. Checked once a getter has read the path: a row added later is checked when it renders. A top-level key (`this.totl`) throws on read (`#301`). `@wcstack/lint` checks the same reads in getters and methods without running them |
|
|
3039
3048
|
|
|
3040
3049
|
The check **under-approximates**: it stays silent for anything it cannot decide statically, because a false alarm costs more than a missed one. None of these warn:
|
|
3041
3050
|
|
|
3042
3051
|
- A `null` / `undefined` parent (the "seed as `null`, assign later" shape)
|
|
3043
3052
|
- Row fields of a list that starts empty (the row shape is unknown)
|
|
3053
|
+
- A row field no row has, unless a row has a near name (a likely typo, `.nmae` beside `name`) — rows are data, and an optional field (a `pending` the page puts on its own rows) is absent from the others. Since 4.4; the first row lacking a field another row has does not warn either (4.3)
|
|
3044
3054
|
- Sub-properties of an intermediate getter's return value
|
|
3045
3055
|
- Paths under the mount path of a volume that is still loading — they are checked once it settles
|
|
3046
3056
|
- Reserved `$` namespaces (`$command.*` and friends)
|
|
@@ -3602,7 +3612,7 @@ That's it. The client-side `@wcstack/state` finds the `<wcs-ssr>` element, takes
|
|
|
3602
3612
|
|-------|-------------|
|
|
3603
3613
|
| **Server** | `renderToString()` runs your template in happy-dom, executes `$connectedCallback` (including `fetch()`), applies all bindings, and outputs rendered HTML with a `<wcs-ssr>` element before the `<wcs-state>`. Rows, branches and text bindings are marked with comments. `$watch` handlers do not run, and streams keep their `initial` value |
|
|
3604
3614
|
| **Client** | `<wcs-state enable-ssr>` loads its state from its own source, takes the snapshot's data over it (getters, setters and methods stay), skips `$connectedCallback`, and binds the server's DOM in place. Rows and branches are adopted where they are — a custom element in them is connected once and never disconnected and connected again — and get their bindings. A server row that no longer fits its template is rendered again in its place; rows and branches the client does not render (fewer rows, another branch) go as soon as the page is bound. A volume adopts the server's data at its mount path |
|
|
3605
|
-
| **Version mismatch** | A snapshot whose `<wcs-ssr version>` has another major.minor is discarded with one `console.warn`, and the page renders on the client as if there were no server: the state loads from its own source, `$connectedCallback` runs, and the server's rows, branches and markers give way to the templates they were rendered from |
|
|
3615
|
+
| **Version mismatch** | A snapshot whose `<wcs-ssr version>` has another major.minor is discarded with one `console.warn`, and the page renders on the client as if there were no server: the state loads from its own source, `$connectedCallback` runs, and the server's rows, branches and markers give way to the templates they were rendered from. Since 4.4 they give way only after the page was planned: markup this client cannot plan (a newer minor's filter argument, for example) fails its initialization with the server's HTML left as it was |
|
|
3606
3616
|
|
|
3607
3617
|
### What `enable-ssr` Does
|
|
3608
3618
|
|