@aiquants/daily-report 0.45.0 → 0.46.1

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,44 @@
2
2
 
3
3
  All notable changes to `@aiquants/daily-report` are documented here.
4
4
 
5
+ ## 0.46.1 (2026-10-16)
6
+
7
+ 版番号は 0.46.1 (patch) とする: 公開の型は変えず、外部ソースの日報へ誤って書き込む経路を閉じる。
8
+
9
+ ### Fixed
10
+
11
+ - **外部ソースの日報は、持ち主でも変えられない**: 編集と削除の入口 (DetailList のカードと List の側面ペインのボタン、ダブルクリック) と下書きの
12
+ 自動の編集は、閲覧者が持ち主かどうかだけで出ていた。そのため外部ソースの日報 (ホストの `externalSources` のアダプタが外部の表から読む日報) の
13
+ 持ち主にも出ていた。保存すると件名だけが hub に書かれて外部の表と食い違い、本文は内部の表に行が無いので何も書かれないまま成功を答えた
14
+ (画面は保存の直後だけ新しい本文を見せ、読み直すと元の本文に戻る)。削除と公開も hub に書けた。
15
+ 今は、入口と下書きの自動の編集を、パッケージ自身の区分 (`Internal`。字面のまま比べる) の持ち主の日報にだけ出す。サーバーの更新・公開・削除は、
16
+ 外部ソースの日報を持ち主を比べる前に断り (action は 403 `{"error":"Reports from this source are read-only"}`、サービスは `Forbidden`)、
17
+ 何も書かず知らせない。区分の判定は 1 つの関数 (`isMutableDailyReportSource`) にあり、サーバーの関門とクライアントの入口が同じものを読む。
18
+ ホストへの影響: サービスの `updateDailyReport`・`publishDailyReport`・`deleteDailyReport` を外部ソースの日報で呼んでいた所は `Forbidden` を受ける
19
+ (どれも書き込みが外部の表と食い違う経路だった)。
20
+
21
+ ## 0.46.0 (2026-10-16)
22
+
23
+ 版番号は 0.46.0 (minor) とする: 設定の `streamKey` と公開の `buildDailyReportSseStreamKey` を置き換える (下の Breaking)。
24
+
25
+ ### Changed
26
+
27
+ - **Redis のキーは 1 つの接頭辞から組む**: SSE のストリームとワーカーをまたぐ 3 種の epoch (ID の一覧・営業日・詳細) のキーを、サービスの設定
28
+ `redisKeyPrefix` (既定 `daily-report`) から組む (`<接頭辞>:sse-stream:v4`・`<接頭辞>:ids:epoch`・`<接頭辞>:date-epoch:<営業日>`・
29
+ `<接頭辞>:detail-epoch:<日報の ID>`)。epoch のキーは固定の名前だったので、同じ Redis を使う環境 (開発と本番など) は、ストリームのキーを分けても
30
+ 互いのキャッシュを無効にし合った。環境ごとに接頭辞を 1 つ渡せば、知らせも epoch も重ならない。既定の接頭辞のキーは 0.45.0 と字面まで同じ。
31
+
32
+ ### Breaking
33
+
34
+ - **設定の `streamKey` は `redisKeyPrefix` に置き換わる**: 設定に `streamKey` があると、値によらず生成のときに `TypeError`
35
+ (`[daily-report] redisKeyPrefix must be given in place of the removed key streamKey`) で止まる。接頭辞は空白も制御文字も含まない 1 文字以上で、`:` で
36
+ 始まらず終わらないもの。ほかの値は `RangeError` (`[daily-report] redisKeyPrefix must be a Redis key prefix of …; got <値>`) で止まる。
37
+ Migration: `streamKey` に基のキーを渡していたホストは、環境を見分ける名前を `redisKeyPrefix` に渡す (ストリームは `<接頭辞>:sse-stream:v4` になり、
38
+ 前の `<基のキー>:v4` のストリームは書かれなくなって手で消せる)。既定のまま使っていたホストは何も変えなくてよい。
39
+ - **公開の `buildDailyReportSseStreamKey(streamKey)` は `buildDailyReportRedisKeys(redisKeyPrefix)` に置き換わる**: 接頭辞の下のどのキーも返す
40
+ (`DailyReportRedisKeys`: `sseStream`・`idsEpoch`・`dateEpochPrefix`・`detailEpochPrefix`)。
41
+ Migration: `buildDailyReportSseStreamKey(base)` は `buildDailyReportRedisKeys(prefix).sseStream` に替え、epoch を直に読み書きする試験は返したキーを使う。
42
+
5
43
  ## 0.45.0 (2026-10-16)
6
44
 
7
45
  版番号は 0.45.0 (minor) とする: ピアの `@aiquants/swipe-overlay` の下限を上げる (下の Breaking)。
package/README.md CHANGED
@@ -526,7 +526,10 @@ its payload — with one strict parser each, after authentication and before any
526
526
  `{"error":"Report visibility is unavailable"}` with `Retry-After: 5` and `Cache-Control: no-store`, before the internal user lookup and any intent.
527
527
  Then the internal user (`User not found`, 404). A `create` whose scope does not show the package's own source type (`Internal`) is 403
528
528
  `{"error":"Creating reports is restricted"}` before any query; the other intents answer a report the scope hides as 404 (**Where it applies** in
529
- [Source-type visibility](#source-type-visibility)).
529
+ [Source-type visibility](#source-type-visibility)). An `update`, `publish` or `delete` of a report the scope shows but of an external source (any
530
+ source type other than the package's own `Internal`, compared exactly) is 403 `{"error":"Reports from this source are read-only"}` before the owner
531
+ is compared and before any write: the package writes only the reports it stores itself, and its client draws the edit and delete entrances and a
532
+ draft's automatic editor only for the viewer's own `Internal` reports.
530
533
  - **One wire contract for the action and the JSON endpoints**, which the server, the package's client and a host that calls the routes itself all read
531
534
  from the shared entry (`@aiquants/daily-report`; neither side restates a name):
532
535
  - the intents (`DAILY_REPORT_ACTION_INTENTS` and their type `DailyReportActionIntent`, from which the server's form reading and the client's
@@ -605,7 +608,7 @@ its payload — with one strict parser each, after authentication and before any
605
608
  shared draft-label test, beside the hub row) or true when its creation category is one of the names; a row whose flag reads as anything but 1 or 0
606
609
  throws.
607
610
  The client compares no name: the views open the viewer's own draft in the editor by `isDraft` alone, so a draft named by any of the names, or by a
608
- variant the collation equates, behaves as one ([Client wiring](#client-wiring)). The SSE stream's key carries the wire version (`<streamKey>:v4`; [Realtime architecture](#realtime-architecture)),
611
+ variant the collation equates, behaves as one ([Client wiring](#client-wiring)). The SSE stream's key carries the wire version (`<redisKeyPrefix>:sse-stream:v4`; [Realtime architecture](#realtime-architecture)),
609
612
  so an entry written before `isDraft` existed is never read.
610
613
  - `resolveVisibleSourceTypes(request, { reason })` — **Required row-level authorization port.** Returns the `Hub.source_type` values this request may
611
614
  view, or the explicit opt-out `{ unrestricted: "<reason>" }` of a deployment that does not restrict by source type. Applies uniformly to every server
@@ -665,11 +668,18 @@ its payload — with one strict parser each, after authentication and before any
665
668
  The JSON answers themselves write nothing, and no line carries a request header's value or traces an answer given before the endpoint (the 401, 404
666
669
  and 405), so a client decides neither the number nor the size of the lines.
667
670
  - Primary tuning parameters: `idsTtlMs` (180 s) / `businessDateTtlMs` (300 s) / `cacheDefaultTtlMs` (60 s) / `cacheMaxBuckets` (4,096) /
668
- `cacheMaxBytes` (512 MiB per process; both **The SQL result cache** in [The API route](#the-api-route)) / `streamKey` / `streamMaxLen` (10,000
671
+ `cacheMaxBytes` (512 MiB per process; both **The SQL result cache** in [The API route](#the-api-route)) / `streamMaxLen` (10,000
669
672
  entries); the attachment tuning lives in the `attachments` block ([Attachments](#attachments)).
670
673
  Every number among them is an integer of at least 1, and only an omitted one takes its default: `0`, a negative number, `NaN`, a fraction or 2^53
671
674
  stops `createDailyReportService` and `createDailyReportServer` with a `RangeError` that names the key
672
675
  (`[daily-report] idsTtlMs must be an integer >= 1; got 0`).
676
+ - `redisKeyPrefix` (`daily-report` by default) — the prefix of every Redis key of the service: the SSE stream `<prefix>:sse-stream:v4` and the
677
+ cross-worker epochs `<prefix>:ids:epoch`, `<prefix>:date-epoch:<business date>` and `<prefix>:detail-epoch:<report id>`. Environments that share
678
+ one Redis (development and production, say) each pass their own prefix, so that neither reads the other's notifications nor drops the other's
679
+ cached reads. The prefix is one or more characters without whitespace or control characters, neither
680
+ starting nor ending with `:`, and only an omitted one takes the default: any other value stops `createDailyReportService` and
681
+ `createDailyReportServer` with a `RangeError` (`[daily-report] redisKeyPrefix must be a Redis key prefix of …; got ""`), and a configuration that
682
+ carries `streamKey` stops with a `TypeError` that names `redisKeyPrefix` instead.
673
683
 
674
684
  #### Configuration errors
675
685
 
@@ -1637,10 +1647,10 @@ and without the retry the victim would answer 500 and lose what it wrote.
1637
1647
 
1638
1648
  ### Realtime architecture
1639
1649
 
1640
- 1. Mutation services publish to Redis Stream (`<streamKey>:v4`, `daily-report:sse-stream:v4` by default: the key carries the wire version of the
1650
+ 1. Mutation services publish to Redis Stream (`<redisKeyPrefix>:sse-stream:v4`, `daily-report:sse-stream:v4` by default: the key carries the wire version of the
1641
1651
  stream's messages and their embedded report, so a deployment that changes a required key of them reads no entry an older version wrote, and the stream of the previous version
1642
1652
  stops growing and can be deleted by hand; a host that reads or writes the stream itself, such as an integration test, builds the key with the
1643
- server entry's `buildDailyReportSseStreamKey(streamKey)` instead of restating the version) in sequence:
1653
+ server entry's `buildDailyReportRedisKeys(redisKeyPrefix).sseStream` instead of restating the version) in sequence:
1644
1654
  `invalidate -> epoch increment -> SSE publish`.
1645
1655
  2. `DailyReportSseReader` uses a single blocking `xRead` loop to fan-out events to all SSE connections (eliminating per-connection Redis TCP
1646
1656
  connections). `createDailyReportServer` creates it; `createDailyReportHandlers` takes the reader as `sseReader` typed by the port
@@ -4596,8 +4606,9 @@ Documenting a name here is no reason to export it, and the values that tune the
4596
4606
  `DAILY_REPORT_SHARP_THUMBNAIL_DECODE_BUDGET_BYTES` / `DAILY_REPORT_SHARP_THUMBNAIL_DECODE_WORK_MODEL` (the render deadline `renderMs` and the
4597
4607
  decode-work and walk bounds, for a host's conformance checks) / `DAILY_REPORT_ATTACHMENT_THUMBNAIL_OUTPUT_MEDIA_TYPES` /
4598
4608
  `DAILY_REPORT_ATTACHMENT_THUMBNAIL_MAX_OUTPUT_BYTES`.
4599
- - SSE and caching: `buildDailyReportSseStreamKey(streamKey)` (the Redis stream key the service writes and reads the SSE entries of, the base key
4600
- with the wire version appended, `<streamKey>:v4`, for a host that reads or writes the stream itself) / `DailyReportSseReader` /
4609
+ - SSE and caching: `buildDailyReportRedisKeys(redisKeyPrefix)` (`DailyReportRedisKeys`: the keys the service builds under the prefix, the SSE
4610
+ stream `sseStream` with the wire version, `<prefix>:sse-stream:v4`, and the epoch keys `idsEpoch`, `dateEpochPrefix` and `detailEpochPrefix`,
4611
+ for a host that reads or writes the stream or the epochs itself) / `DailyReportSseReader` /
4601
4612
  `readDailyReportSseStreamEntry(raw, logger)` (reads one Redis stream entry, `{ id, message }` as the client returns it, once: `{ entry, json }`,
4602
4613
  the frozen `StreamEntry` the shared reader hands every subscriber and the entry's parsed JSON value; for a host whose own reader keeps the same
4603
4614
  contract) /