@aiquants/daily-report 0.13.0 → 0.14.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/README.md +3 -0
- package/dist/server.d.mts +91 -30
- package/dist/server.d.ts +91 -30
- 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/dist/styles/daily-report.standalone.css +1 -1
- package/package.json +7 -5
- package/src/server/handlers.ids-stream.spec.ts +4 -2
- package/src/server/handlers.ts +51 -29
- package/src/server/handlers.visibility.spec.ts +158 -0
- package/src/server/service.spec.ts +156 -62
- package/src/server/service.ts +72 -59
- package/src/server/test-helpers/handlers-config.ts +39 -20
- package/src/server/visibility.spec.ts +129 -3
- package/src/server/visibility.ts +106 -11
package/README.md
CHANGED
|
@@ -151,6 +151,9 @@ createDailyReportServer({
|
|
|
151
151
|
- **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.
|
|
152
152
|
- **Matching** is case-insensitive on both the JS and SQL sides (both fold to lower case), so it does not depend on the database collation. Surrounding whitespace is trimmed from the *tokens you return* but never from the stored column value — the two sides fold identically, and a padded stored value is simply invisible (fail-closed) rather than visible on one path and hidden on the other.
|
|
153
153
|
- **Where it applies**: the SQL predicate is injected in the service layer, so it holds for every delivery path (including attachment bytes) rather than only the HTTP handlers. It also gates the mutations that act on someone else's report (`addComment`, `deleteComment`, star, read); a hidden report is reported as `404`, never `403`.
|
|
154
|
+
- **How it reaches the service**: the resolved set is wrapped in a `DailyReportViewerScope` and passed as the **first** argument of every gated service method.
|
|
155
|
+
The class is nominal (it holds a private field), so `{ visible: null }`, `{ ...scope, visible: null }` and `new DailyReportViewerScope(...)` are all type errors — the only ways to obtain one are `DailyReportViewerScope.restrictTo(tokens)` — which accepts `readonly string[]` only, so it cannot be handed a nullish value and quietly widen — and `DailyReportViewerScope.unrestricted(auditReason)`.
|
|
156
|
+
Because a bare `null` is not spellable as a visibility value, `grep -rn "DailyReportViewerScope.unrestricted("` enumerates every unfiltered read in a codebase, each carrying a written reason.
|
|
154
157
|
- **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.
|
|
155
158
|
- **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.
|
|
156
159
|
|
package/dist/server.d.mts
CHANGED
|
@@ -1610,14 +1610,75 @@ declare function deniesEverySourceType(set: VisibleSourceTypeSet): boolean;
|
|
|
1610
1610
|
*/
|
|
1611
1611
|
declare function buildVisibleSourceTypeCondition(sourceTypeColumn: AnyMsSqlColumn, set: VisibleSourceTypeSet): SQL | undefined;
|
|
1612
1612
|
/**
|
|
1613
|
-
*
|
|
1614
|
-
*
|
|
1613
|
+
* Request-scoped viewer scope carrying the normalized visible source-type set.
|
|
1614
|
+
* 正規化済みの可視 sourceType 集合を運ぶ、リクエストスコープの閲覧範囲。
|
|
1615
1615
|
*
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1618
|
-
* (
|
|
1619
|
-
*
|
|
1620
|
-
*
|
|
1616
|
+
* このクラスは private フィールドを持つことで**名目型**になっており、`{ visible: null }` のような
|
|
1617
|
+
* 構造的偽造も、`{ ...scope, visible: null }` のようなスプレッドによる widening も型が拒否する
|
|
1618
|
+
* (`unique symbol` ブランドでは後者を防げないことを実測で確認済み)。
|
|
1619
|
+
*
|
|
1620
|
+
* 生成経路は {@link DailyReportViewerScope.restrictTo} と {@link DailyReportViewerScope.unrestricted}
|
|
1621
|
+
* の 2 つだけであり、`null` を「可視性」としてそのまま綴ることはできない。結果として、
|
|
1622
|
+
* **絞り込まれない読み取りは必ず `unrestricted(理由)` として現れる**ため、
|
|
1623
|
+
* `grep -rn "DailyReportViewerScope.unrestricted("` が fail-open の全数リストになる。
|
|
1624
|
+
*/
|
|
1625
|
+
declare class DailyReportViewerScope {
|
|
1626
|
+
#private;
|
|
1627
|
+
private constructor();
|
|
1628
|
+
/** 正規化済みの可視集合。`null` は無制限、空集合は 0 件 (fail-close)。 */
|
|
1629
|
+
get visible(): VisibleSourceTypeSet;
|
|
1630
|
+
/**
|
|
1631
|
+
* Builds a scope restricted to the given source types (empty input = zero rows).
|
|
1632
|
+
* 指定した sourceType へ限定した閲覧範囲を生成する処理 (空入力は 0 件)。
|
|
1633
|
+
*
|
|
1634
|
+
* 入力は {@link normalizeVisibleSourceTypes} を通るため、大小文字・前後空白・重複・非文字列は
|
|
1635
|
+
* ここで吸収される。
|
|
1636
|
+
*
|
|
1637
|
+
* ❗ 引数は `readonly string[]` に**狭めてある**。`null` / `undefined` を受け付けると、
|
|
1638
|
+
* 「restrict」という名前のまま無制限スコープを生む第 2 の入口になり、
|
|
1639
|
+
* 「絞り込まれない読み取りは必ず {@link DailyReportViewerScope.unrestricted} に現れる」
|
|
1640
|
+
* という監査不変条件が崩れる。無制限は必ず理由付きで明示すること。
|
|
1641
|
+
*
|
|
1642
|
+
* @param sourceTypes Visible source-type tokens. 可視 sourceType のトークン群。
|
|
1643
|
+
* @returns The scope. 生成した閲覧範囲。
|
|
1644
|
+
*/
|
|
1645
|
+
static restrictTo(sourceTypes: readonly string[]): DailyReportViewerScope;
|
|
1646
|
+
/**
|
|
1647
|
+
* Builds an unrestricted scope; every call site is an audited fail-open.
|
|
1648
|
+
* 無制限の閲覧範囲を生成する処理。全呼び出し箇所が監査対象の fail-open となる。
|
|
1649
|
+
*
|
|
1650
|
+
* ❗ 理由文字列は必須。読み捨てるが、呼び出し箇所の全数列挙と「なぜここは絞らなくてよいか」の
|
|
1651
|
+
* 記録を強制するために受け取る。
|
|
1652
|
+
*
|
|
1653
|
+
* @param auditReason Why unrestricted access is correct here. なぜここで無制限が正当かの理由。
|
|
1654
|
+
* @returns The unrestricted scope. 無制限の閲覧範囲。
|
|
1655
|
+
*/
|
|
1656
|
+
static unrestricted(auditReason: string): DailyReportViewerScope;
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Computes the cache-key digest for a whole viewer scope.
|
|
1660
|
+
* 閲覧範囲全体からキャッシュキー用ダイジェストを算出する処理。
|
|
1661
|
+
*
|
|
1662
|
+
* スコープに次元が増えてもキャッシュキーの生成点を 1 つに保つための入口。
|
|
1663
|
+
*
|
|
1664
|
+
* @param scope Viewer scope. 閲覧範囲。
|
|
1665
|
+
* @returns Digest usable as a cache-key component. キャッシュキー構成要素として使えるダイジェスト。
|
|
1666
|
+
*/
|
|
1667
|
+
declare function computeViewerScopeDigest(scope: DailyReportViewerScope): string;
|
|
1668
|
+
/**
|
|
1669
|
+
* Computes a stable and bounded cache-key digest of the set.
|
|
1670
|
+
* 可視集合から安定・有界なキャッシュキー用ダイジェストを算出する処理。
|
|
1671
|
+
*
|
|
1672
|
+
* 無制限は `"all"`、空集合は `"none"`、それ以外は `s:` 名前空間の下へ**長さ接頭辞付きトークンを昇順連結**する。
|
|
1673
|
+
* 連結は長さ接頭辞付きなので**単射**であり、通常経路 (`s:`) では論理的に等価な集合が必ず同一の、
|
|
1674
|
+
* 異なる集合が必ず別のダイジェストになる。sentinel (`all` / `none`) とも名前空間が分かれるため、
|
|
1675
|
+
* 限定集合が無制限 / 全拒否と同じバケットを共有することはない。権限が安定している間はキーが
|
|
1676
|
+
* 不変になるため、キャッシュは完全に再利用される (性能無退行)。
|
|
1677
|
+
*
|
|
1678
|
+
* ❗ 区分数が異常に多い場合だけキー長を抑えるため FNV-1a 32bit へ退避し `sh:` 接頭辞を付ける。
|
|
1679
|
+
* **この退避経路に限り理論上の衝突がありうる** (32bit)。衝突しても影響は同一ユーザーの
|
|
1680
|
+
* キャッシュ再利用に留まり、認可判定そのものは SQL 述語が担うため漏洩には至らない。
|
|
1681
|
+
* 通常の区分数 (数個) では到達しない。
|
|
1621
1682
|
*
|
|
1622
1683
|
* @param set Normalized visible set. 正規化済みの可視集合。
|
|
1623
1684
|
* @returns Digest usable as a cache-key component. キャッシュキー構成要素として使えるダイジェスト。
|
|
@@ -1782,15 +1843,15 @@ declare function createDailyReportService(config: DailyReportServiceConfig): {
|
|
|
1782
1843
|
clearCache: () => Promise<void>;
|
|
1783
1844
|
invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
|
|
1784
1845
|
getUserIdByExternalId: (externalId: string) => Promise<number | null>;
|
|
1785
|
-
getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
1786
|
-
getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number
|
|
1787
|
-
getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
1788
|
-
getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
1789
|
-
getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }
|
|
1790
|
-
getDailyReportDetailByIdByExternalId: (reportHubId: number, externalId: string, options
|
|
1846
|
+
getDailyReportIdsByExternalId: (scope: DailyReportViewerScope, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
|
|
1847
|
+
getDailyReportIdsFirstPageByExternalId: (scope: DailyReportViewerScope, externalId: string, limit: number) => Promise<DailyReportItem[]>;
|
|
1848
|
+
getDailyReportsByBusinessDate: (scope: DailyReportViewerScope, businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
1849
|
+
getDailyReportsByBusinessDateByExternalId: (scope: DailyReportViewerScope, businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
1850
|
+
getDailyReportDetailById: (scope: DailyReportViewerScope, reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
1851
|
+
getDailyReportDetailByIdByExternalId: (scope: DailyReportViewerScope, reportHubId: number, externalId: string, options?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
1791
1852
|
getDraftLabelId: () => Promise<number | null>;
|
|
1792
1853
|
getAttachmentsByHubIds: (hubIds: number[]) => Promise<Map<number, DailyReportAttachmentSummary[]>>;
|
|
1793
|
-
getAttachmentForUser: (
|
|
1854
|
+
getAttachmentForUser: (scope: DailyReportViewerScope, attachmentId: number, userId: number) => Promise<{
|
|
1794
1855
|
fileName: string;
|
|
1795
1856
|
filePath: string;
|
|
1796
1857
|
fileType: string | null;
|
|
@@ -1798,10 +1859,10 @@ declare function createDailyReportService(config: DailyReportServiceConfig): {
|
|
|
1798
1859
|
} | null>;
|
|
1799
1860
|
markAttachmentMissing: (attachmentId: number) => Promise<void>;
|
|
1800
1861
|
markAttachmentPresent: (attachmentId: number) => Promise<void>;
|
|
1801
|
-
setStarStatus: (userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string
|
|
1802
|
-
setReadStatus: (userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string
|
|
1803
|
-
addComment: (userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string
|
|
1804
|
-
deleteComment: (userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string
|
|
1862
|
+
setStarStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
1863
|
+
setReadStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
1864
|
+
addComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string) => Promise<DailyReportCommentItem>;
|
|
1865
|
+
deleteComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string) => Promise<void>;
|
|
1805
1866
|
createDailyReport: (userId: number, businessDate: string, clientTempId: string) => Promise<DailyReportDetail>;
|
|
1806
1867
|
updateDailyReport: (reportHubId: number, userId: number, data: {
|
|
1807
1868
|
title?: string;
|
|
@@ -2141,15 +2202,15 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2141
2202
|
clearCache: () => Promise<void>;
|
|
2142
2203
|
invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
|
|
2143
2204
|
getUserIdByExternalId: (externalId: string) => Promise<number | null>;
|
|
2144
|
-
getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
2145
|
-
getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number
|
|
2146
|
-
getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
2147
|
-
getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
2148
|
-
getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }
|
|
2149
|
-
getDailyReportDetailByIdByExternalId: (reportHubId: number, externalId: string, options
|
|
2205
|
+
getDailyReportIdsByExternalId: (scope: DailyReportViewerScope, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
|
|
2206
|
+
getDailyReportIdsFirstPageByExternalId: (scope: DailyReportViewerScope, externalId: string, limit: number) => Promise<DailyReportItem[]>;
|
|
2207
|
+
getDailyReportsByBusinessDate: (scope: DailyReportViewerScope, businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
2208
|
+
getDailyReportsByBusinessDateByExternalId: (scope: DailyReportViewerScope, businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
2209
|
+
getDailyReportDetailById: (scope: DailyReportViewerScope, reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
2210
|
+
getDailyReportDetailByIdByExternalId: (scope: DailyReportViewerScope, reportHubId: number, externalId: string, options?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
2150
2211
|
getDraftLabelId: () => Promise<number | null>;
|
|
2151
2212
|
getAttachmentsByHubIds: (hubIds: number[]) => Promise<Map<number, DailyReportAttachmentSummary[]>>;
|
|
2152
|
-
getAttachmentForUser: (
|
|
2213
|
+
getAttachmentForUser: (scope: DailyReportViewerScope, attachmentId: number, userId: number) => Promise<{
|
|
2153
2214
|
fileName: string;
|
|
2154
2215
|
filePath: string;
|
|
2155
2216
|
fileType: string | null;
|
|
@@ -2157,10 +2218,10 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2157
2218
|
} | null>;
|
|
2158
2219
|
markAttachmentMissing: (attachmentId: number) => Promise<void>;
|
|
2159
2220
|
markAttachmentPresent: (attachmentId: number) => Promise<void>;
|
|
2160
|
-
setStarStatus: (userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string
|
|
2161
|
-
setReadStatus: (userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string
|
|
2162
|
-
addComment: (userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string
|
|
2163
|
-
deleteComment: (userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string
|
|
2221
|
+
setStarStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
2222
|
+
setReadStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
2223
|
+
addComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string) => Promise<DailyReportCommentItem>;
|
|
2224
|
+
deleteComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string) => Promise<void>;
|
|
2164
2225
|
createDailyReport: (userId: number, businessDate: string, clientTempId: string) => Promise<DailyReportDetail>;
|
|
2165
2226
|
updateDailyReport: (reportHubId: number, userId: number, data: {
|
|
2166
2227
|
title?: string;
|
|
@@ -2193,4 +2254,4 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2193
2254
|
knownSourceTypes: readonly string[];
|
|
2194
2255
|
};
|
|
2195
2256
|
|
|
2196
|
-
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, type DailyReportVisibilityResolveContext, type DailyReportVisibilityResolveReason, type DailyReportVisibleSourceTypes, type EpochStore, type ExternalReportFields, type RedisStreamMessage, SqlResultCache, type SqlResultCacheQueryOptions, type StreamEntry, type VisibleSourceTypeSet, asciiFallbackFileName, buildVisibleSourceTypeCondition, computeSourceTypeDigest, createDailyReportHandlers, createDailyReportServer, createDailyReportService, createEpochStore, defineDailyReportAuthzResources, defineDailyReportSchema, deniesEverySourceType, encodeRfc8187, formatDateValue, generateETag, isSourceTypeVisible, isStreamIdLte, jsonResponseWithETag, normalizeVisibleSourceTypes, sanitizeMediaType, seedDailyReportAuthzResources, transformJsonArray };
|
|
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 };
|
package/dist/server.d.ts
CHANGED
|
@@ -1610,14 +1610,75 @@ declare function deniesEverySourceType(set: VisibleSourceTypeSet): boolean;
|
|
|
1610
1610
|
*/
|
|
1611
1611
|
declare function buildVisibleSourceTypeCondition(sourceTypeColumn: AnyMsSqlColumn, set: VisibleSourceTypeSet): SQL | undefined;
|
|
1612
1612
|
/**
|
|
1613
|
-
*
|
|
1614
|
-
*
|
|
1613
|
+
* Request-scoped viewer scope carrying the normalized visible source-type set.
|
|
1614
|
+
* 正規化済みの可視 sourceType 集合を運ぶ、リクエストスコープの閲覧範囲。
|
|
1615
1615
|
*
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1618
|
-
* (
|
|
1619
|
-
*
|
|
1620
|
-
*
|
|
1616
|
+
* このクラスは private フィールドを持つことで**名目型**になっており、`{ visible: null }` のような
|
|
1617
|
+
* 構造的偽造も、`{ ...scope, visible: null }` のようなスプレッドによる widening も型が拒否する
|
|
1618
|
+
* (`unique symbol` ブランドでは後者を防げないことを実測で確認済み)。
|
|
1619
|
+
*
|
|
1620
|
+
* 生成経路は {@link DailyReportViewerScope.restrictTo} と {@link DailyReportViewerScope.unrestricted}
|
|
1621
|
+
* の 2 つだけであり、`null` を「可視性」としてそのまま綴ることはできない。結果として、
|
|
1622
|
+
* **絞り込まれない読み取りは必ず `unrestricted(理由)` として現れる**ため、
|
|
1623
|
+
* `grep -rn "DailyReportViewerScope.unrestricted("` が fail-open の全数リストになる。
|
|
1624
|
+
*/
|
|
1625
|
+
declare class DailyReportViewerScope {
|
|
1626
|
+
#private;
|
|
1627
|
+
private constructor();
|
|
1628
|
+
/** 正規化済みの可視集合。`null` は無制限、空集合は 0 件 (fail-close)。 */
|
|
1629
|
+
get visible(): VisibleSourceTypeSet;
|
|
1630
|
+
/**
|
|
1631
|
+
* Builds a scope restricted to the given source types (empty input = zero rows).
|
|
1632
|
+
* 指定した sourceType へ限定した閲覧範囲を生成する処理 (空入力は 0 件)。
|
|
1633
|
+
*
|
|
1634
|
+
* 入力は {@link normalizeVisibleSourceTypes} を通るため、大小文字・前後空白・重複・非文字列は
|
|
1635
|
+
* ここで吸収される。
|
|
1636
|
+
*
|
|
1637
|
+
* ❗ 引数は `readonly string[]` に**狭めてある**。`null` / `undefined` を受け付けると、
|
|
1638
|
+
* 「restrict」という名前のまま無制限スコープを生む第 2 の入口になり、
|
|
1639
|
+
* 「絞り込まれない読み取りは必ず {@link DailyReportViewerScope.unrestricted} に現れる」
|
|
1640
|
+
* という監査不変条件が崩れる。無制限は必ず理由付きで明示すること。
|
|
1641
|
+
*
|
|
1642
|
+
* @param sourceTypes Visible source-type tokens. 可視 sourceType のトークン群。
|
|
1643
|
+
* @returns The scope. 生成した閲覧範囲。
|
|
1644
|
+
*/
|
|
1645
|
+
static restrictTo(sourceTypes: readonly string[]): DailyReportViewerScope;
|
|
1646
|
+
/**
|
|
1647
|
+
* Builds an unrestricted scope; every call site is an audited fail-open.
|
|
1648
|
+
* 無制限の閲覧範囲を生成する処理。全呼び出し箇所が監査対象の fail-open となる。
|
|
1649
|
+
*
|
|
1650
|
+
* ❗ 理由文字列は必須。読み捨てるが、呼び出し箇所の全数列挙と「なぜここは絞らなくてよいか」の
|
|
1651
|
+
* 記録を強制するために受け取る。
|
|
1652
|
+
*
|
|
1653
|
+
* @param auditReason Why unrestricted access is correct here. なぜここで無制限が正当かの理由。
|
|
1654
|
+
* @returns The unrestricted scope. 無制限の閲覧範囲。
|
|
1655
|
+
*/
|
|
1656
|
+
static unrestricted(auditReason: string): DailyReportViewerScope;
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Computes the cache-key digest for a whole viewer scope.
|
|
1660
|
+
* 閲覧範囲全体からキャッシュキー用ダイジェストを算出する処理。
|
|
1661
|
+
*
|
|
1662
|
+
* スコープに次元が増えてもキャッシュキーの生成点を 1 つに保つための入口。
|
|
1663
|
+
*
|
|
1664
|
+
* @param scope Viewer scope. 閲覧範囲。
|
|
1665
|
+
* @returns Digest usable as a cache-key component. キャッシュキー構成要素として使えるダイジェスト。
|
|
1666
|
+
*/
|
|
1667
|
+
declare function computeViewerScopeDigest(scope: DailyReportViewerScope): string;
|
|
1668
|
+
/**
|
|
1669
|
+
* Computes a stable and bounded cache-key digest of the set.
|
|
1670
|
+
* 可視集合から安定・有界なキャッシュキー用ダイジェストを算出する処理。
|
|
1671
|
+
*
|
|
1672
|
+
* 無制限は `"all"`、空集合は `"none"`、それ以外は `s:` 名前空間の下へ**長さ接頭辞付きトークンを昇順連結**する。
|
|
1673
|
+
* 連結は長さ接頭辞付きなので**単射**であり、通常経路 (`s:`) では論理的に等価な集合が必ず同一の、
|
|
1674
|
+
* 異なる集合が必ず別のダイジェストになる。sentinel (`all` / `none`) とも名前空間が分かれるため、
|
|
1675
|
+
* 限定集合が無制限 / 全拒否と同じバケットを共有することはない。権限が安定している間はキーが
|
|
1676
|
+
* 不変になるため、キャッシュは完全に再利用される (性能無退行)。
|
|
1677
|
+
*
|
|
1678
|
+
* ❗ 区分数が異常に多い場合だけキー長を抑えるため FNV-1a 32bit へ退避し `sh:` 接頭辞を付ける。
|
|
1679
|
+
* **この退避経路に限り理論上の衝突がありうる** (32bit)。衝突しても影響は同一ユーザーの
|
|
1680
|
+
* キャッシュ再利用に留まり、認可判定そのものは SQL 述語が担うため漏洩には至らない。
|
|
1681
|
+
* 通常の区分数 (数個) では到達しない。
|
|
1621
1682
|
*
|
|
1622
1683
|
* @param set Normalized visible set. 正規化済みの可視集合。
|
|
1623
1684
|
* @returns Digest usable as a cache-key component. キャッシュキー構成要素として使えるダイジェスト。
|
|
@@ -1782,15 +1843,15 @@ declare function createDailyReportService(config: DailyReportServiceConfig): {
|
|
|
1782
1843
|
clearCache: () => Promise<void>;
|
|
1783
1844
|
invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
|
|
1784
1845
|
getUserIdByExternalId: (externalId: string) => Promise<number | null>;
|
|
1785
|
-
getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
1786
|
-
getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number
|
|
1787
|
-
getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
1788
|
-
getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
1789
|
-
getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }
|
|
1790
|
-
getDailyReportDetailByIdByExternalId: (reportHubId: number, externalId: string, options
|
|
1846
|
+
getDailyReportIdsByExternalId: (scope: DailyReportViewerScope, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
|
|
1847
|
+
getDailyReportIdsFirstPageByExternalId: (scope: DailyReportViewerScope, externalId: string, limit: number) => Promise<DailyReportItem[]>;
|
|
1848
|
+
getDailyReportsByBusinessDate: (scope: DailyReportViewerScope, businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
1849
|
+
getDailyReportsByBusinessDateByExternalId: (scope: DailyReportViewerScope, businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
1850
|
+
getDailyReportDetailById: (scope: DailyReportViewerScope, reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
1851
|
+
getDailyReportDetailByIdByExternalId: (scope: DailyReportViewerScope, reportHubId: number, externalId: string, options?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
1791
1852
|
getDraftLabelId: () => Promise<number | null>;
|
|
1792
1853
|
getAttachmentsByHubIds: (hubIds: number[]) => Promise<Map<number, DailyReportAttachmentSummary[]>>;
|
|
1793
|
-
getAttachmentForUser: (
|
|
1854
|
+
getAttachmentForUser: (scope: DailyReportViewerScope, attachmentId: number, userId: number) => Promise<{
|
|
1794
1855
|
fileName: string;
|
|
1795
1856
|
filePath: string;
|
|
1796
1857
|
fileType: string | null;
|
|
@@ -1798,10 +1859,10 @@ declare function createDailyReportService(config: DailyReportServiceConfig): {
|
|
|
1798
1859
|
} | null>;
|
|
1799
1860
|
markAttachmentMissing: (attachmentId: number) => Promise<void>;
|
|
1800
1861
|
markAttachmentPresent: (attachmentId: number) => Promise<void>;
|
|
1801
|
-
setStarStatus: (userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string
|
|
1802
|
-
setReadStatus: (userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string
|
|
1803
|
-
addComment: (userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string
|
|
1804
|
-
deleteComment: (userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string
|
|
1862
|
+
setStarStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
1863
|
+
setReadStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
1864
|
+
addComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string) => Promise<DailyReportCommentItem>;
|
|
1865
|
+
deleteComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string) => Promise<void>;
|
|
1805
1866
|
createDailyReport: (userId: number, businessDate: string, clientTempId: string) => Promise<DailyReportDetail>;
|
|
1806
1867
|
updateDailyReport: (reportHubId: number, userId: number, data: {
|
|
1807
1868
|
title?: string;
|
|
@@ -2141,15 +2202,15 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2141
2202
|
clearCache: () => Promise<void>;
|
|
2142
2203
|
invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
|
|
2143
2204
|
getUserIdByExternalId: (externalId: string) => Promise<number | null>;
|
|
2144
|
-
getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
2145
|
-
getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number
|
|
2146
|
-
getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
2147
|
-
getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }
|
|
2148
|
-
getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }
|
|
2149
|
-
getDailyReportDetailByIdByExternalId: (reportHubId: number, externalId: string, options
|
|
2205
|
+
getDailyReportIdsByExternalId: (scope: DailyReportViewerScope, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
|
|
2206
|
+
getDailyReportIdsFirstPageByExternalId: (scope: DailyReportViewerScope, externalId: string, limit: number) => Promise<DailyReportItem[]>;
|
|
2207
|
+
getDailyReportsByBusinessDate: (scope: DailyReportViewerScope, businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
2208
|
+
getDailyReportsByBusinessDateByExternalId: (scope: DailyReportViewerScope, businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
|
|
2209
|
+
getDailyReportDetailById: (scope: DailyReportViewerScope, reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
2210
|
+
getDailyReportDetailByIdByExternalId: (scope: DailyReportViewerScope, reportHubId: number, externalId: string, options?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
|
|
2150
2211
|
getDraftLabelId: () => Promise<number | null>;
|
|
2151
2212
|
getAttachmentsByHubIds: (hubIds: number[]) => Promise<Map<number, DailyReportAttachmentSummary[]>>;
|
|
2152
|
-
getAttachmentForUser: (
|
|
2213
|
+
getAttachmentForUser: (scope: DailyReportViewerScope, attachmentId: number, userId: number) => Promise<{
|
|
2153
2214
|
fileName: string;
|
|
2154
2215
|
filePath: string;
|
|
2155
2216
|
fileType: string | null;
|
|
@@ -2157,10 +2218,10 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2157
2218
|
} | null>;
|
|
2158
2219
|
markAttachmentMissing: (attachmentId: number) => Promise<void>;
|
|
2159
2220
|
markAttachmentPresent: (attachmentId: number) => Promise<void>;
|
|
2160
|
-
setStarStatus: (userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string
|
|
2161
|
-
setReadStatus: (userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string
|
|
2162
|
-
addComment: (userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string
|
|
2163
|
-
deleteComment: (userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string
|
|
2221
|
+
setStarStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isStarred: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
2222
|
+
setReadStatus: (scope: DailyReportViewerScope, userId: number, reportHubId: number, businessDate: string | null, isRead: boolean, clientTempId: string) => Promise<DailyReportUserStatusRow>;
|
|
2223
|
+
addComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, content: string, businessDate: string | null, clientTempId: string) => Promise<DailyReportCommentItem>;
|
|
2224
|
+
deleteComment: (scope: DailyReportViewerScope, userId: number, reportHubId: number, commentId: number, businessDate: string | null, clientTempId: string) => Promise<void>;
|
|
2164
2225
|
createDailyReport: (userId: number, businessDate: string, clientTempId: string) => Promise<DailyReportDetail>;
|
|
2165
2226
|
updateDailyReport: (reportHubId: number, userId: number, data: {
|
|
2166
2227
|
title?: string;
|
|
@@ -2193,4 +2254,4 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
|
|
|
2193
2254
|
knownSourceTypes: readonly string[];
|
|
2194
2255
|
};
|
|
2195
2256
|
|
|
2196
|
-
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, type DailyReportVisibilityResolveContext, type DailyReportVisibilityResolveReason, type DailyReportVisibleSourceTypes, type EpochStore, type ExternalReportFields, type RedisStreamMessage, SqlResultCache, type SqlResultCacheQueryOptions, type StreamEntry, type VisibleSourceTypeSet, asciiFallbackFileName, buildVisibleSourceTypeCondition, computeSourceTypeDigest, createDailyReportHandlers, createDailyReportServer, createDailyReportService, createEpochStore, defineDailyReportAuthzResources, defineDailyReportSchema, deniesEverySourceType, encodeRfc8187, formatDateValue, generateETag, isSourceTypeVisible, isStreamIdLte, jsonResponseWithETag, normalizeVisibleSourceTypes, sanitizeMediaType, seedDailyReportAuthzResources, transformJsonArray };
|
|
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 };
|