@wcstack/state 1.17.0 → 1.19.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 +57 -0
- package/README.md +57 -0
- package/dist/index.esm.js +1557 -243
- 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 +1 -1
package/README.ja.md
CHANGED
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
- **Web Component バインディング** — Shadow DOM コンポーネントとの双方向状態バインディング
|
|
99
99
|
- **command token** — pub/sub チャネル(`command.<method>: tokenName`)で state から wc-bindable カスタム要素のメソッドを起動
|
|
100
100
|
- **event token** — command token の双対。wc-bindable 要素が dispatch するイベントを `eventToken.<prop>: tokenName` + `$on` マップで state が受信
|
|
101
|
+
- **stream** — `$streams` 宣言で連続的な非同期フロー(async iterable / `ReadableStream`)を fold して reactive プロパティ化。switchMap 型の依存駆動 restart 付き
|
|
101
102
|
- **パス getter** — ドットパスキー getter(`get "users.*.fullName"()`)によるデータツリーの任意の深さへのフラットな仮想プロパティ定義、自動依存追跡・キャッシュ
|
|
102
103
|
- **Mustache 構文** — テキストノードでの `{{ path|filter }}`
|
|
103
104
|
- **複数の状態ソース** — JSON, JS モジュール, インラインスクリプト, API, 属性
|
|
@@ -1393,6 +1394,62 @@ $on: {
|
|
|
1393
1394
|
|
|
1394
1395
|
event token は command token と同じ `Token` pub/sub プリミティブを共有します —— `name` / `size` / `subscribe` / `unsubscribe` / `emit`、subscribe 順の保持つき([Token API](#token-api) 参照)。token はイベントごとに registry から解決されるため、`setInitialState()` による再構築後も最新の `$on` 購読者に届きます。所有する `<wcs-state>` が disconnect されると、event-token registry はクリアされます。
|
|
1395
1396
|
|
|
1397
|
+
## Stream(`$streams`)
|
|
1398
|
+
|
|
1399
|
+
command token / event token が運ぶのは離散的なやり取りです。**`$streams`** は残る形 —— 連続的なフローをカバーします。非同期 producer(async iterable / async generator / `ReadableStream`)を宣言すると、フレームワークがそれを **fold して単一の reactive プロパティに畳み込みます** —— 各チャンクは通常のパス代入を通るため、バインディング・パス getter・`$updatedCallback` は自分で値を代入した場合とまったく同じように反応します。`args` 関数が読んだ state パスが変化すると、実行中の producer は abort され、新しい引数で source が張り直されます(switchMap 型の依存駆動 restart)。stream は `$connectedCallback` 完了後に eager に起動し、要素の disconnect で abort されます。
|
|
1400
|
+
|
|
1401
|
+
```html
|
|
1402
|
+
<wcs-state>
|
|
1403
|
+
<script type="module">
|
|
1404
|
+
export default {
|
|
1405
|
+
prompt: "",
|
|
1406
|
+
|
|
1407
|
+
$streams: {
|
|
1408
|
+
// フル形: LLM トークンストリームを累積
|
|
1409
|
+
tokens: {
|
|
1410
|
+
args: (state) => state.prompt, // 依存はここでのみ捕捉される
|
|
1411
|
+
source: (prompt, signal) => llmStream(prompt, signal),
|
|
1412
|
+
fold: (acc, chunk) => acc + chunk, // reduce(累積)
|
|
1413
|
+
initial: "", // fold 指定時は必須
|
|
1414
|
+
},
|
|
1415
|
+
|
|
1416
|
+
// 最小形: fold 省略 = latest(最新チャンクで置換)、args 省略 = 一度だけ起動
|
|
1417
|
+
ticker: {
|
|
1418
|
+
source: (_args, signal) => priceStream(signal),
|
|
1419
|
+
},
|
|
1420
|
+
},
|
|
1421
|
+
};
|
|
1422
|
+
</script>
|
|
1423
|
+
</wcs-state>
|
|
1424
|
+
```
|
|
1425
|
+
|
|
1426
|
+
| フィールド | 必須 | 契約 |
|
|
1427
|
+
|---|---|---|
|
|
1428
|
+
| `source` | ✔ | `(args, signal) => AsyncIterable \| ReadableStream \| Promise<同>`。**`AbortSignal` を必ず尊重すること** —— restart / 破棄はこの signal で駆動される |
|
|
1429
|
+
| `args` | — | readonly な state proxy を受ける同期・純粋関数。ここで読んだパスが依存として捕捉される。省略時は一度だけ起動し restart しない |
|
|
1430
|
+
| `fold` | — | 同期関数 `(acc, chunk) => next`。省略時は latest(チャンクで置換)。毎回新しい値を返すこと —— `acc` の in-place 変異は非サポート |
|
|
1431
|
+
| `initial` | `fold` 指定時 ✔ | 初期値。起動・restart のたびにプロパティはこの値にリセットされる |
|
|
1432
|
+
|
|
1433
|
+
stream の値は普通のプロパティで、コンパニオンの status / error は読み取り専用の名前空間から参照できます:
|
|
1434
|
+
|
|
1435
|
+
```html
|
|
1436
|
+
<p data-wcs="textContent: tokens"></p>
|
|
1437
|
+
<p data-wcs="textContent: $streamStatus.tokens"></p> <!-- "idle" | "active" | "done" | "error" -->
|
|
1438
|
+
<p data-wcs="textContent: $streamError.tokens"></p> <!-- 直近のエラー。(re)start で null -->
|
|
1439
|
+
```
|
|
1440
|
+
|
|
1441
|
+
error 時、プロパティは直前の fold 結果を保持し、エラーは `$streamError.<name>` に入ります。`done` / `error` の stream も依存の変化で restart します(再試行 = 依存の叩き直し)。
|
|
1442
|
+
|
|
1443
|
+
重要な規範:
|
|
1444
|
+
|
|
1445
|
+
- **協調キャンセル(MUST)** —— `source` は渡された `AbortSignal` を必ず監視し、発火したら生産を停止すること。
|
|
1446
|
+
- **有界 fold** —— 需要は producer に逆流しません(backpressure は明示的に放棄)。無限 / 長寿命ストリームでは latest・count・last-N(`(acc, chunk) => [...acc.slice(-99), chunk]`)・ウィンドウ集計など有界な fold を使うこと。生の全チャンク累積は有限ストリーム限定。
|
|
1447
|
+
- **`args` は同期** —— Promise を返すとエラー。`args` 内での wildcard 読みも拒否されます。
|
|
1448
|
+
- **自己依存・相互サイクルの禁止** —— `args` が自 stream の値や status を読むとエラーになります。2 つの stream の相互サイクル(A の `args` が B の値を読み、B の `args` が A の値を読む)は検出されず無限 restart になるため組まないこと。一方向のチェイン(A の値を B の `args` が読む)は正当です。
|
|
1449
|
+
- **SSR では起動しない** —— サーバーでは宣言のパースとプロパティの実体化(`initial`)のみ行い、source は実行されません。クライアント側は通常どおり起動します。
|
|
1450
|
+
|
|
1451
|
+
完全な契約 —— ライフサイクルと所有権・restart セマンティクス・flush 粒度・スコープ外リスト —— は [docs/streams.ja.md](docs/streams.ja.md) を参照してください。
|
|
1452
|
+
|
|
1396
1453
|
## Inputs と属性ミラー
|
|
1397
1454
|
|
|
1398
1455
|
`wcBindable.inputs` は一方向のプロパティ入力(state → 要素)を宣言します。エントリに `attribute` を設定すると、フレームワークはプロパティを書き込むたびにその値を当該 HTML 属性へも書き込むため、`attributeChangedCallback`・CSS の属性セレクタ・DevTools がすべてプロパティ値と同期し続けます。
|
package/README.md
CHANGED
|
@@ -98,6 +98,7 @@ That's it. No build, no bootstrap code, no framework.
|
|
|
98
98
|
- **Web Component binding** — bidirectional state binding with Shadow DOM components
|
|
99
99
|
- **Command tokens** — invoke methods on wc-bindable custom elements from state via a pub/sub channel (`command.<method>: tokenName`)
|
|
100
100
|
- **Event tokens** — the dual of command tokens: receive a wc-bindable element's dispatched events in state via `eventToken.<prop>: tokenName` + the `$on` map
|
|
101
|
+
- **Streams** — fold continuous async flows (async iterables / `ReadableStream`) into reactive properties via the `$streams` declaration, with switchMap-style dependency-driven restart
|
|
101
102
|
- **Path getters** — dot-path key getters (`get "users.*.fullName"()`) for virtual properties at any depth in a data tree, all defined flat in one place with automatic dependency tracking and caching
|
|
102
103
|
- **Mustache syntax** — `{{ path|filter }}` in text nodes
|
|
103
104
|
- **Multiple state sources** — JSON, JS module, inline script, API, attribute
|
|
@@ -1394,6 +1395,62 @@ $on: {
|
|
|
1394
1395
|
|
|
1395
1396
|
Event tokens share the same `Token` pub/sub primitive as command tokens — `name` / `size` / `subscribe` / `unsubscribe` / `emit`, with subscribe-order preservation (see [Token API](#token-api)). The token is resolved from the registry on every event so a re-`setInitialState()` rebuild still reaches the latest `$on` subscribers. When the owning `<wcs-state>` is disconnected, the event-token registry is cleared.
|
|
1396
1397
|
|
|
1398
|
+
## Streams (`$streams`)
|
|
1399
|
+
|
|
1400
|
+
Command tokens and event tokens carry discrete interactions. **`$streams`** covers the remaining shape: a continuous flow. Declare an async producer (async iterable / async generator / `ReadableStream`) and the framework **folds it into a single reactive property** — each chunk goes through normal path assignment, so bindings, path getters, and `$updatedCallback` react exactly as if you had assigned the value yourself. When a state path read by the `args` function changes, the running producer is aborted and the source is restarted with the new arguments (switchMap-style dependency-driven restart). Streams start eagerly after `$connectedCallback` completes and are aborted when the element disconnects.
|
|
1401
|
+
|
|
1402
|
+
```html
|
|
1403
|
+
<wcs-state>
|
|
1404
|
+
<script type="module">
|
|
1405
|
+
export default {
|
|
1406
|
+
prompt: "",
|
|
1407
|
+
|
|
1408
|
+
$streams: {
|
|
1409
|
+
// Full form: accumulate an LLM token stream
|
|
1410
|
+
tokens: {
|
|
1411
|
+
args: (state) => state.prompt, // dependencies are captured here, and only here
|
|
1412
|
+
source: (prompt, signal) => llmStream(prompt, signal),
|
|
1413
|
+
fold: (acc, chunk) => acc + chunk, // reduce (accumulate)
|
|
1414
|
+
initial: "", // required when fold is specified
|
|
1415
|
+
},
|
|
1416
|
+
|
|
1417
|
+
// Minimal form: no fold = latest (replace with the newest chunk), no args = start once
|
|
1418
|
+
ticker: {
|
|
1419
|
+
source: (_args, signal) => priceStream(signal),
|
|
1420
|
+
},
|
|
1421
|
+
},
|
|
1422
|
+
};
|
|
1423
|
+
</script>
|
|
1424
|
+
</wcs-state>
|
|
1425
|
+
```
|
|
1426
|
+
|
|
1427
|
+
| Field | Required | Contract |
|
|
1428
|
+
|---|---|---|
|
|
1429
|
+
| `source` | ✔ | `(args, signal) => AsyncIterable \| ReadableStream \| Promise<same>`. **Must honor the `AbortSignal`** — restart and disposal are driven by it |
|
|
1430
|
+
| `args` | — | Synchronous pure function over a readonly state proxy. Paths read here are captured as dependencies; omitted = start once, never restart |
|
|
1431
|
+
| `fold` | — | Synchronous `(acc, chunk) => next`. Omitted = latest (replace with the chunk). Must return a new value — no in-place mutation of `acc` |
|
|
1432
|
+
| `initial` | with `fold` ✔ | Seed value. The property resets to it on every (re)start |
|
|
1433
|
+
|
|
1434
|
+
The stream's value is an ordinary property, and its companion status / error live under read-only namespaces:
|
|
1435
|
+
|
|
1436
|
+
```html
|
|
1437
|
+
<p data-wcs="textContent: tokens"></p>
|
|
1438
|
+
<p data-wcs="textContent: $streamStatus.tokens"></p> <!-- "idle" | "active" | "done" | "error" -->
|
|
1439
|
+
<p data-wcs="textContent: $streamError.tokens"></p> <!-- last error, null after (re)start -->
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
On error the property keeps its last folded value and the error lands in `$streamError.<name>`; a `done` or `error` stream restarts when its dependencies change (retrying = re-hitting the dependency).
|
|
1443
|
+
|
|
1444
|
+
Key rules:
|
|
1445
|
+
|
|
1446
|
+
- **Cooperative cancellation (MUST)** — `source` must observe the passed `AbortSignal` and stop producing when it fires.
|
|
1447
|
+
- **Bounded fold** — demand never flows back to the producer (backpressure is deliberately abandoned). For infinite / long-lived streams use a bounded fold — latest, count, last-N (`(acc, chunk) => [...acc.slice(-99), chunk]`), windowed aggregates. Raw accumulation of every chunk is for finite streams only.
|
|
1448
|
+
- **`args` is synchronous** — returning a Promise is an error, and wildcard reads inside `args` are rejected.
|
|
1449
|
+
- **No self-dependency, no mutual cycles** — `args` reading the stream's own value or status raises an error. Mutual cycles between two streams (A's `args` reads B's value and vice versa) are not detected and restart forever — do not build them. One-way chains (A's value feeding B's `args`) are legitimate.
|
|
1450
|
+
- **SSR does not start streams** — on the server the declaration is parsed and the property is materialized with `initial`, but no source runs; the client starts streams as usual.
|
|
1451
|
+
|
|
1452
|
+
See [docs/streams.md](docs/streams.md) for the full contract — lifecycle and ownership, restart semantics, flush granularity, and the out-of-scope list.
|
|
1453
|
+
|
|
1397
1454
|
## Inputs and Attribute Mirror
|
|
1398
1455
|
|
|
1399
1456
|
`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.
|