@aiquants/daily-report 0.37.0 → 0.39.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 CHANGED
@@ -2,6 +2,104 @@
2
2
 
3
3
  All notable changes to `@aiquants/daily-report` are documented here.
4
4
 
5
+ ## 0.39.0 (2026-10-09)
6
+
7
+ ### Added
8
+
9
+ - **キーボードで移る読み上げも何に当たったかを言う**: 検索の答えを見せている間、行は印を描いた欄の見出しごとに 1 文を持つ (ビューの描く順)。打った語の印が 1 つでもある見出しは新しい整形関数 `searchFieldMatched(field)` (en `Author matched`、ja `作成者で一致`)、読みの印だけの見出しは `searchFieldMatchedByReading(field)` (en `Author matched by its romaji reading`、ja `作成者がローマ字の読みで一致`) で言い、`field` は欄の見出しの解決済みの文言 (見出しの札とメタデータの列の作成者は 1 つの `author`、面談者・添付・コメントの欄はそれぞれの節の見出し)。文は行の葉が集計へ登録したものから作る (`describeDrawnSearchMarks`) ので、ビューが描かない欄を言わない。
10
+ DetailList の行と側面ペインの一致の印の隠した文は、当たったキーワードの数の文・この文・表示外の文の順 (表示外の文は印を描いた欄が無いときだけ)。List のカードは見える印を持たず、画面が印の store を持つ間、主ボタンの説明 (`aria-describedby`) のプレビューの後に、カードの印の囲みの中の隠した説明 (`hidden`、`data-daily-report-search-match-description`) を足す。印の `aria-description` は閲覧の読み上げのために今までどおり持つ。固有キーは 108 から 110 (文字列のキー 89・整形関数 21。キーの表は全 121 キー)。
11
+ 以前は、矢印キーで行を移る読み上げに何に当たったかが届かなかった: List のカードの作成者の印は主ボタンの子で (ボタンの子は読み上げで平らになり、読みの印の説明は落ちる)、プレビューは説明として平らに読まれ、DetailList の行の説明は状態・添付・種別と当たったキーワードの数だけだった。
12
+ - **`DailyReportRows`** (client entry): 日報の行の形 (`Pick<ReadonlyArray<DailyReportItem>, "length" | "at"> & Iterable<DailyReportItem>`) の名前。`DailyReportActionState.items` と両ビューの `dailyReportItems` はこの名前で宣言し、0.37.0 で変わった `items` の型を、ホストは書き写さずに読み込める。
13
+
14
+ ### Changed
15
+
16
+ - **ids のストリームは行を列で運ぶ** (下の Breaking): チャンク行 (`DailyReportIdsStreamChunkLine`) は行のオブジェクトの配列 (`items`) を持たず、ストリーム順の ID (`ids`)・続いて等しい営業日の連 (`dates` と、連ごとの行の数 `counts`)・区分の辞書 (`sourceTypes`。null は区分を持たない行) と行ごとのその添え字 (`sources`) を運ぶ。エンドポイントの名前は `ids-columns` (`DAILY_REPORT_API_ENDPOINTS.idsStream`)。
17
+ 4,000 行の行は約 300 KB から約 37 KB になり (ステージングの 228,222 行の一覧の本文は 17,046,814 B から 2,113,845 B)、受け手の JSON の解釈は行ごとのオブジェクトを作らない。クライアントは連をたどり (`forEachIdsStreamRow`)、値の変わった行と新しい行だけにオブジェクトを作るので、値の同じ再検証は行を何も作らない。
18
+ Node での計測 (228,222 行): 行 1 つの解釈と組み立ては 1.49 ms から 0.45 ms、クライアントの全量の走査は 249.6 ms から 188.1 ms。性能の試験の押し続けでは、ストリームの読み (`streamOnce`) が 1 回の押し続けあたり 13.7〜20.0 ms から 7.4〜9.9 ms、解釈と行のつなぎの割り当てが 1.5〜1.9 MB から 0.26〜0.56 MB、80,000 行での GC が 17.4 / 24.0 ms から 1.9 / 2.2 ms になった。
19
+ - **`isIdsStreamChunkLine` はワイヤの行の唯一の厳格な読み手**: 受けるのは `kind` が `"chunk"` で、列の長さがそろい (ID の数 = 連の行の数の和 = 添え字の数、営業日の数 = 連の数)、どの連の数も 1 以上、どの添え字も辞書の範囲、辞書はどれも文字列か null、どの営業日も正準の `YYYY-MM-DD` か null、どの ID も正の安全な整数で、行がストリームの順に並び、`isLast` が真偽で `nextCursor` が null かカーソルの形の行だけ。クライアントは読めない行を警告して読み飛ばし、カーソルから再開する。
20
+ 行の分類 (`classifyIdsStreamLine`) は `kind` だけで決め、`kind` を持たない行は種類の分からない行 (警告して読み飛ばす)。以前は `kind` の無い行を `items` の配列か `error` の文字で分けた。
21
+ - **エンドポイントの表は JSON の答えのキャッシュの方針を持つ**: JSON の行は `cache` (`etag` か `no-store`) を持ち、経路はその方針の部品をハンドラーへ渡して、200・断り・経路の 405 と 500 のどれもその部品で組む。`no-store` は `search-marks` だけで、ほかの JSON のエンドポイントは `etag`。どの 429 も 1 つの断りの部品が組む。経路の跡の `debug` の行は、`etag` のエンドポイントで `<状態> endpoint=<名前> etag_match=<true|false>`、`search-marks` で `<状態> endpoint=<名前>`。
22
+ 以前は `search-marks` の 405 と 500 が ETag を持ち、跡の行は ETag の無い答えにも `etag_match` を書いた。
23
+ - **進捗の札の点は動かない**: ids のストリームの進捗の札の点は明滅しない (件数と割合が publish ごとに変わる)。終わらない動きは、合成器に入力の無いフレームを毎秒約 60 回描かせ続け、押し続けたキーのフレームはその描画の後ろで待つ (明滅のあった間は、ストリームの配信中に測ったどの条件でも、押し続けのキーの描画の前の待ちが p95 で 8.6〜16.2 ms、明滅が無ければ約 0.2 ms)。
24
+ 残る終わらない動きは、読み込みか再接続の間だけ出る 4 つ (再接続中と最新化中の札の回転・List の行と側面ペインの読み込み中のスケルトン・見えているサムネイルの枠の読み込み中の地) だけで、`src/client/ui/motion-policy.spec.ts` がソースと配布する CSS でその場所と数を理由と一緒に許す。
25
+ - **側面ペインは選択を選択の置き場から読む**: List のビューの選択と、長押しの間に側面ペインの中身が追う落ち着いた選択は、ビューごとの選択の置き場 (`keyboard/list-selection-store.ts`) にあり、側面ペインは `useSyncExternalStore` で購読する。キーの経路はホストへの選択と同じ同期の確定の中で置き場へ書くので、キー 1 つの確定は 1 回のまま。一覧と側面ペインを並べるパネルの組をコンテキストで包まないので、キーのたびに React がその部分木をたどって選択の読み手を探すことは無い (List の押し続けで、コンテキストの走査は 1 回の押し続けあたり 8.1〜10.1 ms から 4.6〜5.4 ms)。
26
+ - **行のクラスは描くたびに重ねない**: DetailList の行の部品 (メタデータの組・種別のバッジ・面談者・コメント・未読の目印) と両ビューの読み込み中のスケルトンは、決まったクラスをモジュールで 1 回、値で選ぶクラスを状態ごとに 1 回だけ重ねる (`createClassMerger`)。もう描いた状態の行を描いても tailwind-merge は呼ばれない (12 行を足して 0 回。重ねていたときは List で 64 回、DetailList で 288 回)。
27
+ - **添付の行の名前も測って収め、何も切らない**: 添付の行の名前は、行の残りの幅に伸びる名前の列 (`data-testid="daily-report-attachment-name-track"`) の幅へ、タイルの格子の名前と同じく測って収める (`observeNameTrackWidth`。文書ごとに 1 つの観測器が格子の容器と行の名前の列を見る)。収めた名前は切り取らない 1 つの文字列で、列幅を超えない (省略記号と拡張子も収まらない幅では、その形を幅に収まるだけの文字で切る)。列幅が届くまでの最初のフレームとサーバー描画は今までどおり CSS の省略。
28
+ 大きさの注記は自分の描画の層を持つ箱 (`relative truncate`) で省略する。添付の節で切り取る箱はどれも自分の描画の層を持つので、添付のある DetailList の行も、窓をずらすスクロールの 1 段で描き直すのは素の行と同じ 2 行 (Chromium 148、幅 952 px と 496 px のカード。以前は 9 行)。収まらない大きさの注記を持つ狭いカード (幅 260 px、列 122 px) では 10 行のまま (README の **File name** の Limit)。
29
+ - **画面に出さない文のトークンは layout を閉じ込める**: `VISUALLY_HIDDEN_CLASS_NAME` 自身が `contain-layout` を持つ (種別のバッジの展開した名前・一致の印の隠した文・ファイル名の読み上げ用の写しのどれも、1 px の箱からはみ出す文が外の配置に加わらない)。
30
+ - **側面ペインの作成者の行の間**: 作成者の行の部分の間は 8 px、折り返した行の間は 4 px (`gap-x-2 gap-y-1`)。種別のバッジは外側の余白を持たないので、折り返して行の頭に来たバッジは行の始まりにそろう。以前は部分の間が縦横とも 4 px で、側面ペインのバッジだけが左の余白 8 px を持ったので、作成者とバッジの間は 12 px、バッジと一致の印の枠の間は 4 px で、折り返したバッジは行の頭から 8 px 内側に始まった。
31
+ - **ログ: 注入した警告の書き先も接頭辞を持つ**: `DailyReportIdsStreamClientOptions.warn` へ注入した書き先は IdsStream のチャンネルのロガーとして包むので、どの行も `[DailyReportIdsStream]` の接頭辞を 1 つ持つ文面と、その後の行のほかの引数で受ける。以前は注入した書き先だけが接頭辞の無い文面を受けた。
32
+ - **`markDailyReportSearchHits` は要求のシグナルを確かめる**: `options` が `AbortSignal` を持つオブジェクトでなければ、どのクエリよりも前にホスト向けの誤りで止める (`options` か `signal` が無ければ `TypeError` の `[daily-report] markDailyReportSearchHits: options.signal must be the request's AbortSignal (new AbortController().signal for a call that is never aborted)`、ほかの値は `; got <値>` で終わる `RangeError`)。以前は分割代入とプロパティの読み取りのエンジンの例外で落ち、空の ID の並びでは `{}` をそのまま通した。
33
+ - **開発者向け**:
34
+ - `src/client/row-reads.spec.ts` は出荷するクライアントのソースを型検査器で読み、位置から日報の行を読む `.at(` が `rowAt` のほかに無いこと (検索の選択の 4 つの読み取りは名前で挙げた例外) と、行の形を `DailyReportRows` の 1 か所でだけ書くことを確かめる。
35
+ - `src/server/service.drafts.spec.ts` はサービスの SQL をメモリの SQLite で流し (`test-helpers/sqlite-report-db.ts`)、作り直した・後から入れた下書きのラベル、2 つの下書きのラベルの公開、ラベルの無い作成、作成区分の DTO を確かめる。
36
+ - 印の範囲 (`markSearchText`) は、出現を 1 つずつ並べる前の定義 (`test-helpers/search-mark-occurrences.ts`) とバイト単位で同じことを、種から再現する 3,000 の乱数の場合・4,000 単位を超える行・適合の語群で確かめる。
37
+ - 行の並び (`ReportRowList`) の編集は、塊の境目の近くの削除・置き換え・差し込みを種から再現する 16 × 200 の場合で素の配列と照合する (`test-helpers/seeded-random.ts`)。
38
+ - 一致の印と面のずれは、印の欄の鍵 14 個 × 面 4 つ (List のカード・側面ペインの記事と関連のタブ・DetailList の行) の表で確かめ、葉が登録する数は描いた `<mark>` の数と等しい。
39
+ - ID のまとまりの送り手は、止めた要求が送る処理の落ち着くまで枠を持つことを確かめる。
40
+
41
+ ### Fixed
42
+
43
+ - **印の計算は出現の数で伸びない**: `search-marks` は、欄ごとに候補の重なる出現を正規化した文字の中で 1 つの連なりにまとめてから生の値へ写し、連なりごとに tier の覆いへ足す (`forEachSearchMarkRun`)。出現ごとのオブジェクトを作らないので、欄の費用は欄の長さ × 候補で、答えは同じ (出現ごとの定義とバイト単位で同じ)。サービスは欄の間 (日報の間を含む) で event loop に順を譲り、欄の前ごとにシグナルを確かめる。
44
+ 以前は欄の出現を 1 つずつ並べてからまとめたので、1 人の作成者が自分の日報の長い本文 1 つで worker の event loop を秒の単位で止め、メモリを 1 GB 越えへ押し上げられた (同じ文字 100 万単位の欄 1 つを、その文字を 1〜30 個並べた 30 語で探す印の計算は、重なる出現が 3,000 万近くあり 4,541 ms・2,044 MB かかった。今は回る連なりが 1,000,029 で 659 ms・323 MB、そのうち 584 ms は行の長さだけに比例する位置つきの写し)。
45
+ - **下書きかの判断は、判断のたびに名前で SQL の中で**: SSE の宛先 (`report-create`・`report-update`・`report-publish` の中身と、`comment-add`・`comment-delete`) は、読み取りの規則と同じ名前と相関の EXISTS で、日報 1 件の 1 行の読み取り (`isDraftReport`) で決める。公開は下書きの名前のラベルをトランザクションの中ですべて外し、外した後も下書きなら公開のイベントを作成者だけへ送る。作成は正準のラベル (`draftLabelNames[0]`) を挿入と同じトランザクションの中で名前で引き、無ければ何も書かずにそのラベルを名乗って失敗する (下の Breaking)。作成区分が下書きの日報の DTO に足す下書きのラベルは、読み取りのたびに名前で引いた正準のラベル自身の ID を持つ。`getDraftLabelId()` は何も覚えず、呼ぶたびに名前で引く。
46
+ 以前は宛先をプロセスの最初に引いて覚えたラベルの ID で決めた (空の答えも覚えた) ので、下書きのラベルを作り直したり後から入れたりすると、worker は下書きの保存とコメントを全員へ配り、読み取りでは隠れたままだった。公開は正準のラベルだけを外したので、2 つ目の下書きの名前のラベルを持つ日報は読み取りで隠れたまま中身が全員へ届いた。作成は正準のラベルが無いとラベルの無い日報 (誰にでも見える) を作った。作成区分の DTO のラベルは、覚えた ID の最小 (ステージングでは「作成中」の 2) を正準の名前で見せた (今は正準のラベルの 3)。
47
+ - **一致の印の store は自分の失敗だけを黙って捨てる**: 握りつぶすのは要求の失敗 (通信の失敗・中断・ほかの状態・読めない本文) だけで、ホストの `onSessionExpired`・429 のやり直しの予約・答えを当てて行へ知らせることが投げた例外はまとまりの拒否になり、ID のまとまりの送り手が枠を空けた後に `reportError` で報告する (ページの `error` の出来事)。報告しないのは、止めた要求がその中断の理由で拒まれたときだけ。以前は 4 つをまとめて 1 つの `try` で捨て、送り手もどの拒否も捨てたので、ホストの処理や当てはめの不変条件の誤りがコンソールにも出来事にも残らなかった。
48
+ - **一覧の外の位置は行を読まない**: ビュー・キーボード・スクロールの位置から行を読む場所はどれも `rowAt` を通り、一覧の行でない位置 (負・端数・長さ以上) には undefined を答える。以前は 0.37.0 の `at` の読み取りが、行の `at` と同じく負の位置を末尾から数えたので、DetailList のスクロールの復元のアンカーが -1 なら末尾の行を復元の先にし、窓の比べ方は -1 の窓を末尾の行と比べえた。
49
+ - **文書の誤り**: README は `search-marks` を本文から ETag を作るエンドポイントに数え、読みの印の `aria-description` がキーボードの経路でも弱い当たりを伝えると書き、動きの一覧に進捗の札の明滅を載せず、注入した警告の書き先を接頭辞の約束の例外にしていた。
50
+
51
+ ### Breaking
52
+
53
+ - **ids のストリームの行の形とエンドポイントの名前**: チャンク行は `items` を持たず列を運び (上の Changed)、エンドポイントは `GET {apiBasePath}/ids-columns`。名前を行の形と一緒に変えたので、前の版のクライアント (開いたままの古いタブ) の要求は 404 で失敗し、黙って空の一覧を描かない。
54
+ Migration: サーバーとクライアントを同じ版で配備する (古いタブは再読み込みで新しい版になる)。ストリームを自分で読むホストは、行を `isIdsStreamChunkLine` で読み、`ids`・`dates` と `counts`・`sourceTypes` と `sources` から行を組む (`DAILY_REPORT_API_ENDPOINTS.idsStream` で経路を組む)。エッジやプロキシの規則が経路 `{apiBasePath}/ids-stream` を名指していれば (バッファリングを切るなど)、`ids-columns` へ移す。
55
+ - **`draftLabelNames` は必須** (`draftLabelName` は削除): サービスの設定 `draftLabelNames` は空白だけでない名前の 1 つ以上の並び (同じ名前の重複は不可。先頭が正準の名前) で、省略・`[]`・空白だけの名前・重複は生成時に `RangeError` (`[daily-report] draftLabelNames must be one or more non-blank label names; got <値>`)。パッケージの既定の名前 (`下書き`) は無い。
56
+ Migration: `draftLabelName: "X"` は `draftLabelNames: ["X"]` に、`draftLabelNames` を省いていたホストは自分のラベルの名前を渡す。正準のラベル (先頭の名前) が無いと日報の作成が失敗するので、配備の前にそのラベルがあることを確かめる。
57
+ - **クライアントの `draftLabelName` は必須**: `config.draftLabelName` はパッケージの既定 (`"draft"`) を持たず、型は `Partial` の外 (渡し忘れは型の誤り)。描画時は空白だけでない文字列でない値 (欠けた値を含む) が `RangeError` (`[daily-report] config.draftLabelName must be a non-blank string (the server's canonical draft label name, draftLabelNames[0]); got <値>`)。`defaultDailyReportClientConfig` と `DailyReportClientConfigDefaults` はこのキーを持たない。
58
+ Migration: サーバーの `draftLabelNames[0]` を `config.draftLabelName` に渡す。
59
+
60
+ ## 0.38.0 (2026-10-09)
61
+
62
+ ### Added
63
+
64
+ - **検索の絞り込み**: 検索を、作成者 (一覧に出る作成者 `employeeName`。詳細と同じ伏せ方)・区分 (`Internal` と `externalSources` の各アダプターの区分)・顧客 (詳細に出る顧客名) で絞り込める。どれも複数の値を選べ、1 つの絞り込みの値どうしは OR、絞り込みどうしと文字とは AND。
65
+ 作成者と顧客は索引の値と完全に等しい値だけが通り (値の無い日報は通らない)、区分は大小文字によらず比べる。絞り込みの値には印を付けない。
66
+ 文字が空白だけ (`String.prototype.trim` が読む空白。クライアントとサーバーが共有する `isBlankSearchText`) で絞り込みが 1 つ以上あれば、絞り込みだけの検索として、閲覧者が読める日報のうち絞り込みを通るものをすべて ID の列の順に並べる (答えの `words` は `[]` で、ハイライトも一致の印も無い)。
67
+ 空白でないのに語が残らない文字 (幅の無い文字だけなど) は、絞り込みがあっても今までどおり 400。
68
+ - **`search` の絞り込みの引数**: `author`・`sourceType`・`customer` (`DAILY_REPORT_API_QUERY_PARAMS` の `searchAuthor`・`searchSourceType`・`searchCustomer`) を値ごとに繰り返して送る (値はコンマを含みうるので、コンマでつながない)。上限 (`DAILY_REPORT_SEARCH_FILTER_LIMITS`) は、1 つの絞り込みの値 16 個・値 1 つ 200 単位 (UTF-16)・作成者と顧客の値の長さの合計 300 単位。
69
+ 値は送られたままに読み (切り詰めも捨てもしない)、空白だけの値・正規の形でない値 (前後に `trim` が読む空白がある・対を持たないサロゲートを含む)・長すぎる値・多すぎる値・同じ値の重複 (区分は大小文字によらず比べる)・長さの合計の超過・`Internal` でも外部ソースの区分でもない区分は、`{ error: { message: "Invalid search filters", reason } }` の 400。
70
+ 理由は `filter-blank-value`・`filter-value-not-canonical`・`filter-value-too-long`・`filter-too-many-values`・`filter-duplicate-value`・`filters-too-long`・`filter-unknown-source-type`。400 の順は `q` と `combine`・絞り込み・文字の計画で、どれもサービスの呼び出しより前。絞り込みの引数の無い要求は、SQL に条件を足さない。
71
+ - **絞り込みの候補 `GET {apiBasePath}/search-facets`** (JSON。`DailyReportSearchFacets` の `{ sourceTypes, authors, customers }`): 区分は、閲覧者が読める区分を設定の順 (`Internal`、続いて `externalSources` の順。大小文字だけが前の区分と違う後の区分は除く) に、問い合わせずに返す。
72
+ 作成者と顧客は、閲覧者が開ける日報の索引の文書の NULL でない値を、検索と同じ範囲の条件の SQL の `GROUP BY` で重複なく、列の順に、件数を絞らずに返す (ほかの人の下書き・消した日報・見えない区分の日報の値は出ない)。サーバーではキャッシュしない。
73
+ 候補と絞り込みは索引の値を読む (パッケージの保存と突き合わせが書き直す) ので、パッケージの外で元の行を変えるホストは、その日報の突き合わせを流す。
74
+ 受け入れは同じ工場から作る 4 つ目のインスタンスで、検索と同じ `concurrency` と `rateLimitPerMinute` に自分のバケットを持つ (拒否の行は `route=search-facets`)。検索の設定が無ければ 404、内部 ID の分からない利用者には受け入れも問い合わせも無しに空の候補を返し、答えは ETag 付きで、同じ候補への `If-None-Match` には 304。
75
+ サービスの `listDailyReportSearchFacets(scope, userId)` と、絞り込みが名指せる区分 `searchFilterSourceTypes`。
76
+ - **ホストが描く絞り込みの部品 `config.search.renderFilterSelect`**: 関数 (`DailyReportSearchFilterSelectRenderer`) を渡すと、検索窓の入力欄の後に絞り込みの切り替え (`labels.searchFiltersToggle(n)`。n は選んだ値の数) が出て、開くと検索の行の下に、作成者・ソース区分・顧客の 3 つの箱と状態の行を持つ絞り込みの行が出る。
77
+ 候補は行を開くたびに読み直し (要求は 1 本ずつ。読み直しの間と、読み直しに失敗したあとは前の候補のまま)、作成者と顧客はロケールの照合の順、区分はサーバーの順で、見出しはバッジの見出し。渡さなければ、切り替えも行も候補の要求も無い。
78
+ 箱 (`<fieldset>`) は見出し (`<legend>`) を部品の上に見せ、見出しが箱の名前になる。読み直しで候補から消えた選んだ値は、その箱の候補の後ろに足すので、部品はそれを見せて外せる。
79
+ 箱ごとの部品は `{ facet, label, options, value, onChange, locale }` (`DailyReportSearchFilterSelectProps`) を受ける制御された部品で、`onChange` へは `options` の値の重複の無い部分集合を渡す (それ以外は `RangeError`)。上限を超える選択は受けずに状態の行で知らせる (知らせは、外からの解除や新しい選択で選択が変われば消える)。
80
+ 箱ごとに誤りの境界を置くので、描画で投げた部品はその箱だけを `labels.searchFilterUnavailable` にし、ほかの箱と検索窓は動き続ける。境界の中に Suspense の境界も置くので、描画を待たせる部品 (`React.lazy`・`use()`) はその箱だけに `labels.searchFiltersLoading` を見せ、画面のほかの所を隠さない。絞り込みの変更は入力欄の今の文字と AND/OR で確定し直し、Enter・デバウンス・AND/OR の切り替えも今の絞り込みを載せる。
81
+ パッケージはブラウザーのコードに `@aiquants/select-box` も `@aiquants/fuzzy-search` も取り込まない (`src/client-bundle.structure.spec.ts` がソースと作り直した束で確かめる) ので、パッケージのブラウザーの下限は変わらない。部品は自分の下限を持つ (`@aiquants/select-box` で作る部品は、`Intl.Segmenter` のために Firefox 125)。
82
+ - **ラベル 6 個** (固有キーは 102 から 108。文字列のキー 89・整形関数 19。キーの表は全 119 キー): `searchFilterSourceType` (en `Source`・ja `ソース区分`)・`searchFiltersLoading`・`searchFiltersTooMany`・`searchFilterUnavailable` と、整形関数の `searchFiltersToggle(count)` (en `Filters` / `Filters (3)`・ja `絞り込み` / `絞り込み (3)`。0 なら数を出さない)・`searchFiltersFailed(failure)` (候補を読めなかったときの状態の行。`busy`・`unavailable`・`failed` ごとに、検索の失敗の文言と別の、候補のことを述べる文言。`busy` と `failed` は開き直せば読み直すことも述べ、`unavailable` (サーバーが絞り込みを持たない) は述べない。ja `絞り込みの候補を読み込めませんでした。絞り込みを閉じて開き直すと読み直します`)。作成者と顧客の箱の見出しは `author` と `customer`。
83
+ - **公開の名前**: server の `DailyReportSearchCriteria`・`DailyReportSearchFilters`・`DailyReportSearchNameFilter`・`DailyReportSearchFacets`、client の `DAILY_REPORT_SEARCH_FILTER_FACETS`・`DailyReportSearchFilterFacet`・`DailyReportSearchFilterOption`・`DailyReportSearchFilterSelectProps`・`DailyReportSearchFilterSelectRenderer`。
84
+ 共有エントリの `DAILY_REPORT_API_ENDPOINTS` は `searchFacets` を、`DAILY_REPORT_API_QUERY_PARAMS` は `searchAuthor`・`searchSourceType`・`searchCustomer` を持つ。
85
+
86
+ ### Changed
87
+
88
+ - **索引の文書は絞り込みの値を持つ**: `DailyReportSearchDocument` の `author_name` と `customer_name` (NULL を許す `nvarchar(200)`、`Latin1_General_100_BIN2`) に、作成者と顧客を、一覧が見せる正規の形 (`String.prototype.trim` で前後の空白を除き、対を持たないサロゲートを U+FFFD にし、何も残らなければ NULL) で書く。ホストは作成者と顧客の表示の値を、前後の空白を除いて 200 単位 (UTF-16) までに保つ。超えた値は日報の ID とこの取り決めを名乗る例外 (その日報の保存は失敗し、突き合わせはその日報の区切りで止まる)。
89
+ `defineDailyReportSearchSchema` と `DailyReportSearchDocumentTable` は 2 列 (`authorName`・`customerName`) を持つ。文書の要約は絞り込みの値の節 (`P`) を持つので、値だけが変わった日報も突き合わせが書き直す。要約は文字を UTF-16 の単位のまま入れる (対を持たないサロゲートと U+FFFD を見分ける)。正規化の版は変わらない。
90
+ - **検索のセッションは絞り込みと一緒に確定する**: 世代は、正規の形の絞り込み (値ごとに重複を除き、UTF-16 の単位の順に並べたもの) が変わったときにも進み (並びだけの違いでは進まない)、静かな送り直しも同じ絞り込みを載せ、解除は絞り込みも消す。
91
+ 消去のボタンと Esc は文字だけを消し、絞り込みが残れば絞り込みだけの検索として続け、残らなければ解除する。消去のボタンは消す文字があるときだけ出る (絞り込みだけの検索で入力欄が空なら出ず、Esc も何もしない)。絞り込みの部品を渡さないホストでは、どれも今までどおり。
92
+ - **開発者向け**: `src/client-bundle.structure.spec.ts` は、`@aiquants/select-box` も `src/client` と `src/shared` のソースと作り直した `client.mjs`・`index.mjs` に入らないことを確かめる。
93
+
94
+ ### Breaking
95
+
96
+ - **検索の設定を持つホストは、配備の前に文書の表へ 2 列と索引を足す**: この版は保存と突き合わせのたびに `author_name` と `customer_name` を書くので、列の無い表ではどの保存も失敗する。`search.tables.document` に自前のモデルを渡すホストは、モデルにも `authorName` と `customerName` が要る (無ければ `search` の検査が `RangeError`)。
97
+ Migration: 配備の前に `DailyReportSearchDocument` へ `author_name nvarchar(200) collate Latin1_General_100_BIN2 NULL` と `customer_name nvarchar(200) collate Latin1_General_100_BIN2 NULL` を足し、それぞれに索引を作る (候補の `GROUP BY` と絞り込みの `IN` が読む)。列は NULL を許すので、0.36.0 のプロセスは足した後もそのまま動く。
98
+ データベースへ書くすべてのサーバーがこの版で動いてから `service.reconcileSearchIndex` を 1 回流す (要約が変わるので、すべての文書を書き直して 2 列を埋める)。流し終えるまで、書き直していない文書は作成者と顧客の絞り込みを通らず、その値は候補に出ない。
99
+ 前の版のサーバーが後から書いた文書は、次の突き合わせまで 2 列が空か古いままなので、前の版が無くなってからもう 1 回流し、書き直しが 0 件であることで確かめる。
100
+ - **`service.searchDailyReports(scope, userId, criteria)`**: 3 つ目の引数は計画 (`DailyReportSearchPlan`) でなく条件 (`DailyReportSearchCriteria`: `{ plan, filters }`。絞り込みだけの検索では `plan` が null)。
101
+ Migration: サービスを直接呼ぶホストは、計画を `{ plan, filters: { sourceTypes: null, authors: null, customers: null } }` で包む。
102
+
5
103
  ## 0.37.0 (2026-10-09)
6
104
 
7
105
  ### Added
@@ -62,7 +160,7 @@ All notable changes to `@aiquants/daily-report` are documented here.
62
160
  ### Breaking
63
161
 
64
162
  - **`DailyReportActionState.items` は配列でない**: アクションの状態の行 (`useDailyReportActionContext().items`) は、長さ (`length`)・位置の日報 (`at`)・走査だけで読む永続の並び (型は `Pick<ReadonlyArray<DailyReportItem>, "length" | "at"> & Iterable<DailyReportItem>`) で、行が変わるたびに新しいオブジェクトになる。ビューの `dailyReportItems` も同じ形を受ける (配列も渡せる)。
65
- Migration: `items` を配列として読むホスト (`items[i]`・`map`・`filter`・`find`) は、`items.at(i)`・`for…of`・`Array.from(items)` で読む (`Array.from` は一覧全体を写す)。
163
+ Migration: `items` を配列として読むホスト (`items[i]`・`map`・`filter`・`find`) は、`items.at(i)`・`for…of`・`Array.from(items)` で読む (`Array.from` は一覧全体を写す)。型を書き写していたホストは、0.38.0 より後の版から、client entry の名前 `DailyReportRows` で読み込める。
66
164
  - **`markDailyReportSearchHits` は要求のシグナルを求める**: サービスの `markDailyReportSearchHits(scope, userId, plan, ids, { signal })` は 5 つ目の引数に要求のシグナルを取り、確かめた時点で中断されていればその `reason` を投げる。
67
165
  Migration: サービスを自分で呼ぶホストは、要求の `request.signal` (止めないなら `new AbortController().signal`) を渡し、投げた `reason` を要求の中断として扱う。
68
166