@wcstack/state 4.9.0 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.ja.md +33 -12
  2. package/README.md +33 -12
  3. package/dist/auto.min.js +1 -1
  4. package/dist/auto.min.js.map +1 -1
  5. package/dist/index.esm.js +1 -1
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/manifest.esm.js +29 -31
  8. package/dist/parser.esm.js +4 -1
  9. package/dist/split/auto.js +1 -1
  10. package/dist/split/chunks/chunk-65B2K2XD.js +2 -0
  11. package/dist/split/chunks/{chunk-J3PTKUWV.js.map → chunk-65B2K2XD.js.map} +1 -1
  12. package/dist/split/chunks/{chunk-ISBASPV2.js → chunk-FFXJVUTT.js} +2 -2
  13. package/dist/split/chunks/{chunk-LWRYFYOC.js → chunk-FT7OW7CG.js} +2 -2
  14. package/dist/split/chunks/chunk-GU7IAIWW.js +2 -0
  15. package/dist/split/chunks/chunk-GU7IAIWW.js.map +1 -0
  16. package/dist/split/chunks/chunk-IWY3I2DG.js +2 -0
  17. package/dist/split/chunks/{chunk-47I6RYV3.js.map → chunk-IWY3I2DG.js.map} +1 -1
  18. package/dist/split/chunks/{chunk-FQUDP3XK.js → chunk-JCQTA35B.js} +2 -2
  19. package/dist/split/chunks/chunk-MZVF2WEY.js +2 -0
  20. package/dist/split/chunks/{chunk-P3JKSHRN.js → chunk-PTFWXISY.js} +2 -2
  21. package/dist/split/chunks/{chunk-BCL75PFH.js → chunk-SJRJALR4.js} +2 -2
  22. package/dist/split/chunks/chunk-TB52U4QV.js +2 -0
  23. package/dist/split/chunks/chunk-TB52U4QV.js.map +1 -0
  24. package/dist/split/chunks/chunk-UE6JK2ZA.js +2 -0
  25. package/dist/split/chunks/chunk-UE6JK2ZA.js.map +1 -0
  26. package/dist/split/core.js +1 -1
  27. package/dist/split/features/devtools.js +1 -1
  28. package/dist/split/features/diagnostics.js +1 -1
  29. package/dist/split/features/diagnostics.js.map +1 -1
  30. package/dist/split/features/formats.js +1 -1
  31. package/dist/split/features/formats.js.map +1 -1
  32. package/dist/split/features/list-keys.js +1 -1
  33. package/dist/split/features/native-commands.js +1 -1
  34. package/dist/split/features/recursion.js +1 -1
  35. package/dist/split/features/scopes.js +1 -1
  36. package/dist/split/features/ssr.js +1 -1
  37. package/dist/split/features/temporal.js +1 -1
  38. package/dist/wcs-manifest.json +9 -6
  39. package/package.json +1 -1
  40. package/dist/split/chunks/chunk-47I6RYV3.js +0 -2
  41. package/dist/split/chunks/chunk-EBIQH2HE.js +0 -2
  42. package/dist/split/chunks/chunk-EBIQH2HE.js.map +0 -1
  43. package/dist/split/chunks/chunk-J3PTKUWV.js +0 -2
  44. package/dist/split/chunks/chunk-SOFOV2OG.js +0 -2
  45. package/dist/split/chunks/chunk-SOFOV2OG.js.map +0 -1
  46. package/dist/split/chunks/chunk-TS2HEHBQ.js +0 -2
  47. package/dist/split/chunks/chunk-YIDHPXYQ.js +0 -2
  48. package/dist/split/chunks/chunk-YIDHPXYQ.js.map +0 -1
  49. /package/dist/split/chunks/{chunk-ISBASPV2.js.map → chunk-FFXJVUTT.js.map} +0 -0
  50. /package/dist/split/chunks/{chunk-LWRYFYOC.js.map → chunk-FT7OW7CG.js.map} +0 -0
  51. /package/dist/split/chunks/{chunk-FQUDP3XK.js.map → chunk-JCQTA35B.js.map} +0 -0
  52. /package/dist/split/chunks/{chunk-TS2HEHBQ.js.map → chunk-MZVF2WEY.js.map} +0 -0
  53. /package/dist/split/chunks/{chunk-P3JKSHRN.js.map → chunk-PTFWXISY.js.map} +0 -0
  54. /package/dist/split/chunks/{chunk-BCL75PFH.js.map → chunk-SJRJALR4.js.map} +0 -0
package/README.ja.md CHANGED
@@ -193,9 +193,9 @@
193
193
  <script type="importmap">
194
194
  {
195
195
  "imports": {
196
- "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/core.js",
197
- "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/features/temporal.js",
198
- "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/features/scopes.js"
196
+ "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/core.js",
197
+ "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/features/temporal.js",
198
+ "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/features/scopes.js"
199
199
  }
200
200
  }
201
201
  </script>
@@ -261,7 +261,7 @@
261
261
  ページが名指したアドオンを、同じビルドから読み込みます。
262
262
 
263
263
  ```html
264
- <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/auto.js"></script>
264
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/auto.js"></script>
265
265
  <wcs-state features="scopes diagnostics">
266
266
  <script type="module">
267
267
  export default {
@@ -1764,13 +1764,27 @@ export default {
1764
1764
 
1765
1765
  | フィルタ | 説明 | 例 |
1766
1766
  |---|---|---|
1767
- | `date(loc?, style?)` | 日付フォーマット | `createdAt\|date` / `createdAt\|date(ja-JP)` / `createdAt\|date(en-US,medium)` → `"Oct 13, 2026"` |
1768
- | `time(loc?, style?)` | 時刻フォーマット | `createdAt\|time` / `createdAt\|time(short)` |
1769
- | `datetime(loc?, style?)` | 日付 + 時刻 | `createdAt\|datetime(en-US)` / `createdAt\|datetime(ja-JP,long)` |
1770
- | `ymd(sep?)` | YYYY-MM-DD(既定は `-`) | `createdAt\|ymd` / `createdAt\|ymd(/)` |
1771
- | `hms(sep?)` | HH:MM:SS(既定は `:`) | `createdAt\|hms` / `createdAt\|hms(-)` |
1767
+ | `date(loc?, style?, zone?)` | 日付フォーマット(`zone?` は 4.10 から) | `createdAt\|date` / `createdAt\|date(ja-JP)` / `createdAt\|date(en-US,medium)` → `"Oct 13, 2026"` / `opensAt\|date(ja-JP,full,Asia/Tokyo)` |
1768
+ | `time(loc?, style?, zone?)` | 時刻フォーマット(`zone?` は 4.10 から) | `createdAt\|time` / `createdAt\|time(short)` / `opensAt\|time(,short,Asia/Tokyo)` |
1769
+ | `datetime(loc?, style?, zone?)` | 日付 + 時刻(`zone?` は 4.10 から) | `createdAt\|datetime(en-US)` / `createdAt\|datetime(ja-JP,long)` / `createdAt\|datetime(,,UTC)` |
1770
+ | `ymd(sep?)` | YYYY-MM-DD(既定は `-`)。実行環境のタイムゾーンの日付 | `createdAt\|ymd` / `createdAt\|ymd(/)` |
1771
+ | `hms(sep?)` | HH:MM:SS(既定は `:`)。実行環境のタイムゾーンの時刻 | `createdAt\|hms` / `createdAt\|hms(-)` |
1772
1772
 
1773
- **5 つとも `Date` を取ります**(タイムスタンプではありません)。他の型は「requires a date value」で拒否されます。値が無いとき(`undefined` / `null`)は、4.7 から何も描きません。JSON から読み込んだ state は数値か ISO 文字列を持つので、束縛ではなく読み出し側で変換してください: `get createdAt() { return new Date(this.createdAtMs); }`。`loc?` の既定はページのロケール([ロケール](#ロケール))で、**適用のたびに**読み直されるため、束縛を組み立てた後に確定したロケールも反映されます(明示引数 `date(ja-JP)` は束縛式の一部なので組み立て時に固定されます)。`style?` は `full`・`long`・`medium`・`short`(Intl の `dateStyle` / `timeStyle`)で、無ければロケールの数字の書式(`2026/10/13`)、書式だけならページのロケールです(`date(medium)`)。`currency` の通貨は束縛を組み立てるときに確かめ、数は `loc` が無ければページのロケールで書きます。複数形などほかの書式は getter で書きます(`Intl.PluralRules`、`Intl.NumberFormat`)。
1773
+ **5 つとも `Date` を取ります**(タイムスタンプではありません)。他の型は「requires a date value」で拒否されます。値が無いとき(`undefined` / `null`)は、4.7 から何も描きません。JSON から読み込んだ state は数値か ISO 文字列を持つので、束縛ではなく読み出し側で変換してください: `get createdAt() { return new Date(this.createdAtMs); }`。日付だけの文字列は瞬間ではなく日です: `new Date("2026-10-29")` は UTC の 0 時で、グリニッジより西では前の日になります —— `new Date(y, m - 1, d)` がページの動く場所のその日です(4.9 から `#29` もそう言います)。`loc?` の既定はページのロケール([ロケール](#ロケール))で、**適用のたびに**読み直されるため、束縛を組み立てた後に確定したロケールも反映されます(明示引数 `date(ja-JP)` は束縛式の一部なので組み立て時に固定されます)。`style?` は `full`・`long`・`medium`・`short`(Intl の `dateStyle` / `timeStyle`)で、無ければロケールの数字の書式(`2026/10/13`)、書式だけならページのロケールです(`date(medium)`)。`currency` の通貨は束縛を組み立てるときに確かめ、数は `loc` が無ければページのロケールで書きます。複数形などほかの書式は getter で書きます(`Intl.PluralRules`、`Intl.NumberFormat`)。
1774
+
1775
+ **タイムゾーン。** `zone?` が無ければ、`date`・`time`・`datetime` は実行環境のタイムゾーンで書きます —— ブラウザでは**読み手の**、SSR ではサーバーのタイムゾーンです。ページのロケールとは関係ありません。ある場所の時刻は、よそから読むと違って見えます: 東京の図書館の 10 月 29 日(木)09:00〜20:00 の開館時間は、ロサンゼルスから読むと `2026年10月28日水曜日`・`17:00:00`・`4:00:00` です。4.10 から、`zone?` で書くタイムゾーンを指定できます —— IANA のタイムゾーン、Intl の `timeZone` です(`Asia/Tokyo`・`America/New_York`・`UTC`)。どこの読み手にもその場所の時刻が見えます:
1776
+
1777
+ ```html
1778
+ <html lang="ja">
1779
+ <!-- state: get opensAt() { return new Date("2026-10-29T09:00:00+09:00"); }、closesAt は同じく 20:00 -->
1780
+ <p>
1781
+ <span data-wcs="textContent: opensAt|date(,full,Asia/Tokyo)"></span>
1782
+ <span data-wcs="textContent: opensAt|time(,short,Asia/Tokyo)"></span>–<span data-wcs="textContent: closesAt|time(,short,Asia/Tokyo)"></span>
1783
+ </p>
1784
+ <!-- 東京から読んでもロサンゼルスから読んでも 2026年10月29日木曜日 9:00–20:00 -->
1785
+ ```
1786
+
1787
+ 空の引数は既定のままです: `time(,short,Asia/Tokyo)` はページのロケール、`date(ja-JP,,Asia/Tokyo)` は数字の書式、`datetime(,,Asia/Tokyo)` はその両方。タイムゾーンだけを書いたときも、`/` を含むか `UTC` なら両方とも既定です(`date(Asia/Tokyo)`、`datetime(UTC)`)。`/` の無いほかのタイムゾーンは 3 つ目に書きます(`date(,,GMT)`)—— 1 つ目に書くとロケールとして読みます。Intl の取らないタイムゾーンは束縛を組み立てるときに拒否され(`#54`)、書式の場所に書いたタイムゾーン(`date(ja-JP,Asia/Tokyo)`)は 3 つ目に書くよう言われます(`#52`)。`ymd` と `hms` はタイムゾーンを取りません: `Date` を実行環境のタイムゾーンで読む(`getFullYear()` 〜 `getSeconds()`)ので、ある場所の日付や時刻にはタイムゾーン付きの `date` / `time` か getter を使います。
1774
1788
 
1775
1789
  ### 真偽値 / デフォルト
1776
1790
 
@@ -2610,7 +2624,7 @@ $renderedCallback(paths) {
2610
2624
  | ハンドラ間 | `$watch` の宣言順 | **宣言を並べ替える** |
2611
2625
  | 同一パスの行間 | `indexes` 昇順 | 固定 |
2612
2626
 
2613
- **機構間の層を動かす唯一のもの**が、`state` 参加者を受け付ける `<wcs-view-transition>` です。バインディング適用 —— したがって `$renderedCallback` —— がフレームで着地する一方、`$watch`・`$stream` restart は state アドレスを消費し DOM を見ないので、drain がキューされた microtask に留まります。タグがある間の順序は `$watch` → `$stream` restart → `$renderedCallback` です。この層を並べ替えるものはページ上でこれ 1 つだけです。[docs/timing-and-firing-contract.ja.md](https://github.com/wcstack/wcstack/blob/main/docs/timing-and-firing-contract.ja.md) §4.3 を参照してください。
2627
+ **機構間の層を動かす唯一のもの**が、`state` 参加者を受け付ける `<wcs-view-transition>` です。バインディング適用 —— したがって `$renderedCallback` —— がフレームで着地します。4.10 から、`$watch` と `$stream` restart はそれを待つので、順序は上のとおりのまま、1 フレーム遅れるだけです。それらが書いたもの(`$watch` が state に写した一覧など)も同じトランジションの中で当たります。4.9 までは drain がキューされた microtask に留まって先に走り(`$watch` → `$stream` restart → `$renderedCallback`)、ログインの後に画面を移す `$watch` が、ルートのガードが読むセッションを `<wcs-storage>` が保存するより先に走っていました。この層を動かすものはページ上でこれ 1 つだけです。[docs/timing-and-firing-contract.ja.md](https://github.com/wcstack/wcstack/blob/main/docs/timing-and-firing-contract.ja.md) §4.3 を参照してください。
2614
2628
 
2615
2629
  主なルール:
2616
2630
 
@@ -2977,8 +2991,9 @@ li {
2977
2991
  | 49 | | `<wcs-state src="…"> failed to initialize.` |
2978
2992
  | 50 | | `<wcs-state src="…"> $connectedCallback failed.` |
2979
2993
  | 51 | | `<wcs-state src="…"> $disconnectedCallback failed.` |
2980
- | 52 | | `filter <name> takes a style of full, long, medium or short, not "<style>"` |
2994
+ | 52 | | `filter <name> takes a style of full, long, medium or short, not "<style>"`(4.10 から、書式の場所のタイムゾーン(`/` を含むものと `UTC`)には `— a time zone is the third argument, after the locale and the style, and an empty argument keeps its default: <name>(,,<zone>), <name>(en-US,long,<zone>)` が付く) |
2981
2995
  | 53 | | `filter currency takes an ISO 4217 currency code (JPY, USD, EUR), not "<code>"` |
2996
+ | 54 | | `filter <name> takes an IANA time zone (Asia/Tokyo, America/New_York, UTC), not "<zone>"`(4.10 から) |
2982
2997
  | 101 | `binding-syntax` | `Invalid bindText: "<text>". Missing ':' separator between propPart and statePart.` |
2983
2998
  | 102 | `binding-syntax` | `"<text>": "<keyword>" takes no modifiers or filters on its left side — write "<keyword>:".` |
2984
2999
  | 103 | `binding-syntax` | `"<text>": "else" takes no value — write "else:".` |
@@ -3053,10 +3068,12 @@ dropped. Validate statically: npx @wcstack/lint <file>.
3053
3068
  判定は**過小近似**です。静的に決められない形では黙ります —— 誤検知でページを騒がせないことを優先しているためで、以下はすべて警告しません:
3054
3069
 
3055
3070
  - 親が `null` / `undefined`(初期値 `null` に後から代入する形)
3071
+ - キーの下の空のオブジェクト(`fieldErrors: {}` に後から `this.fieldErrors = { date: "…" }` を代入する形)— 種が `{}` の同じ形。4.10 から。4.9 までは、その下の束縛ごとに報告し、「updates … will be silently dropped」と言っていたが、そうではない: オブジェクトを置き換えれば更新される。lint は 4.9 からその代入を読む
3056
3072
  - どの行にも無い行フィールド —— ただし、行に近い名前がある(打ち間違いらしい。`name` の横の `.nmae`)なら警告します。行はデータで、任意のフィールド(ページが自分の行に付ける `pending` など)はほかの行にはありません。4.4 から。ほかの行にあるフィールドが最初の行に無いだけの場合も警告しません(4.3 から)
3057
3073
  - 途中の getter の戻り値のサブプロパティ
3058
3074
  - まだ読み込み中のボリュームのマウントパス配下のパス —— ボリュームが決着した時点で検査される
3059
3075
  - `$` 始まりの予約名前空間(`$command.*` など)
3076
+ - 要素が書き込むオブジェクトの下のキー —— router の `searchParams: query` は URL のキーを持つので、`query.next` は URL にあるときだけある。4.10 から。4.9 までは、そうした束縛を「silently dropped」と報告していた
3060
3077
 
3061
3078
  裏を返すと、**警告が出ない = 正しい保証にはなりません**。網羅した検査は `npx @wcstack/lint <file>` 側で行ってください。再設定(`setInitialState()`)では、すべてのパスを新しい state に対して検査し直します。
3062
3079
 
@@ -3297,6 +3314,10 @@ export default {
3297
3314
  `Intl` が受け付けないタグ(`<html lang="en_US">`)は、`Intl` の中で throw させる代わりに
3298
3315
  1 回報告し(`#48`)、フィルタは `"en"` を使う。`getConfig().locale` は設定された値を返し続ける。
3299
3316
 
3317
+ ロケールはタイムゾーンではない: `<html lang="ja-JP">` のページも、日付と時刻は読み手のタイムゾーンで
3318
+ 日本語に書く。ある場所の時刻は、日付フィルタの 3 つ目にその場所のタイムゾーンを渡して書く
3319
+ (4.10 から — [日付 / 時刻](#日付--時刻))。
3320
+
3300
3321
  **ロケールを後から変えても何も再描画されない。** これは state ではなく
3301
3322
  グローバル設定なので、依存グラフに載らない。フィルタ自体はバインド構築時に
3302
3323
  取り込むのではなく**適用のたびに読む**ので、別の理由で再描画されたバインドは新しい値を
package/README.md CHANGED
@@ -194,9 +194,9 @@ leaves features out, it can compose them instead:
194
194
  <script type="importmap">
195
195
  {
196
196
  "imports": {
197
- "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/core.js",
198
- "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/features/temporal.js",
199
- "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/features/scopes.js"
197
+ "@wcstack/state/core": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/core.js",
198
+ "@wcstack/state/features/temporal": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/features/temporal.js",
199
+ "@wcstack/state/features/scopes": "https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/features/scopes.js"
200
200
  }
201
201
  }
202
202
  </script>
@@ -263,7 +263,7 @@ receptacle the owning add-on fills on install).
263
263
  core, and the add-ons the page names from the same build.
264
264
 
265
265
  ```html
266
- <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.9.0/dist/split/auto.js"></script>
266
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@wcstack/state@4.10.0/dist/split/auto.js"></script>
267
267
  <wcs-state features="scopes diagnostics">
268
268
  <script type="module">
269
269
  export default {
@@ -1772,13 +1772,27 @@ The names 3.x kept as aliases of the 3.2 renames (`inc`, `dec`, `fix`, `uc`, `lc
1772
1772
 
1773
1773
  | Filter | Description | Example |
1774
1774
  |---|---|---|
1775
- | `date(loc?, style?)` | Date format | `createdAt\|date` / `createdAt\|date(ja-JP)` / `createdAt\|date(en-US,medium)` → `"Oct 13, 2026"` |
1776
- | `time(loc?, style?)` | Time format | `createdAt\|time` / `createdAt\|time(short)` |
1777
- | `datetime(loc?, style?)` | Date + Time | `createdAt\|datetime(en-US)` / `createdAt\|datetime(ja-JP,long)` |
1778
- | `ymd(sep?)` | YYYY-MM-DD (default `-`) | `createdAt\|ymd` / `createdAt\|ymd(/)` |
1779
- | `hms(sep?)` | HH:MM:SS (default `:`) | `createdAt\|hms` / `createdAt\|hms(-)` |
1775
+ | `date(loc?, style?, zone?)` | Date format; `zone?` since 4.10 | `createdAt\|date` / `createdAt\|date(ja-JP)` / `createdAt\|date(en-US,medium)` → `"Oct 13, 2026"` / `opensAt\|date(ja-JP,full,Asia/Tokyo)` |
1776
+ | `time(loc?, style?, zone?)` | Time format; `zone?` since 4.10 | `createdAt\|time` / `createdAt\|time(short)` / `opensAt\|time(,short,Asia/Tokyo)` |
1777
+ | `datetime(loc?, style?, zone?)` | Date + Time; `zone?` since 4.10 | `createdAt\|datetime(en-US)` / `createdAt\|datetime(ja-JP,long)` / `createdAt\|datetime(,,UTC)` |
1778
+ | `ymd(sep?)` | YYYY-MM-DD (default `-`), in the runtime's time zone | `createdAt\|ymd` / `createdAt\|ymd(/)` |
1779
+ | `hms(sep?)` | HH:MM:SS (default `:`), in the runtime's time zone | `createdAt\|hms` / `createdAt\|hms(-)` |
1780
1780
 
1781
- **All five take a `Date`,** not a timestamp — a value of any other type is rejected with "requires a date value". An absent one (`undefined` / `null`) renders nothing since 4.7. State loaded from JSON carries a number or an ISO string, so convert it where it is read rather than in the binding: `get createdAt() { return new Date(this.createdAtMs); }`. `loc?` defaults to the page locale ([Locale](#locale)), and the default is re-read on **every** apply, so a locale settled after the bindings were planned still takes effect (an explicit `date(ja-JP)` is part of the binding expression and is fixed when it is planned). `style?` is `full`, `long`, `medium` or `short` (Intl's `dateStyle` / `timeStyle`); without it the format is the locale's numeric one (`10/13/2026`), and a style alone keeps the page locale (`date(medium)`). `currency`'s code is checked when the bindings are planned; the number is formatted in the page locale unless `loc` names one. Plurals and other formats belong in a getter (`Intl.PluralRules`, `Intl.NumberFormat`).
1781
+ **All five take a `Date`,** not a timestamp — a value of any other type is rejected with "requires a date value". An absent one (`undefined` / `null`) renders nothing since 4.7. State loaded from JSON carries a number or an ISO string, so convert it where it is read rather than in the binding: `get createdAt() { return new Date(this.createdAtMs); }`. A date-only string is a day, not a moment: `new Date("2026-10-29")` is midnight UTC, which is the day before west of Greenwich — `new Date(y, m - 1, d)` is that day where the page runs (`#29` says so since 4.9). `loc?` defaults to the page locale ([Locale](#locale)), and the default is re-read on **every** apply, so a locale settled after the bindings were planned still takes effect (an explicit `date(ja-JP)` is part of the binding expression and is fixed when it is planned). `style?` is `full`, `long`, `medium` or `short` (Intl's `dateStyle` / `timeStyle`); without it the format is the locale's numeric one (`10/13/2026`), and a style alone keeps the page locale (`date(medium)`). `currency`'s code is checked when the bindings are planned; the number is formatted in the page locale unless `loc` names one. Plurals and other formats belong in a getter (`Intl.PluralRules`, `Intl.NumberFormat`).
1782
+
1783
+ **Time zones.** Without `zone?`, `date`, `time` and `datetime` format in the time zone of the runtime — **the reader's**, in a browser, and the server's under SSR — whatever the page's locale. A time that belongs to a place reads differently elsewhere: a library in Tokyo open on Thursday 29 October from 09:00 to 20:00, read from Los Angeles, shows `2026年10月28日水曜日`, `17:00:00` and `4:00:00`. Since 4.10, `zone?` names the zone to format in — an IANA time zone, Intl's `timeZone` (`Asia/Tokyo`, `America/New_York`, `UTC`) — so every reader sees the place's time:
1784
+
1785
+ ```html
1786
+ <html lang="ja">
1787
+ <!-- the state: get opensAt() { return new Date("2026-10-29T09:00:00+09:00"); }, closesAt the same at 20:00 -->
1788
+ <p>
1789
+ <span data-wcs="textContent: opensAt|date(,full,Asia/Tokyo)"></span>
1790
+ <span data-wcs="textContent: opensAt|time(,short,Asia/Tokyo)"></span>–<span data-wcs="textContent: closesAt|time(,short,Asia/Tokyo)"></span>
1791
+ </p>
1792
+ <!-- 2026年10月29日木曜日 9:00–20:00, from Tokyo and from Los Angeles alike -->
1793
+ ```
1794
+
1795
+ An empty argument keeps its default: `time(,short,Asia/Tokyo)` keeps the page locale, `date(ja-JP,,Asia/Tokyo)` the numeric format, `datetime(,,Asia/Tokyo)` both. A zone alone keeps both too when it has a `/` or is `UTC` (`date(Asia/Tokyo)`, `datetime(UTC)`); another zone without a `/` goes third (`date(,,GMT)`), since in the first place it is read as a locale. A zone Intl does not take is refused when the bindings are planned (`#54`), and a zone in the style's place (`date(ja-JP,Asia/Tokyo)`) is told that it goes third (`#52`). `ymd` and `hms` take no zone: they read the `Date` in the runtime's zone (`getFullYear()` … `getSeconds()`), so for a place's date or time use `date` / `time` with its zone, or a getter.
1782
1796
 
1783
1797
  ### Boolean / Default
1784
1798
 
@@ -2622,7 +2636,7 @@ Firing order is defined in three layers, and only the middle one is yours to ste
2622
2636
  | Between handlers | declaration order in `$watch` | **reorder the declarations** |
2623
2637
  | Between rows of one path | ascending `indexes` | fixed |
2624
2638
 
2625
- **The one thing that moves the mechanism layer** is a `<wcs-view-transition>` that accepts the `state` participant. Binding application — and with it `$renderedCallback` — then lands on a frame, while `$watch` and the `$stream` restart stay on the microtask the drain was queued on, because they consume state addresses and not the DOM. For as long as the tag is present the order is `$watch` → `$stream` restart → `$renderedCallback`. Nothing else on the page reorders this layer; see [docs/timing-and-firing-contract.md](https://github.com/wcstack/wcstack/blob/main/docs/timing-and-firing-contract.md) §4.3.
2639
+ **The one thing that moves the mechanism layer** is a `<wcs-view-transition>` that accepts the `state` participant. Binding application — and with it `$renderedCallback` — then lands on a frame. Since 4.10 `$watch` and the `$stream` restart wait for it, so the order stays as above, a frame later, and what they write (a list a `$watch` copies into state) is applied in the same transition; up to 4.9 they stayed on the microtask the drain was queued on and ran first (`$watch` → `$stream` restart → `$renderedCallback`), so a `$watch` that navigated after a login ran before `<wcs-storage>` had saved the session its route guard reads. Nothing else on the page moves this layer; see [docs/timing-and-firing-contract.md](https://github.com/wcstack/wcstack/blob/main/docs/timing-and-firing-contract.md) §4.3.
2626
2640
 
2627
2641
  Key rules:
2628
2642
 
@@ -2989,8 +3003,9 @@ The first line is what `@wcstack/state` and `/auto` print, the second what `/cor
2989
3003
  | 49 | | `<wcs-state src="…"> failed to initialize.` |
2990
3004
  | 50 | | `<wcs-state src="…"> $connectedCallback failed.` |
2991
3005
  | 51 | | `<wcs-state src="…"> $disconnectedCallback failed.` |
2992
- | 52 | | `filter <name> takes a style of full, long, medium or short, not "<style>"` |
3006
+ | 52 | | `filter <name> takes a style of full, long, medium or short, not "<style>"` (since 4.10, a time zone in the style's place (one with a `/`, or `UTC`) adds: `— a time zone is the third argument, after the locale and the style, and an empty argument keeps its default: <name>(,,<zone>), <name>(en-US,long,<zone>)`) |
2993
3007
  | 53 | | `filter currency takes an ISO 4217 currency code (JPY, USD, EUR), not "<code>"` |
3008
+ | 54 | | `filter <name> takes an IANA time zone (Asia/Tokyo, America/New_York, UTC), not "<zone>"` (since 4.10) |
2994
3009
  | 101 | `binding-syntax` | `Invalid bindText: "<text>". Missing ':' separator between propPart and statePart.` |
2995
3010
  | 102 | `binding-syntax` | `"<text>": "<keyword>" takes no modifiers or filters on its left side — write "<keyword>:".` |
2996
3011
  | 103 | `binding-syntax` | `"<text>": "else" takes no value — write "else:".` |
@@ -3065,11 +3080,13 @@ dropped. Validate statically: npx @wcstack/lint <file>.
3065
3080
  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:
3066
3081
 
3067
3082
  - A `null` / `undefined` parent (the "seed as `null`, assign later" shape)
3083
+ - An empty object under a key (`fieldErrors: {}`, filled later with `this.fieldErrors = { date: "…" }`) — the same shape, seeded `{}`. Since 4.10; up to 4.9 each binding below it was reported, and its "updates … will be silently dropped" was not so: replacing the object updates them. The lint reads those assignments since 4.9
3068
3084
  - Row fields of a list that starts empty (the row shape is unknown)
3069
3085
  - 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)
3070
3086
  - Sub-properties of an intermediate getter's return value
3071
3087
  - Paths under the mount path of a volume that is still loading — they are checked once it settles
3072
3088
  - Reserved `$` namespaces (`$command.*` and friends)
3089
+ - Keys under an object an element writes — the router's `searchParams: query` has the URL's keys, so `query.next` is there only when the URL has it. Since 4.10; up to 4.9 such a binding was reported as "silently dropped"
3073
3090
 
3074
3091
  So **no warning is not a proof of correctness.** For exhaustive checking, run `npx @wcstack/lint <file>`. A re-set (`setInitialState()`) checks every path again against the new state.
3075
3092
 
@@ -3311,6 +3328,10 @@ the module is evaluated, so write it in the markup. An explicit
3311
3328
  is reported once (`#48`) and the filters use `"en"`, rather than throwing inside `Intl`;
3312
3329
  `getConfig().locale` keeps returning what was set.
3313
3330
 
3331
+ The locale is not the time zone: a `<html lang="ja-JP">` page still writes its dates and times in
3332
+ the reader's zone, in Japanese. A time that belongs to a place takes that place's zone as the date
3333
+ filters' third argument (since 4.10 — [Date / Time](#date--time)).
3334
+
3314
3335
  **Changing the locale later does not re-render anything.** It is a global
3315
3336
  setting, not state, so it is not part of the dependency graph. The filters do
3316
3337
  read it on every application rather than capturing it when the binding is built,