@aiquants/daily-report 0.31.0 → 0.33.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/CHANGELOG.md +121 -0
- package/README.md +154 -83
- package/dist/client.d.mts +38 -18
- package/dist/client.d.ts +38 -18
- package/dist/client.js +4 -4
- package/dist/client.mjs +4 -4
- package/dist/ids-stream-CtlszCHr.d.ts +32 -0
- package/dist/ids-stream-CyK2upgD.d.mts +32 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/server.d.mts +63 -34
- package/dist/server.d.ts +63 -34
- package/dist/server.js +4 -4
- package/dist/server.mjs +4 -4
- package/dist/{ids-stream-BR5RSj5u.d.ts → sse-schema-DcOgQr_O.d.mts} +685 -643
- package/dist/{ids-stream-DvB2h1dj.d.mts → sse-schema-DcOgQr_O.d.ts} +685 -643
- package/dist/styles/daily-report.standalone.css +1 -1
- package/package.json +5 -4
- package/dist/types-DkERw8NE.d.mts +0 -74
- package/dist/types-DkERw8NE.d.ts +0 -74
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,127 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@aiquants/daily-report` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.33.0 (2026-10-07)
|
|
6
|
+
|
|
7
|
+
0.32.0 の続き: 一覧の末尾の近くの削除で錨の日報が跳ばないこと、action の照合用 ID を 2 つの正準の形だけにすること、SSE のエントリをプロセスで 1 度だけ読むことと 1 回の読み取りをバイトの予算で縛ること、action の閲覧者ごとのレートの上限、ids ストリームのアイテムの publish をトランジションの優先度で届けることと、見出しの件数とビューの本体を配置の境界にすること、action と JSON のエンドポイントの共有のワイヤの契約、後で呼ばれるタイマーを持たない期限の記録、原本の HEAD の大きさの契約。どれも README の該当の節が今の振る舞いを述べる。
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **一覧の末尾の近くで窓より前の日報が消えても、錨の日報は跳ばない**: 末尾の近くで窓より前の日報が消えて中身が縮むと、スクロールの領域は位置を小さくなった最大位置へ動かす (クランプ)。両ビューのスクロールの錨は、VirtualScroll がそのクランプを `onScrollAdjust` の `"clamp"` で知らせると (`@aiquants/virtualscroll` 3.13.0)、錨を記録したまま保ち、記録した位置だけをクランプの `delta` だけ動かすので、一覧の変化の後に錨の日報を同じ px へ戻す。
|
|
12
|
+
以前はクランプを利用者のスクロールと数えて動いた眺めを保ったので、DetailList は末尾から 42 px 上で 302 px、末尾ちょうどで 344 px 跳んだ (一覧の途中では跳ばない)。`"clamp"` を知らせない版の VirtualScroll の上では、クランプは知らされず以前と同じに振る舞う。
|
|
13
|
+
- **action の照合用 ID は 2 つの形だけ**: `clientTempId` は、パッケージのクライアントが作る小文字の UUID (`crypto.randomUUID()`) と、先頭に 0 を持たない 16 桁までの負の 10 進の整数 (`String(-1 * Date.now())`) だけを受け、ほかは 400 (`Invalid clientTempId`。無いか空なら従来どおり `clientTempId required`) で、どのサービスも呼ばない。SSE のどのメッセージの照合用 ID も同じ形で読み、形の外の ID を持つエントリは配らない。
|
|
14
|
+
以前は空でない文字列ならフォームの上限 (1 MiB) まで受けたので、約 1 MiB の ID を付けたスターの切り替えが 200 で答え、約 1 MiB の状態のイベントを永続の SSE のストリームへ書いて、接続中のすべての閲覧者と再接続の追いつきへ届けた。
|
|
15
|
+
- **原本の HEAD も大きさの上限の契約を確かめる**: 読み取りポートが HEAD に `attachments.maxBytes` を超える `size` を申告したら、同じ実体の GET と同じポートの契約違反の 500 (`reason=port_contract code=over_max_bytes`、error) で答え、`Content-Length` を付けない。以前は HEAD だけが申告をそのまま `Content-Length` にして 200 で答えた。
|
|
16
|
+
- **宣言の無い上限超えの本文の運ばれ方を文書が正しく述べる**: 宣言した長さが上限を超える本文には 413 が届くが、宣言の無い (チャンク転送の) 本文と長さを偽る本文は、数えた長さが上限を超えたところで要求の本文を取り消し、Node のアダプターでは接続が閉じる (413 は届かない。413 を届けるには、上限の無い大きさの残りを読み切ることになる)。振る舞いは変わらず、数えた拒否は従来どおり warn の行に残る。以前の README と docstring は、どちらにも 413 が届くと述べていた。
|
|
17
|
+
- **後で呼ばれるタイマーを予約しない**: 作成した日報の錠 (1 秒)、自分の操作のエコーの抑止 (30 秒) と、キャッシュをまだ持たない日報への SSE の知らせの待ち (30 秒) は、どれも単調な時計の期限で読み、タイマーを使わない。待ちの購読はアンマウントの確定で解く。以前は錠を外す 1 秒のタイマーとエコーごとの 30 秒のタイマーを張り、プロバイダーがアンマウントした後にも走りえた (待ちの期限のタイマーと購読は受動的な副作用の後始末で止めていた)。
|
|
18
|
+
- **クライアントは action の答えと JSON のエンドポイントの答えを厳格に読む**: action の答えは送った操作の答えの形 (共有の `parseActionResult`) でなければ失敗として巻き戻し、日報の ID を送られたままの 10 進の文字で読む (以前は手書きの型で数と読み、`String()` / `Number()` で食い違いを隠した)。2xx でない答えは状態を持つ誤りで拒む。`report` と `business-date` の答えは厳格なスキーマで読み、合わなければ `Error` で拒む (以前は型の主張と `?? []` で読み、文字列で拒んだ)。
|
|
19
|
+
- **CommonJS のバンドルに死んだホットモジュールの分岐と、ビルドの警告が無い**: ids ストリームのセッションのホットモジュールの後始末は `import.meta.hot?.dispose(destroySession)` の 1 行で、CommonJS の形式はビルドの時に `import.meta.hot` を `undefined` に置き換えて畳む。以前の CommonJS のバンドルは常に偽になる判定を持ち、ビルドのたびに `import.meta` の警告を 3 つ出した。
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **SSE のエントリはプロセスで 1 度だけ読む**: 共有リーダーはライブのエントリ 1 件を読んだときに 1 度だけ解釈し検証して、凍結した同じ値を全接続へ渡す。接続ごとの仕事は読み終えた値の絞り込みと、メッセージごとに 1 度だけ作るフレームの字面の選択だけで、書く字面は以前と 1 バイトも違わない。以前は接続ごとに解釈・検証・直列化し直したので、1 MiB のイベントで接続 1 つあたり約 1.6 ms、200 接続で約 311 ms をイベントループで使った。
|
|
24
|
+
- **SSE の 1 回の読み取りはバイトの予算で縛る**: 共有リーダーの `XREAD` と catch-up の 1 ページの件数は、64 MiB の予算を 1 件のイベントの上限で割った 10 件 (`DAILY_REPORT_SSE_CATCH_UP_BATCH`)。以前の 100 件と 1,000 件は件数だけの縛りで、最大のイベントばかりのページは約 5.9 GiB を溜めえた。小さなイベント 1,000 件の catch-up は 113 往復になる (往復の時間 0 で約 95 ms、1 ms で約 225 ms)。
|
|
25
|
+
- **ids ストリームのアイテムの publish はキーより後に描く**: アクションのプロバイダーは一覧を 1 つの購読で追い、版か落ち着きが変わったときだけ `startTransition` の中で描き直すので、行の導きとビューの行の数はトランジションの優先度で描かれ、ストリームの間に押したキーは publish の描画より先に確定する。ページの根・SSE の接続・進捗の札・行の集合の大きさは、読む項目だけを持つ見え方を読み、アイテムの publish では描き直さない (本番の React で、4 回の publish でページの根も SSE の接続の部品も 0 回)。公開の `useDailyReportIdsStream` は今までどおり状態の全体を返す。
|
|
26
|
+
- **見出しの件数とビューの本体は配置の境界**: 見せている間に変わる件数の文字 (総件数と走査中の件数) は、幅を見えない寸法の札が取る閉じ込めた箱に置くので、文字の変化は箱の中だけを配置し直す。走査中の件数は publish された値をそのまま見せる (補間しない。総件数は従来どおり補間し、動きを減らす設定に従う)。両ビューの根要素の中に、大きさ・配置・スタイルを閉じ込めたビューの本体の箱があり、行の数やスクロールバーの変化の配置はそこから始まる。List のモバイルのオーバーレイは本体の外に置く。以前は ids ストリームの間、40 回のキーの長押しごとに文書の根からの配置が 34〜51 回 (35〜54 ms) 起きた (ストリームの無いときは 0〜1 回)。
|
|
27
|
+
- **新しいコメントのフォームは隠しの欄を持たない**: 送信は投稿の処理だけが運ぶので、使われない `intent`・`reportHubId`・`businessDate` の隠しの欄とそのための props を除いた。本文の欄は欄自身のウィンドウの `HTMLInputElement` として読む。
|
|
28
|
+
- **開発者向け**: action と JSON のエンドポイントのワイヤはサーバーとクライアントが共有する定義から読む (`src/shared/action-wire.ts`: 操作の名前・欄の名前・指示とその符号化・操作ごとの答えとその厳格な解釈。`src/shared/api-endpoints.ts`: エンドポイントとクエリの引数の名前)。照合用 ID の形は `src/shared/client-temp-id.ts` の 1 つ。SSE のエントリの 1 度だけの読み取りとフレームの字面は `src/server/sse-entry.ts`、読み取りの予算は `src/server/payload-limits.ts` (`SSE_READ_BUDGET_BYTES`・`SSE_READ_ENTRY_COUNT`)。
|
|
29
|
+
レートの制限とバケットは `src/server/rate-limit.ts` へ移り (`attachment-delivery/rate-limiter.ts` は無い)、添付の経路と action が同じ 1 つの仕組みを使う。`pnpm run verify` は網羅率の段の後に、前の公開のタグから足した `src` の行がどれかの spec で走ることを確かめ (`scripts/check-changed-lines-coverage.mjs`。免除は `scripts/changed-lines-coverage-exemptions.json`)、ビルドの警告はどれもビルドを落とす (`scripts/lib/build-warnings.mjs`)。
|
|
30
|
+
クライアントの後で呼ばれるコールバック (タイマー・フレーム・リスナー・監視) を張る場所は、どれも理由付きで `src/client/deferred-callback-scope.spec.ts` に載る。action の `executeAction` は共有の指示を受け、`keepLock` の選択肢は無い。
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- server の `parseClientTempId` と、それだけが作る型 `DailyReportClientTempId` (照合用 ID の解釈。サービスを直接呼ぶホストが ID を読む。下の Breaking)。
|
|
35
|
+
- action の閲覧者ごとのレートの上限: 1 分に 600 件 (ハンドラー工場ごと、つまりワーカーのプロセスごと。認証が返す外部の利用者 ID で数える)。超えた要求は本文を読む前に 429 (`{"error":"Too many requests"}`、`Retry-After: 60`) で断り、どのサービスも呼ばない。行は閲覧者の窓ごとに最初の `429 action reason=rate_limit` と、窓の終わりの `rate_limit_suppressed count=<N> since=<…> route=action` の 2 行まで (warn。利用者 ID を書かない)。上限は、留まった側面ペインの自動既読 (ペイン 1 つで 1 分に 120 件) の 5 つ分。
|
|
36
|
+
|
|
37
|
+
### Breaking
|
|
38
|
+
|
|
39
|
+
- action の照合用 ID は 2 つの形だけ (上の Fixed)。server のサービスの書き込み (`setStarStatus`・`setReadStatus`・`addComment`・`deleteComment`・`createDailyReport`・`updateDailyReport`・`publishDailyReport`・`deleteDailyReport`) は照合用 ID を `DailyReportClientTempId` でだけ受ける (以前は `string`)。SSE のストリームの、形の外の照合用 ID を持つエントリは配らない。
|
|
40
|
+
Migration: パッケージのクライアント以外から action を呼ぶホストは、照合用 ID を小文字の UUID (`crypto.randomUUID()`) か、先頭に 0 を持たない 16 桁までの負の整数で送る。サービスを直接呼ぶホスト (結合試験など) は、ID を `parseClientTempId` で読んでから渡す (どちらの形でもなければ `null`)。
|
|
41
|
+
- server の `StreamEntry` (共有リーダーのポート `DailyReportSseReaderPort` が渡すエントリ) は `{ id, message }` で、`message` は 1 度だけ読んだメッセージ (`{ parsed, published, text }`: スキーマが検証したメッセージ・書かれたままの JSON の値・その字面) か、正しいメッセージを持たないエントリの `null` (以前は `message` がエントリの欄の `Record<string, string>`)。
|
|
42
|
+
Migration: 自分のリーダーを渡すホストは、エントリ 1 件の `data` の欄を JSON として解釈して `dailyReportSseMessageSchema` で検証し、読めなければ `message: null` にして、同じ値をすべての購読者へ渡す。
|
|
43
|
+
- action の `deleteComment` の 200 の答えは、ほかの操作と同じく `intent` (`"deleteComment"`) を持つ。
|
|
44
|
+
Migration: 答えを自分で読むホストは、答えの欄を共有の形 (`ActionResult`) で読む (知らない欄を落とす読み方なら変更は要らない)。
|
|
45
|
+
- action の閲覧者ごとのレートの上限 (上の Added): 1 人の閲覧者が 1 つのワーカーへ 1 分に 600 件を超えて送ると 429。
|
|
46
|
+
Migration: パッケージのクライアント以外から action を呼ぶホストは 429 を失敗として扱い、`Retry-After` の後に送り直す。
|
|
47
|
+
- ピアの `@aiquants/virtualscroll` の下限を 3.13.0 へ上げた (`peer-floors.json`)。一覧の末尾の近くで窓より前の日報が消えても錨の日報が跳ばない修正 (上の Fixed) は、3.13.0 がペインのクランプを `onScrollAdjust` の `"clamp"` で知らせることに頼る。
|
|
48
|
+
Migration: `@aiquants/virtualscroll` を 3.13.0 以降へ上げる。
|
|
49
|
+
|
|
50
|
+
## 0.32.0 (2026-10-07)
|
|
51
|
+
|
|
52
|
+
0.31.0 の続き: 添付の設定を 1 つの任意の入れ子のブロック (`attachments`) にまとめること、action の本文の上限と、送られた値 (件名・本文・コメント・真偽値) と `forceRefresh` を 1 つの厳格な解釈で読むこと、SSE のイベント 1 件の上限、ids ストリームのスナップショットトークンを取得ごとに 1 回だけ作ることと publish が一覧を写さないこと、アクションのプロバイダーの操作の値を描画をまたいで同じに保つこと、削除の印の付いた行の外し漏れ、件数の補間が動きを減らす設定に従うこと。どれも README の該当の節が今の契約を記す。
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- **action の本文は 1 MiB まで**: action はフォームの本文を `ACTION_FORM_MAX_BYTES` (1,048,576 バイト) までしか読まない。宣言した長さ (`Content-Length`) がそれを超える本文は読まずに、宣言の無い本文 (チャンク転送) と長さを偽る本文は読んだバイトを数えて上限を超えたところで読むのをやめて、413 (`{"error":"Form too large"}`) で答える。どのサービスも呼ばず、error の行も書かない。
|
|
57
|
+
記録は 60 秒の窓ごとに warn の 2 行まで (既定のロガーの接頭辞は `[DailyReportAction]`): 窓の最初の拒否の `413 action reason=form_too_large measured=<declared|counted> limit=1048576` と、窓の終わりに数えた拒否があれば `form_too_large_suppressed count=<N> since=<窓の始まりの ISO 8601> measured=declared:<n>,counted:<n>`。
|
|
58
|
+
SSE のストリームへ書くイベント 1 件の JSON は `SSE_EVENT_MAX_BYTES` (6,356,992 バイト: フォームの上限を JSON の最大の伸びの 6 倍に伸ばし、包みの 64 KiB を足した大きさ) までで、それより大きいイベントは書かずに warn の `[SSE] Event too large (<操作>): <バイト数> bytes > 6356992; not published` を残す。
|
|
59
|
+
受け付けた action の文字がこの上限を超えるイベントを作ることは無く、超えうるのは日報の詳細を丸ごと運ぶイベント (作成・更新・公開) のうち、積み上がった詳細 (本文とコメントの全件) が約 6 MiB を超えた日報だけで、キャッシュの無効化は書く前に済むので、閲覧者は次の読み込みで最新を読む。
|
|
60
|
+
以前は本文に上限が無く (前段のリバースプロキシの上限まで)、8 MiB のコメントも 200 で格納され、永続の SSE のストリームへ書かれて、接続中のすべての閲覧者と再接続の追いつきへ届いた。
|
|
61
|
+
- **action の送られた値も 1 つの厳格な解釈で読む**: フォームを読むのは `src/server/action-form.ts` だけで、操作の実行はフォームではなく解釈し終えた指示を受け取る。
|
|
62
|
+
- 更新 (`update`) は送られた欄だけを変える: 送られない欄は格納した値のまま、空文字はその欄を消して NULL で格納する。件名は注入した件名の列が宣言した長さ (パッケージのスキーマでは `nvarchar(200)`: UTF-16 の符号単位で 200) までで、超えれば 400 (`Invalid title`)。
|
|
63
|
+
- スターと既読の切り替えの `isStarred` / `isRead` は `true` か `false` だけで、ほかの値 (`TRUE`・`1`・`yes`・空文字) は 400 (`Invalid isStarred` / `Invalid isRead`)、無ければ 400 (`isStarred required` / `isRead required`)。コメントの追加の `content` は空でない文字 (無いか空なら `Content required`)。
|
|
64
|
+
- フォームのどこかにファイルの部分があれば 400 (`Invalid form`)。フォームは作りからテキストだけになる。
|
|
65
|
+
- API の `business-date`・`report`・`ids-stream` の `forceRefresh` は、無ければ偽で、`true` と `false` だけを受け、ほかの値は 400 (`{"error":{"message":"Invalid forceRefresh"}}`。`ids-stream` は HEAD の短絡より前に答える)。
|
|
66
|
+
- action のどの 400 も、内部ユーザーの解決と可視集合の解決より前に答える (封鎖された `clearCache` の 400 も)。
|
|
67
|
+
以前は件名・本文・コメントを `form.get()` のまま読んだので、送られない件名は NULL で格納され、ファイルの部分は文字列の型のままサービスへ届いた。`isStarred` / `isRead` と `forceRefresh` は `true` 以外のどの値も偽と読み (`TRUE`・`1`・`yes`・空文字がスターを外し、未読に戻した)、201 文字の件名はドライバーの切り詰めの失敗で 500 になり、`isStarred required` と `Content required` は利用者と可視集合の問い合わせの後に答えた。
|
|
68
|
+
- **削除の印の付いた行は、ストリームの正味の変化が値の変化でも見せない**: 行の導き (`deriveReportRows`) は、見せている行に削除の印があれば、ストリームの変化がその日報の値の変化 (同じ合体窓の削除と中継で正味が値の変化になったもの、印が付いた後に導いた値の変化) でも行を外す。
|
|
69
|
+
以前は印を見ると値の変化を当てはめずに戻ったので、一覧全体から導けば隠れる行が見えたまま残り、印の後の値の変化では前の値の行が残って、後から届いた削除がその行を見つけられず、行は二度と外れなかった。
|
|
70
|
+
- **件数の補間は動きを減らす設定に従う**: ヘッダーの総件数と ids ストリームの走査中の件数は、件数を描く要素のウィンドウが `prefers-reduced-motion: reduce` のとき、値が変わったその描画で目標の値を描き、アニメーションのフレームを 1 つも求めない (`useAnimatedNumber`)。以前は設定を読まずに 250 ms かけて補間した (README の「動きを減らす設定では、スクリプトの動きも含めて何も動かない」に反していた)。
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- **アクションのプロバイダーの操作は 1 つの同じ値**: `DailyReportActionProvider` は操作 (関数・楽観的な状態の台帳・表示中ユーザー) を 1 つの値として 1 回だけ作り、その同一性は表示中ユーザーが替わるまで変わらない。関数はどれも最後に確定した描画の実装を呼び (実装はレイアウトの副作用で入れ替え、描画中に ref を書かない)、状態 (行・台帳の版・SSE の有効と接続の状態) は別のコンテキストで配る。
|
|
75
|
+
パッケージの行・List のカード・DetailList の行と本文カード・側面ペインの中身・編集のフォームは操作だけを読むので、ストリームの publish・SSE の知らせ・接続の状態の変化で描き直さない。台帳の版の変化で描き直すのは日報のコメントの部品 (`ReportComments`) だけ。
|
|
76
|
+
公開のフック `useDailyReportActionContext` は今までどおり操作と状態を 1 つの値で返し、状態が変わらない描画では同じ値を返す (状態が変わるたびに呼び出し側は描き直す)。
|
|
77
|
+
以前はプロバイダーが描画のたびに新しいオブジェクトを配り、操作の関数もほとんどが描画ごとの閉包だったので、ストリームの publish のたびに、描かれているすべての行・カード・側面ペインの中身・編集のフォームが memo を越えて描き直された。
|
|
78
|
+
- **ids ストリームのスナップショットトークンは取得ごとに 1 回**: サービスは ID 一覧の全量を取得したとき (キャッシュのフェッチャーの中、整列の治癒の後) に 1 回だけトークンを作り、一覧と同じ包みに持たせる (`getDailyReportIdsByExternalId` の戻り値 `{ items, streamAnchor, snapshot }`。行の無い包みは空の一覧のトークン)。ids ストリームの配信はそれを読む。
|
|
79
|
+
以前は要求ごとに全件をたどって作り直したので、228,222 件では要求ごとにイベントループを p50 7 ms (静かなホスト)、負荷の下で 11〜26 ms 止めた。
|
|
80
|
+
- **ids ストリームの publish は一覧を写さない**: クライアントの publish が配るスナップショットは一覧そのものを持たず、publish の仕事は変化の大きさに比例する。一覧全体が要る購読者は読むときに作る (`DailyReportIdsStreamClient.readItems()`: 最後の publish の時点の一覧で、合体窓の中のまだ publish していない変化を戻す)。
|
|
81
|
+
プロバイダーは一覧全体から導き直すときだけ読み、ページは中身を見せるかを `loadedCount > 0` で決める。以前は publish ごとに一覧全体を写し (`Array.from`)、228,222 件では publish ごとに p50 1.29 ms、最初の訪問の 59 回の publish で約 56 ms を、publish の描画と同じタスクで使った。
|
|
82
|
+
- **開発者向け**: 添付の設定の解決は `src/server/attachment-delivery/settings.ts` の `resolveAttachmentDelivery` 1 つ (ブロックの検証と既定値の解決) と、平たいキーの拒否 `rejectFlatAttachmentKeys` (`FLAT_ATTACHMENT_KEYS` の表) にある。添付の経路は `service.attachmentDelivery` の解決済みの値だけを読み、ブロックが無ければ配信の状態を作らず、サムネイルが無ければサムネイルのバケット・ゲート・キャッシュを作らない。
|
|
83
|
+
action のフォームの読み取りと解釈は `src/server/action-form.ts` (`readActionCommand`) にあり、ハンドラーの操作の実行 (`runActionCommand`) はフォームを受け取らない。2 つの上限は `src/server/payload-limits.ts` (`ACTION_FORM_MAX_BYTES`・`SSE_EVENT_MAX_BYTES`)。`src/server/wire-params.ts` の `formFieldText` は無く、`parseWireBoolean`・`parseForceRefreshParam`・`readFormTextFields` が加わった。件名と本文の空文字を NULL で格納する規則はサービスの `storedText` 1 か所。
|
|
84
|
+
プロバイダーの描画中の行の導きは `useDerivedReportRows` (`src/client/contexts/daily-report-rows-derivation.ts`) 1 つで、本物のクライアントの publish ごとに触る行の数を試験が数える。行の導きは、乱数の操作列の性質の試験 (8 つの種 × 2 通り × 400 の操作列) で一覧全体から導いた行と比べる。両ビューの描画の回数の試験は本物のプロバイダーを描く (アクションのフックを代役にしない)。
|
|
85
|
+
アニメーションのフレームを求めてよいのは理由を添えた許可の一覧のファイルだけ (`src/client/ui/motion-policy.spec.ts`: 件数の補間は動きを減らす設定を読むこと、キーの長押しのまとめと添付の格子の列幅の受け渡しは動きではない用途)。ビューの Tab の順を全部たどる重い jsdom の試験の時間の上限は 1 つの定数 `HEAVY_JSDOM_TEST_TIMEOUT_MS` (60 秒。`src/client/test-helpers/heavy-jsdom-timeout.ts`) で、公開の verify が負荷の高いホストで Vitest の既定の 5 秒を越えて落ちない。
|
|
86
|
+
配置の格子の画素密度の一覧 (`LAYOUT_LATTICE_DEVICE_PIXEL_RATIOS`) は試験用の部品 (`src/client/test-helpers/layout-lattice-ratios.ts`) へ移り、`dist/client.mjs` に入らない。使われていない `upsertInIdsStreamOrder` は無い。クライアントは ids ストリームの次のカーソルの営業日を、サーバーと同じ `isCanonicalBusinessDateKey` で確かめる (暦に無い日を受けない)。ページはホストの `onSessionExpired` を描画中ではなくレイアウトの副作用で写す。
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- server の型 `DailyReportAttachmentsConfig` と `DailyReportAttachmentThumbnailsConfig` (添付の設定のブロックとそのサムネイル。下の Breaking)。型だけの公開なので、バンドルは増えない。
|
|
91
|
+
- サービスの `titleMaxLength` (件名の上限: 注入した hub テーブルの件名の列が宣言した長さ) と、`getDailyReportIdsByExternalId` の戻り値の `snapshot` (上の Changed)。
|
|
92
|
+
- client の `DailyReportIdsStreamClient` の `readItems()` (上の Changed)。
|
|
93
|
+
|
|
94
|
+
### Breaking
|
|
95
|
+
|
|
96
|
+
- 添付の設定は 1 つの任意のブロック `attachments` (`DailyReportAttachmentsConfig`) にまとまった: サービスの設定 (と、それを受け継ぐ `createDailyReportServer` の設定) の `attachments` は、ID コーデック (`idCodec`) と読み取りポート (`read`) を必ず持ち、大きさの上限と原本の経路の調整値、任意の `thumbnails` (`DailyReportAttachmentThumbnailsConfig`: 描画ポート `renderer` と調整値) を持つ。ブロックを省けば添付機能の無い配備。
|
|
97
|
+
効かない組み合わせ (読み取りの無いコーデック、読み取りの無い描画、ポートの無い調整値) は型で書けず、型の外 (JS のホスト・読み込んだ設定) から来た誤りは、入れ子のパスを名乗る `TypeError` (欠けたポート: `[daily-report] attachments.read must be injected when attachments is given`) か `RangeError` (不正な値: `[daily-report] attachments.thumbnails.cacheBytes must be an integer >= 0; got -1`) で生成を止める。
|
|
98
|
+
ブロックの外の平たい 9 つのキーは受けない: どれか 1 つでも持つ設定は (値が `undefined` でも)、`createDailyReportService`・`createDailyReportHandlers`・`createDailyReportServer` の生成を、値を置くパスとキーを名乗る `TypeError` で止める (`[daily-report] attachments.concurrency must be given in place of the flat key attachmentConcurrency`)。ポートを渡したのに添付が一覧に出ない・調整値が効かない、といった黙った縮退にはならない。ハンドラーの設定 (`DailyReportHandlersConfig`) は添付のキーを持たない。
|
|
99
|
+
サービスの `attachmentDelivery` (`DailyReportAttachmentDelivery`) は、ブロックの既定値を解決して凍結した `{ idCodec, read, maxBytes, rateLimitPerMinute, concurrency, thumbnails }` (`thumbnails` はサムネイルの無い配備で `null`、ほかは `{ renderer, rateLimitPerMinute, concurrency, cacheBytes }`) で、ブロックの無い配備で `null` (以前は `{ idCodec, readAttachment, thumbnailRenderer, maxBytes }` で、注入しないポートが `undefined`)。
|
|
100
|
+
Migration: 平たいキーをブロックの中のパスへ移す。
|
|
101
|
+
|
|
102
|
+
| 平たいキー (0.31.0 まで) | 以前の持ち主 | ブロックの中のパス |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `attachmentIdCodec` | サービス | `attachments.idCodec` |
|
|
105
|
+
| `readAttachment` | サービス | `attachments.read` |
|
|
106
|
+
| `attachmentMaxBytes` | サービス | `attachments.maxBytes` |
|
|
107
|
+
| `attachmentRateLimitPerMinute` | ハンドラー (ファサードが転送) | `attachments.rateLimitPerMinute` |
|
|
108
|
+
| `attachmentConcurrency` | ハンドラー (ファサードが転送) | `attachments.concurrency` |
|
|
109
|
+
| `attachmentThumbnailRenderer` | サービス | `attachments.thumbnails.renderer` |
|
|
110
|
+
| `attachmentThumbnailRateLimitPerMinute` | ハンドラー (ファサードが転送) | `attachments.thumbnails.rateLimitPerMinute` |
|
|
111
|
+
| `attachmentThumbnailConcurrency` | ハンドラー (ファサードが転送) | `attachments.thumbnails.concurrency` |
|
|
112
|
+
| `attachmentThumbnailCacheBytes` | ハンドラー (ファサードが転送) | `attachments.thumbnails.cacheBytes` |
|
|
113
|
+
|
|
114
|
+
原本だけの配備は `attachments: { idCodec, read }` (上限と原本の調整値は任意)、サムネイルも出す配備はそれに `thumbnails: { renderer }` (調整値は任意) を足す。既定値と下限は変わらない。`createDailyReportHandlers` へ手書きのサービスの代役を渡すホストの試験は、代役の `attachmentDelivery` を新しい形か `null` にする。
|
|
115
|
+
- action の更新の契約: 送らなかった欄は格納した値のまま、空文字は欄を消す (NULL で格納。上の Fixed)。以前は送らなかった欄も NULL で格納した。パッケージのクライアントはいつも件名と本文の両方を送り、client の `updateReport(reportHubId, data, businessDate)` (`useDailyReportActionContext` の操作) の `data` は `{ title: string; content: string }` で両方が必須 (以前は `{ title?: string; content?: string }` で、空にした欄を送らなかった)。
|
|
116
|
+
Migration: 欄を消すときは空文字を送る (欄を省くと格納した値のまま)。`updateReport` には件名と本文の両方を、打たれたとおりに渡す。
|
|
117
|
+
- action と API の値の解釈が厳しくなった (上の Fixed): `isStarred` / `isRead` と `forceRefresh` は `true` と `false` だけ、件名は件名の列の長さまで、フォームのファイルの部分は 400、本文は 1 MiB まで (413)。
|
|
118
|
+
Migration: 真偽値は `true` / `false` で送る。`forceRefresh` は省くか `true` / `false` で送る。action のフォームにファイルを入れない。
|
|
119
|
+
- client の `useDailyReportIdsStream` の状態 (`DailyReportIdsStreamState`) は `items` を持たない (上の Changed)。
|
|
120
|
+
Migration: 件数は `loadedCount`、届いた行があるかは `loadedCount > 0` で読む。見せている行はプロバイダーの `items` (`useDailyReportActionContext().items`) で読み、自分で作った `DailyReportIdsStreamClient` の一覧は `readItems()` で読む。
|
|
121
|
+
- server のサービスは、注入した hub テーブルの件名の列が長さを宣言していなければ、生成時に `RangeError` (`[daily-report] tables.hub.title must be a character column with a declared length; got <値>`) を投げる。`createDailyReportHandlers` は、サービスの `titleMaxLength` が 1 以上の整数でなければ生成時に `RangeError` (`[daily-report] service.titleMaxLength must be an integer >= 1; got <値>`) を投げる。ids ストリームの配信はサービスの `getDailyReportIdsByExternalId` が返す `snapshot` を読む。
|
|
122
|
+
Migration: 件名の列は長さを宣言する (`defineDailyReportSchema` の列は宣言済み)。`createDailyReportHandlers` へ渡す手書きのサービスの代役は `titleMaxLength` を持ち、`getDailyReportIdsByExternalId` から `snapshot` を返す。
|
|
123
|
+
- ピアの `@aiquants/virtualscroll` の下限を 3.11.5 へ上げた (`peer-floors.json`)。両ビューが 3.11.5 の変更に頼る所は無いが、この版の試験はすべて 3.11.5 の上で通したので、公開する範囲 (`^3.11.5`) と文書の下限をそれに合わせた。
|
|
124
|
+
Migration: `@aiquants/virtualscroll` を 3.11.5 以降へ上げる。
|
|
125
|
+
|
|
5
126
|
## 0.31.0 (2026-10-06)
|
|
6
127
|
|
|
7
128
|
0.30.0 の続き: 編集中の入力が描画の窓の外や選択の移動を越えて残ること、要求の値 (営業日・ID・操作の時刻・フォーム) の厳格な解釈と作成した日報の営業日を UTC の 0 時で格納すること、原本の `Content-Length` を渡す本文から決めること、一覧の行を ids ストリームの変化だけで導くことと SSE の知らせの合体、合体窓を単調な時計で測ること、行の集合の大きさ (`aria-setsize`) と見出しの総件数を申告した総数で決めること、ホバーの浮き上がりの定義域を 1 から 3 までの 1/4 の倍数のすべてへ広げること、要求の隔離の結線を型で決めること。どれも README の該当の節が今の契約を記す。
|