@aiquants/daily-report 0.14.1 → 0.15.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.md +30 -1
- package/dist/server.d.mts +84 -16
- package/dist/server.d.ts +84 -16
- package/dist/server.js +10 -10
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +10 -10
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client/streaming/daily-report-ids-stream-client.spec.ts +25 -15
- package/src/server/handlers.ts +73 -9
- package/src/server/handlers.visibility.spec.ts +158 -0
- package/src/server/ports.ts +30 -3
- package/src/server/service.spec.ts +95 -0
- package/src/server/service.ts +49 -11
- package/src/server/visibility.spec.ts +135 -0
- package/src/server/visibility.ts +107 -18
package/README.md
CHANGED
|
@@ -144,8 +144,10 @@ createDailyReportServer({
|
|
|
144
144
|
| Return value | Meaning |
|
|
145
145
|
| --- | --- |
|
|
146
146
|
| `undefined` / `null` | Unrestricted — every source type (the default when the port is not injected). |
|
|
147
|
-
| `["legacy", "Internal"]` | Only those source types are visible. |
|
|
147
|
+
| `["legacy", "Internal"]` | Only those source types are visible (bodies **and** comments). |
|
|
148
148
|
| `[]` | Nothing is visible (zero rows) — **not** the same as `null`. |
|
|
149
|
+
| `{ read: [...] }` | Same as returning the bare array — the comment dimension follows `read`. |
|
|
150
|
+
| `{ read: [...], comment: [...] }` | Bodies and comments restricted independently. See [Comment visibility](#comment-visibility). |
|
|
149
151
|
| throws | Treated as `[]` (deny all). The failure is logged; an authorization-store outage never falls open. |
|
|
150
152
|
|
|
151
153
|
- **Vocabulary**: the strings are `DailyReportHub.source_type` **values** — the `sourceType` of each entry you registered in `externalSources`, plus `"Internal"` which the package writes for reports it creates. They are *not* your authorization resource keys; mapping a resource key (e.g. `report_legacy`) onto a source type (e.g. `legacy`) is the consuming app's job. Read the canonical set at boot from `dailyReportServer.knownSourceTypes` and assert your mapping against it — an unknown token silently matches zero rows.
|
|
@@ -157,6 +159,33 @@ createDailyReportServer({
|
|
|
157
159
|
- **Performance**: the port is called once per request on the critical path (cache keys incorporate the resolved set), so keep it fast — cache grants per session/user with a short TTL (≤ 60s). A live SSE connection re-resolves on its existing keep-alive tick (~60s) to bound how long a revoked grant keeps streaming; that call passes `reason: "refresh"` and reuses the connection's original `Request`, so a cache keyed on request identity **must** be bypassed when `reason === "refresh"` or the re-check silently returns the stale set.
|
|
158
160
|
- **SSE latency**: because a live connection re-resolves periodically, a grant change (in either direction) takes effect for the stream within ~60s. Events dropped before a widening are not replayed; the next read (which re-resolves per request) restores them.
|
|
159
161
|
|
|
162
|
+
### Comment visibility
|
|
163
|
+
|
|
164
|
+
Return `{ read, comment }` from the same port to restrict **reading and writing comments** independently of the report bodies — this is what connects a per-category comment permission (e.g. `daily_report_legacy_comment`) to actual access control.
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
resolveVisibleSourceTypes: async (request, { reason }) => {
|
|
168
|
+
const grants = await yourAuthz.grantsFor(request, { skipCache: reason === "refresh" })
|
|
169
|
+
return {
|
|
170
|
+
read: grants.readableSourceTypes, // e.g. ["legacy", "Internal"]
|
|
171
|
+
comment: grants.commentableSourceTypes, // e.g. ["Internal"] — omit the key to follow `read`
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| `comment` | Meaning |
|
|
177
|
+
| --- | --- |
|
|
178
|
+
| omitted | Follows `read` — identical to returning a bare array. This is the default and the only spelling for "same as bodies". |
|
|
179
|
+
| `[]` | Comments are invisible and cannot be written, while the bodies stay readable. |
|
|
180
|
+
| `["Internal"]` | Only those source types' comments are visible/writable. Narrowed to `read ∩ comment` at construction, since comments only ever ship inside an already-read-filtered report. |
|
|
181
|
+
|
|
182
|
+
- **`null` is rejected by the type system** — a second spelling for "follow `read`" would make the dimension four-state and reintroduce exactly the unrestricted-vs-deny-all confusion the tri-state contract exists to prevent. A `null` that slips through at runtime fails **closed**.
|
|
183
|
+
- **What it covers**: `DailyReportDetail` carries comments in **two** structurally different fields — `commentItems` (the package's own comment table) and `comments` (legacy JSON supplied by an `externalSources` adapter). Both are gated. They are emptied, never omitted: the keys are required by the SSE schema, and dropping them would make a receiver discard the whole message.
|
|
184
|
+
- **Live events**: `comment-add` / `comment-delete` are judged on the comment dimension, and the report payload embedded in `report-create` / `report-update` / `report-publish` has its two comment arrays emptied per viewer at delivery time.
|
|
185
|
+
- **Your own comments are always visible and always deletable** (`commentItems` only — the legacy `comments` array carries no user identity, so it is all-or-nothing per source type). Hiding them would strand existing comments as undeletable the moment a grant is revoked, with no security gain.
|
|
186
|
+
- **Writing to external sources remains impossible regardless** — `addComment` rejects any non-`Internal` source type outright, so for external categories the comment dimension effectively controls *reading*.
|
|
187
|
+
- **Cache**: the dimension is folded into the cache-key digest, so revoking only the comment grant invalidates immediately. When `comment` follows `read` (the default) the digest is byte-identical to before, so existing deployments see no cache churn.
|
|
188
|
+
|
|
160
189
|
## Client wiring
|
|
161
190
|
|
|
162
191
|
```tsx
|
package/dist/server.d.mts
CHANGED
|
@@ -73,16 +73,42 @@ type DailyReportAuthenticate = (request: Request, options: {
|
|
|
73
73
|
} | {
|
|
74
74
|
failureRedirect: null;
|
|
75
75
|
}) => Promise<DailyReportAuthResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Per-dimension visible source types (report body vs. comments).
|
|
78
|
+
* 次元ごとの可視 sourceType (本文とコメント)。
|
|
79
|
+
*
|
|
80
|
+
* コメントの閲覧・記入を本文の可視性から独立して絞るための形。
|
|
81
|
+
*
|
|
82
|
+
* - `read`: 本文 (日報行) を閲覧してよい sourceType。**空配列は 0 件**。
|
|
83
|
+
* - `comment` **省略**: コメント次元は `read` に従う (単一次元と同義・現行互換)。
|
|
84
|
+
* - `comment: []`: コメント 0 件かつ記入不可。
|
|
85
|
+
*
|
|
86
|
+
* ❗ `comment` に `null` は受け付けない。「`read` に従う」の綴りを 2 つ作ると、
|
|
87
|
+
* ポート全体の nullish (= 無制限) と次元レベルの nullish が混ざって 4 状態になり、
|
|
88
|
+
* 本ファイルが一貫して避けている「無制限と全拒否の取り違え」を次元単位で再発させる。
|
|
89
|
+
*
|
|
90
|
+
* ❗ `read` の外側にあるコメントトークンは意味を持たない (コメントは必ず read で絞られた
|
|
91
|
+
* 詳細の内側にしか載らない)。`DailyReportViewerScope.restrictTo` が構築時に
|
|
92
|
+
* `read` との積集合へ狭めるため、設定ミスは黙って安全側へ倒れる。
|
|
93
|
+
*/
|
|
94
|
+
type DailyReportVisibilityDimensions = {
|
|
95
|
+
/** 本文を閲覧してよい sourceType。空配列は 0 件。 */
|
|
96
|
+
read: readonly string[];
|
|
97
|
+
/** コメントを閲覧・記入してよい sourceType。省略時は `read` に従う。空配列は 0 件かつ記入不可。 */
|
|
98
|
+
comment?: readonly string[];
|
|
99
|
+
};
|
|
76
100
|
/**
|
|
77
101
|
* Raw return of the visible-source-type resolver.
|
|
78
102
|
* 可視 sourceType リゾルバの生戻り値。
|
|
79
103
|
*
|
|
80
104
|
* - `undefined` / `null`: 無制限 (全件)。ポート未注入時と同じ既定の意味。
|
|
81
|
-
* - `string[]`:
|
|
105
|
+
* - `string[]`: 本文・コメントの双方をその集合に限定。要素は `DailyReportHub.sourceType` の値であり、
|
|
106
|
+
* **空配列は 0 件**。`{ read: <配列> }` と完全に同義。
|
|
107
|
+
* - `{ read, comment? }`: 次元ごとに指定する ({@link DailyReportVisibilityDimensions})。
|
|
82
108
|
*
|
|
83
|
-
* 中立契約: いかなる認可パッケージの型 (`PermissionView` 等)
|
|
109
|
+
* 中立契約: いかなる認可パッケージの型 (`PermissionView` 等) にも依存しない素の文字列と配列のみ。
|
|
84
110
|
*/
|
|
85
|
-
type DailyReportVisibleSourceTypes = readonly string[] | null | undefined;
|
|
111
|
+
type DailyReportVisibleSourceTypes = readonly string[] | DailyReportVisibilityDimensions | null | undefined;
|
|
86
112
|
/**
|
|
87
113
|
* Why the visible-source-type resolver is being called.
|
|
88
114
|
* 可視 sourceType リゾルバが呼ばれた理由。
|
|
@@ -1547,6 +1573,15 @@ declare function defineDailyReportSchema<S extends string>(schemaName: S, opts:
|
|
|
1547
1573
|
*
|
|
1548
1574
|
* この 2 状態の取り違えは情報漏洩か機能停止に直結するため、判定・畳み込み・キー生成を
|
|
1549
1575
|
* 本ファイルへ一元化し、ハンドラ層とサービス層の双方がここだけを参照する。
|
|
1576
|
+
*
|
|
1577
|
+
* 閲覧範囲は**次元ごと**にこの 2 状態を持つ:
|
|
1578
|
+
*
|
|
1579
|
+
* - `visible` = 本文 (日報行) を閲覧してよい区分。
|
|
1580
|
+
* - `commentable` = コメントを閲覧・記入してよい区分。常に `visible` の部分集合。
|
|
1581
|
+
*
|
|
1582
|
+
* 次元が増えても状態数は増やさない。「もう一方の次元に従う」は
|
|
1583
|
+
* {@link DailyReportViewerScope.restrictTo} が**構築時に解決**するため、各次元は最後まで
|
|
1584
|
+
* 厳密に 2 状態であり、本ファイルの判定ヘルパーは次元を問わずそのまま適用できる。
|
|
1550
1585
|
*/
|
|
1551
1586
|
|
|
1552
1587
|
/**
|
|
@@ -1562,10 +1597,19 @@ type VisibleSourceTypeSet = ReadonlySet<string> | null;
|
|
|
1562
1597
|
* `Set` 化する。**空配列は空 `Set` (0 件) を返し、`null` (無制限) へは決して倒さない** —
|
|
1563
1598
|
* この取り違えが情報漏洩か機能停止に直結するため。
|
|
1564
1599
|
*
|
|
1565
|
-
*
|
|
1600
|
+
* ❗ 引数は**単一次元のトークン配列に限る**。次元別オブジェクト
|
|
1601
|
+
* ({@link DailyReportVisibilityDimensions}) をここへ渡すと配列ガードに掛かって全拒否になるため、
|
|
1602
|
+
* 次元の解体は {@link DailyReportViewerScope.restrictTo} が行い、ここには常に配列だけが届く。
|
|
1603
|
+
*
|
|
1604
|
+
* ❗ オーバーロードで「**配列を渡した場合は決して `null` (無制限) を返さない**」という不変条件を
|
|
1605
|
+
* 型に載せている。これは実装の偶然ではなく本ファイルの中核契約 (空配列は 0 件であって無制限では
|
|
1606
|
+
* ない) そのものであり、呼び出し側が `null` 分岐を書いて無制限へ倒す余地を型で塞ぐ。
|
|
1607
|
+
*
|
|
1608
|
+
* @param raw Raw single-dimension resolver value. 単一次元の生値。
|
|
1566
1609
|
* @returns Normalized visible set. 正規化済みの可視集合。
|
|
1567
1610
|
*/
|
|
1568
|
-
declare function normalizeVisibleSourceTypes(raw:
|
|
1611
|
+
declare function normalizeVisibleSourceTypes(raw: readonly string[]): ReadonlySet<string>;
|
|
1612
|
+
declare function normalizeVisibleSourceTypes(raw: readonly string[] | null | undefined): VisibleSourceTypeSet;
|
|
1569
1613
|
/**
|
|
1570
1614
|
* Tests whether a report's sourceType is visible under the given set.
|
|
1571
1615
|
* ある日報の sourceType が可視集合に含まれるかの三値判定。
|
|
@@ -1625,24 +1669,36 @@ declare function buildVisibleSourceTypeCondition(sourceTypeColumn: AnyMsSqlColum
|
|
|
1625
1669
|
declare class DailyReportViewerScope {
|
|
1626
1670
|
#private;
|
|
1627
1671
|
private constructor();
|
|
1628
|
-
/**
|
|
1672
|
+
/** 正規化済みの本文可視集合。`null` は無制限、空集合は 0 件 (fail-close)。 */
|
|
1629
1673
|
get visible(): VisibleSourceTypeSet;
|
|
1674
|
+
/**
|
|
1675
|
+
* 正規化済みのコメント可視集合。`null` は無制限、空集合は 0 件 (fail-close)。
|
|
1676
|
+
*
|
|
1677
|
+
* 常に `visible` の部分集合である (構築時に積集合を取っているため)。
|
|
1678
|
+
*/
|
|
1679
|
+
get commentable(): VisibleSourceTypeSet;
|
|
1630
1680
|
/**
|
|
1631
1681
|
* Builds a scope restricted to the given source types (empty input = zero rows).
|
|
1632
1682
|
* 指定した sourceType へ限定した閲覧範囲を生成する処理 (空入力は 0 件)。
|
|
1633
1683
|
*
|
|
1634
1684
|
* 入力は {@link normalizeVisibleSourceTypes} を通るため、大小文字・前後空白・重複・非文字列は
|
|
1635
|
-
*
|
|
1685
|
+
* ここで吸収される。素の配列は `{ read: <配列> }` と同義で、コメント次元は `read` に従う。
|
|
1636
1686
|
*
|
|
1637
|
-
* ❗ 引数は `
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
1640
|
-
*
|
|
1687
|
+
* ❗ 引数は `null` / `undefined` を**受け付けない**。受け付けると「restrict」という名前のまま
|
|
1688
|
+
* 無制限スコープを生む第 2 の入口になり、「絞り込まれない読み取りは必ず
|
|
1689
|
+
* {@link DailyReportViewerScope.unrestricted} に現れる」という監査不変条件が崩れる。
|
|
1690
|
+
* 無制限は必ず理由付きで明示すること。
|
|
1641
1691
|
*
|
|
1642
|
-
*
|
|
1692
|
+
* ❗ 判別に `Array.isArray` を使ってはならない。型述語が `arg is any[]` であり
|
|
1693
|
+
* `readonly string[]` を負の分岐から除去できないため、`.read` 参照が TS2339 になる
|
|
1694
|
+
* (実測済み)。`"read" in input` で判別する。文字列など object でない契約違反入力は
|
|
1695
|
+
* `in` が実行時に throw するので、先に `typeof` で弾いてから
|
|
1696
|
+
* {@link normalizeVisibleSourceTypes} の fail-close 経路へ落とす。
|
|
1697
|
+
*
|
|
1698
|
+
* @param input Visible source-type tokens, or per-dimension sets. 可視 sourceType のトークン群、または次元別の集合。
|
|
1643
1699
|
* @returns The scope. 生成した閲覧範囲。
|
|
1644
1700
|
*/
|
|
1645
|
-
static restrictTo(
|
|
1701
|
+
static restrictTo(input: readonly string[] | DailyReportVisibilityDimensions): DailyReportViewerScope;
|
|
1646
1702
|
/**
|
|
1647
1703
|
* Builds an unrestricted scope; every call site is an audited fail-open.
|
|
1648
1704
|
* 無制限の閲覧範囲を生成する処理。全呼び出し箇所が監査対象の fail-open となる。
|
|
@@ -1650,17 +1706,29 @@ declare class DailyReportViewerScope {
|
|
|
1650
1706
|
* ❗ 理由文字列は必須。読み捨てるが、呼び出し箇所の全数列挙と「なぜここは絞らなくてよいか」の
|
|
1651
1707
|
* 記録を強制するために受け取る。
|
|
1652
1708
|
*
|
|
1709
|
+
* ❗ **両次元**を無制限にする。片方だけ残すと「無制限」という名前が嘘になり、監査 grep の
|
|
1710
|
+
* 結果が「絞られていない読み取りの全数」でなくなる。
|
|
1711
|
+
*
|
|
1653
1712
|
* @param auditReason Why unrestricted access is correct here. なぜここで無制限が正当かの理由。
|
|
1654
1713
|
* @returns The unrestricted scope. 無制限の閲覧範囲。
|
|
1655
1714
|
*/
|
|
1656
1715
|
static unrestricted(auditReason: string): DailyReportViewerScope;
|
|
1657
1716
|
}
|
|
1658
1717
|
/**
|
|
1659
|
-
* Computes the cache-key digest for a whole viewer scope.
|
|
1660
|
-
*
|
|
1718
|
+
* Computes the cache-key digest for a whole viewer scope (all dimensions).
|
|
1719
|
+
* 閲覧範囲全体 (全次元) からキャッシュキー用ダイジェストを算出する処理。
|
|
1661
1720
|
*
|
|
1662
1721
|
* スコープに次元が増えてもキャッシュキーの生成点を 1 つに保つための入口。
|
|
1663
1722
|
*
|
|
1723
|
+
* ❗ **必ず全次元を畳み込むこと。** 本文次元だけを畳み込むと、コメント権限だけを剥奪された
|
|
1724
|
+
* 同一ユーザーが剥奪前のキャッシュバケットを共有し続け、TTL の間コメント本文入りの詳細を
|
|
1725
|
+
* 受け取り続ける (キーが変わらないため無効化も走らない)。
|
|
1726
|
+
*
|
|
1727
|
+
* 両次元が一致する既定形 (コメント次元が `read` に従う) では**本文のみのダイジェストと同一値**を
|
|
1728
|
+
* 返す。既存配備のキャッシュキーは 1 文字も変わらず、キャッシュ再利用率は無退行となる。
|
|
1729
|
+
* 次元が分かれるときだけ `d:` 名前空間の下へ**長さ接頭辞付きで連結**するため、写像は単射であり
|
|
1730
|
+
* sentinel (`all` / `none`) や単一次元表記 (`s:` / `sh:`) と衝突しない。
|
|
1731
|
+
*
|
|
1664
1732
|
* @param scope Viewer scope. 閲覧範囲。
|
|
1665
1733
|
* @returns Digest usable as a cache-key component. キャッシュキー構成要素として使えるダイジェスト。
|
|
1666
1734
|
*/
|
|
@@ -2254,4 +2322,4 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2254
2322
|
knownSourceTypes: readonly string[];
|
|
2255
2323
|
};
|
|
2256
2324
|
|
|
2257
|
-
export { type AuthzResourceItem, type DailyReportAttachmentBytes, type DailyReportAttachmentError, type DailyReportAttachmentFailure, type DailyReportAttachmentTable, type DailyReportAuthResult, type DailyReportAuthenticate, type DailyReportCommentRow, type DailyReportCommentTable, type DailyReportDb, type DailyReportEncodeUserId, type DailyReportExternalSource, type DailyReportHandlersConfig, type DailyReportHubLabelTable, type DailyReportHubRow, type DailyReportHubTable, type DailyReportIdCodec, type DailyReportInternalRow, type DailyReportInternalTable, type DailyReportLabelTable, type DailyReportReadAttachment, type DailyReportRedisBlockingClient, type DailyReportRedisClient, type DailyReportRedisProvider, type DailyReportResolveUserId, type DailyReportResolveVisibleSourceTypes, type DailyReportServerConfig, type DailyReportService, type DailyReportServiceConfig, type DailyReportSourceTypeInput, DailyReportSseReader, type DailyReportSseReaderConfig, type DailyReportTables, type DailyReportUserStatusRow, type DailyReportUserStatusTable, type DailyReportUserTable, DailyReportViewerScope, type DailyReportVisibilityResolveContext, type DailyReportVisibilityResolveReason, type DailyReportVisibleSourceTypes, type EpochStore, type ExternalReportFields, type RedisStreamMessage, SqlResultCache, type SqlResultCacheQueryOptions, type StreamEntry, type VisibleSourceTypeSet, asciiFallbackFileName, buildVisibleSourceTypeCondition, computeSourceTypeDigest, computeViewerScopeDigest, createDailyReportHandlers, createDailyReportServer, createDailyReportService, createEpochStore, defineDailyReportAuthzResources, defineDailyReportSchema, deniesEverySourceType, encodeRfc8187, formatDateValue, generateETag, isSourceTypeVisible, isStreamIdLte, jsonResponseWithETag, normalizeVisibleSourceTypes, sanitizeMediaType, seedDailyReportAuthzResources, transformJsonArray };
|
|
2325
|
+
export { type AuthzResourceItem, type DailyReportAttachmentBytes, type DailyReportAttachmentError, type DailyReportAttachmentFailure, type DailyReportAttachmentTable, type DailyReportAuthResult, type DailyReportAuthenticate, type DailyReportCommentRow, type DailyReportCommentTable, type DailyReportDb, type DailyReportEncodeUserId, type DailyReportExternalSource, type DailyReportHandlersConfig, type DailyReportHubLabelTable, type DailyReportHubRow, type DailyReportHubTable, type DailyReportIdCodec, type DailyReportInternalRow, type DailyReportInternalTable, type DailyReportLabelTable, type DailyReportReadAttachment, type DailyReportRedisBlockingClient, type DailyReportRedisClient, type DailyReportRedisProvider, type DailyReportResolveUserId, type DailyReportResolveVisibleSourceTypes, type DailyReportServerConfig, type DailyReportService, type DailyReportServiceConfig, type DailyReportSourceTypeInput, DailyReportSseReader, type DailyReportSseReaderConfig, type DailyReportTables, type DailyReportUserStatusRow, type DailyReportUserStatusTable, type DailyReportUserTable, DailyReportViewerScope, type DailyReportVisibilityDimensions, type DailyReportVisibilityResolveContext, type DailyReportVisibilityResolveReason, type DailyReportVisibleSourceTypes, type EpochStore, type ExternalReportFields, type RedisStreamMessage, SqlResultCache, type SqlResultCacheQueryOptions, type StreamEntry, type VisibleSourceTypeSet, asciiFallbackFileName, buildVisibleSourceTypeCondition, computeSourceTypeDigest, computeViewerScopeDigest, createDailyReportHandlers, createDailyReportServer, createDailyReportService, createEpochStore, defineDailyReportAuthzResources, defineDailyReportSchema, deniesEverySourceType, encodeRfc8187, formatDateValue, generateETag, isSourceTypeVisible, isStreamIdLte, jsonResponseWithETag, normalizeVisibleSourceTypes, sanitizeMediaType, seedDailyReportAuthzResources, transformJsonArray };
|
package/dist/server.d.ts
CHANGED
|
@@ -73,16 +73,42 @@ type DailyReportAuthenticate = (request: Request, options: {
|
|
|
73
73
|
} | {
|
|
74
74
|
failureRedirect: null;
|
|
75
75
|
}) => Promise<DailyReportAuthResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Per-dimension visible source types (report body vs. comments).
|
|
78
|
+
* 次元ごとの可視 sourceType (本文とコメント)。
|
|
79
|
+
*
|
|
80
|
+
* コメントの閲覧・記入を本文の可視性から独立して絞るための形。
|
|
81
|
+
*
|
|
82
|
+
* - `read`: 本文 (日報行) を閲覧してよい sourceType。**空配列は 0 件**。
|
|
83
|
+
* - `comment` **省略**: コメント次元は `read` に従う (単一次元と同義・現行互換)。
|
|
84
|
+
* - `comment: []`: コメント 0 件かつ記入不可。
|
|
85
|
+
*
|
|
86
|
+
* ❗ `comment` に `null` は受け付けない。「`read` に従う」の綴りを 2 つ作ると、
|
|
87
|
+
* ポート全体の nullish (= 無制限) と次元レベルの nullish が混ざって 4 状態になり、
|
|
88
|
+
* 本ファイルが一貫して避けている「無制限と全拒否の取り違え」を次元単位で再発させる。
|
|
89
|
+
*
|
|
90
|
+
* ❗ `read` の外側にあるコメントトークンは意味を持たない (コメントは必ず read で絞られた
|
|
91
|
+
* 詳細の内側にしか載らない)。`DailyReportViewerScope.restrictTo` が構築時に
|
|
92
|
+
* `read` との積集合へ狭めるため、設定ミスは黙って安全側へ倒れる。
|
|
93
|
+
*/
|
|
94
|
+
type DailyReportVisibilityDimensions = {
|
|
95
|
+
/** 本文を閲覧してよい sourceType。空配列は 0 件。 */
|
|
96
|
+
read: readonly string[];
|
|
97
|
+
/** コメントを閲覧・記入してよい sourceType。省略時は `read` に従う。空配列は 0 件かつ記入不可。 */
|
|
98
|
+
comment?: readonly string[];
|
|
99
|
+
};
|
|
76
100
|
/**
|
|
77
101
|
* Raw return of the visible-source-type resolver.
|
|
78
102
|
* 可視 sourceType リゾルバの生戻り値。
|
|
79
103
|
*
|
|
80
104
|
* - `undefined` / `null`: 無制限 (全件)。ポート未注入時と同じ既定の意味。
|
|
81
|
-
* - `string[]`:
|
|
105
|
+
* - `string[]`: 本文・コメントの双方をその集合に限定。要素は `DailyReportHub.sourceType` の値であり、
|
|
106
|
+
* **空配列は 0 件**。`{ read: <配列> }` と完全に同義。
|
|
107
|
+
* - `{ read, comment? }`: 次元ごとに指定する ({@link DailyReportVisibilityDimensions})。
|
|
82
108
|
*
|
|
83
|
-
* 中立契約: いかなる認可パッケージの型 (`PermissionView` 等)
|
|
109
|
+
* 中立契約: いかなる認可パッケージの型 (`PermissionView` 等) にも依存しない素の文字列と配列のみ。
|
|
84
110
|
*/
|
|
85
|
-
type DailyReportVisibleSourceTypes = readonly string[] | null | undefined;
|
|
111
|
+
type DailyReportVisibleSourceTypes = readonly string[] | DailyReportVisibilityDimensions | null | undefined;
|
|
86
112
|
/**
|
|
87
113
|
* Why the visible-source-type resolver is being called.
|
|
88
114
|
* 可視 sourceType リゾルバが呼ばれた理由。
|
|
@@ -1547,6 +1573,15 @@ declare function defineDailyReportSchema<S extends string>(schemaName: S, opts:
|
|
|
1547
1573
|
*
|
|
1548
1574
|
* この 2 状態の取り違えは情報漏洩か機能停止に直結するため、判定・畳み込み・キー生成を
|
|
1549
1575
|
* 本ファイルへ一元化し、ハンドラ層とサービス層の双方がここだけを参照する。
|
|
1576
|
+
*
|
|
1577
|
+
* 閲覧範囲は**次元ごと**にこの 2 状態を持つ:
|
|
1578
|
+
*
|
|
1579
|
+
* - `visible` = 本文 (日報行) を閲覧してよい区分。
|
|
1580
|
+
* - `commentable` = コメントを閲覧・記入してよい区分。常に `visible` の部分集合。
|
|
1581
|
+
*
|
|
1582
|
+
* 次元が増えても状態数は増やさない。「もう一方の次元に従う」は
|
|
1583
|
+
* {@link DailyReportViewerScope.restrictTo} が**構築時に解決**するため、各次元は最後まで
|
|
1584
|
+
* 厳密に 2 状態であり、本ファイルの判定ヘルパーは次元を問わずそのまま適用できる。
|
|
1550
1585
|
*/
|
|
1551
1586
|
|
|
1552
1587
|
/**
|
|
@@ -1562,10 +1597,19 @@ type VisibleSourceTypeSet = ReadonlySet<string> | null;
|
|
|
1562
1597
|
* `Set` 化する。**空配列は空 `Set` (0 件) を返し、`null` (無制限) へは決して倒さない** —
|
|
1563
1598
|
* この取り違えが情報漏洩か機能停止に直結するため。
|
|
1564
1599
|
*
|
|
1565
|
-
*
|
|
1600
|
+
* ❗ 引数は**単一次元のトークン配列に限る**。次元別オブジェクト
|
|
1601
|
+
* ({@link DailyReportVisibilityDimensions}) をここへ渡すと配列ガードに掛かって全拒否になるため、
|
|
1602
|
+
* 次元の解体は {@link DailyReportViewerScope.restrictTo} が行い、ここには常に配列だけが届く。
|
|
1603
|
+
*
|
|
1604
|
+
* ❗ オーバーロードで「**配列を渡した場合は決して `null` (無制限) を返さない**」という不変条件を
|
|
1605
|
+
* 型に載せている。これは実装の偶然ではなく本ファイルの中核契約 (空配列は 0 件であって無制限では
|
|
1606
|
+
* ない) そのものであり、呼び出し側が `null` 分岐を書いて無制限へ倒す余地を型で塞ぐ。
|
|
1607
|
+
*
|
|
1608
|
+
* @param raw Raw single-dimension resolver value. 単一次元の生値。
|
|
1566
1609
|
* @returns Normalized visible set. 正規化済みの可視集合。
|
|
1567
1610
|
*/
|
|
1568
|
-
declare function normalizeVisibleSourceTypes(raw:
|
|
1611
|
+
declare function normalizeVisibleSourceTypes(raw: readonly string[]): ReadonlySet<string>;
|
|
1612
|
+
declare function normalizeVisibleSourceTypes(raw: readonly string[] | null | undefined): VisibleSourceTypeSet;
|
|
1569
1613
|
/**
|
|
1570
1614
|
* Tests whether a report's sourceType is visible under the given set.
|
|
1571
1615
|
* ある日報の sourceType が可視集合に含まれるかの三値判定。
|
|
@@ -1625,24 +1669,36 @@ declare function buildVisibleSourceTypeCondition(sourceTypeColumn: AnyMsSqlColum
|
|
|
1625
1669
|
declare class DailyReportViewerScope {
|
|
1626
1670
|
#private;
|
|
1627
1671
|
private constructor();
|
|
1628
|
-
/**
|
|
1672
|
+
/** 正規化済みの本文可視集合。`null` は無制限、空集合は 0 件 (fail-close)。 */
|
|
1629
1673
|
get visible(): VisibleSourceTypeSet;
|
|
1674
|
+
/**
|
|
1675
|
+
* 正規化済みのコメント可視集合。`null` は無制限、空集合は 0 件 (fail-close)。
|
|
1676
|
+
*
|
|
1677
|
+
* 常に `visible` の部分集合である (構築時に積集合を取っているため)。
|
|
1678
|
+
*/
|
|
1679
|
+
get commentable(): VisibleSourceTypeSet;
|
|
1630
1680
|
/**
|
|
1631
1681
|
* Builds a scope restricted to the given source types (empty input = zero rows).
|
|
1632
1682
|
* 指定した sourceType へ限定した閲覧範囲を生成する処理 (空入力は 0 件)。
|
|
1633
1683
|
*
|
|
1634
1684
|
* 入力は {@link normalizeVisibleSourceTypes} を通るため、大小文字・前後空白・重複・非文字列は
|
|
1635
|
-
*
|
|
1685
|
+
* ここで吸収される。素の配列は `{ read: <配列> }` と同義で、コメント次元は `read` に従う。
|
|
1636
1686
|
*
|
|
1637
|
-
* ❗ 引数は `
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
1640
|
-
*
|
|
1687
|
+
* ❗ 引数は `null` / `undefined` を**受け付けない**。受け付けると「restrict」という名前のまま
|
|
1688
|
+
* 無制限スコープを生む第 2 の入口になり、「絞り込まれない読み取りは必ず
|
|
1689
|
+
* {@link DailyReportViewerScope.unrestricted} に現れる」という監査不変条件が崩れる。
|
|
1690
|
+
* 無制限は必ず理由付きで明示すること。
|
|
1641
1691
|
*
|
|
1642
|
-
*
|
|
1692
|
+
* ❗ 判別に `Array.isArray` を使ってはならない。型述語が `arg is any[]` であり
|
|
1693
|
+
* `readonly string[]` を負の分岐から除去できないため、`.read` 参照が TS2339 になる
|
|
1694
|
+
* (実測済み)。`"read" in input` で判別する。文字列など object でない契約違反入力は
|
|
1695
|
+
* `in` が実行時に throw するので、先に `typeof` で弾いてから
|
|
1696
|
+
* {@link normalizeVisibleSourceTypes} の fail-close 経路へ落とす。
|
|
1697
|
+
*
|
|
1698
|
+
* @param input Visible source-type tokens, or per-dimension sets. 可視 sourceType のトークン群、または次元別の集合。
|
|
1643
1699
|
* @returns The scope. 生成した閲覧範囲。
|
|
1644
1700
|
*/
|
|
1645
|
-
static restrictTo(
|
|
1701
|
+
static restrictTo(input: readonly string[] | DailyReportVisibilityDimensions): DailyReportViewerScope;
|
|
1646
1702
|
/**
|
|
1647
1703
|
* Builds an unrestricted scope; every call site is an audited fail-open.
|
|
1648
1704
|
* 無制限の閲覧範囲を生成する処理。全呼び出し箇所が監査対象の fail-open となる。
|
|
@@ -1650,17 +1706,29 @@ declare class DailyReportViewerScope {
|
|
|
1650
1706
|
* ❗ 理由文字列は必須。読み捨てるが、呼び出し箇所の全数列挙と「なぜここは絞らなくてよいか」の
|
|
1651
1707
|
* 記録を強制するために受け取る。
|
|
1652
1708
|
*
|
|
1709
|
+
* ❗ **両次元**を無制限にする。片方だけ残すと「無制限」という名前が嘘になり、監査 grep の
|
|
1710
|
+
* 結果が「絞られていない読み取りの全数」でなくなる。
|
|
1711
|
+
*
|
|
1653
1712
|
* @param auditReason Why unrestricted access is correct here. なぜここで無制限が正当かの理由。
|
|
1654
1713
|
* @returns The unrestricted scope. 無制限の閲覧範囲。
|
|
1655
1714
|
*/
|
|
1656
1715
|
static unrestricted(auditReason: string): DailyReportViewerScope;
|
|
1657
1716
|
}
|
|
1658
1717
|
/**
|
|
1659
|
-
* Computes the cache-key digest for a whole viewer scope.
|
|
1660
|
-
*
|
|
1718
|
+
* Computes the cache-key digest for a whole viewer scope (all dimensions).
|
|
1719
|
+
* 閲覧範囲全体 (全次元) からキャッシュキー用ダイジェストを算出する処理。
|
|
1661
1720
|
*
|
|
1662
1721
|
* スコープに次元が増えてもキャッシュキーの生成点を 1 つに保つための入口。
|
|
1663
1722
|
*
|
|
1723
|
+
* ❗ **必ず全次元を畳み込むこと。** 本文次元だけを畳み込むと、コメント権限だけを剥奪された
|
|
1724
|
+
* 同一ユーザーが剥奪前のキャッシュバケットを共有し続け、TTL の間コメント本文入りの詳細を
|
|
1725
|
+
* 受け取り続ける (キーが変わらないため無効化も走らない)。
|
|
1726
|
+
*
|
|
1727
|
+
* 両次元が一致する既定形 (コメント次元が `read` に従う) では**本文のみのダイジェストと同一値**を
|
|
1728
|
+
* 返す。既存配備のキャッシュキーは 1 文字も変わらず、キャッシュ再利用率は無退行となる。
|
|
1729
|
+
* 次元が分かれるときだけ `d:` 名前空間の下へ**長さ接頭辞付きで連結**するため、写像は単射であり
|
|
1730
|
+
* sentinel (`all` / `none`) や単一次元表記 (`s:` / `sh:`) と衝突しない。
|
|
1731
|
+
*
|
|
1664
1732
|
* @param scope Viewer scope. 閲覧範囲。
|
|
1665
1733
|
* @returns Digest usable as a cache-key component. キャッシュキー構成要素として使えるダイジェスト。
|
|
1666
1734
|
*/
|
|
@@ -2254,4 +2322,4 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2254
2322
|
knownSourceTypes: readonly string[];
|
|
2255
2323
|
};
|
|
2256
2324
|
|
|
2257
|
-
export { type AuthzResourceItem, type DailyReportAttachmentBytes, type DailyReportAttachmentError, type DailyReportAttachmentFailure, type DailyReportAttachmentTable, type DailyReportAuthResult, type DailyReportAuthenticate, type DailyReportCommentRow, type DailyReportCommentTable, type DailyReportDb, type DailyReportEncodeUserId, type DailyReportExternalSource, type DailyReportHandlersConfig, type DailyReportHubLabelTable, type DailyReportHubRow, type DailyReportHubTable, type DailyReportIdCodec, type DailyReportInternalRow, type DailyReportInternalTable, type DailyReportLabelTable, type DailyReportReadAttachment, type DailyReportRedisBlockingClient, type DailyReportRedisClient, type DailyReportRedisProvider, type DailyReportResolveUserId, type DailyReportResolveVisibleSourceTypes, type DailyReportServerConfig, type DailyReportService, type DailyReportServiceConfig, type DailyReportSourceTypeInput, DailyReportSseReader, type DailyReportSseReaderConfig, type DailyReportTables, type DailyReportUserStatusRow, type DailyReportUserStatusTable, type DailyReportUserTable, DailyReportViewerScope, type DailyReportVisibilityResolveContext, type DailyReportVisibilityResolveReason, type DailyReportVisibleSourceTypes, type EpochStore, type ExternalReportFields, type RedisStreamMessage, SqlResultCache, type SqlResultCacheQueryOptions, type StreamEntry, type VisibleSourceTypeSet, asciiFallbackFileName, buildVisibleSourceTypeCondition, computeSourceTypeDigest, computeViewerScopeDigest, createDailyReportHandlers, createDailyReportServer, createDailyReportService, createEpochStore, defineDailyReportAuthzResources, defineDailyReportSchema, deniesEverySourceType, encodeRfc8187, formatDateValue, generateETag, isSourceTypeVisible, isStreamIdLte, jsonResponseWithETag, normalizeVisibleSourceTypes, sanitizeMediaType, seedDailyReportAuthzResources, transformJsonArray };
|
|
2325
|
+
export { type AuthzResourceItem, type DailyReportAttachmentBytes, type DailyReportAttachmentError, type DailyReportAttachmentFailure, type DailyReportAttachmentTable, type DailyReportAuthResult, type DailyReportAuthenticate, type DailyReportCommentRow, type DailyReportCommentTable, type DailyReportDb, type DailyReportEncodeUserId, type DailyReportExternalSource, type DailyReportHandlersConfig, type DailyReportHubLabelTable, type DailyReportHubRow, type DailyReportHubTable, type DailyReportIdCodec, type DailyReportInternalRow, type DailyReportInternalTable, type DailyReportLabelTable, type DailyReportReadAttachment, type DailyReportRedisBlockingClient, type DailyReportRedisClient, type DailyReportRedisProvider, type DailyReportResolveUserId, type DailyReportResolveVisibleSourceTypes, type DailyReportServerConfig, type DailyReportService, type DailyReportServiceConfig, type DailyReportSourceTypeInput, DailyReportSseReader, type DailyReportSseReaderConfig, type DailyReportTables, type DailyReportUserStatusRow, type DailyReportUserStatusTable, type DailyReportUserTable, DailyReportViewerScope, type DailyReportVisibilityDimensions, type DailyReportVisibilityResolveContext, type DailyReportVisibilityResolveReason, type DailyReportVisibleSourceTypes, type EpochStore, type ExternalReportFields, type RedisStreamMessage, SqlResultCache, type SqlResultCacheQueryOptions, type StreamEntry, type VisibleSourceTypeSet, asciiFallbackFileName, buildVisibleSourceTypeCondition, computeSourceTypeDigest, computeViewerScopeDigest, createDailyReportHandlers, createDailyReportServer, createDailyReportService, createEpochStore, defineDailyReportAuthzResources, defineDailyReportSchema, deniesEverySourceType, encodeRfc8187, formatDateValue, generateETag, isSourceTypeVisible, isStreamIdLte, jsonResponseWithETag, normalizeVisibleSourceTypes, sanitizeMediaType, seedDailyReportAuthzResources, transformJsonArray };
|