@aiquants/daily-report 0.3.1 → 0.3.2

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.
@@ -13,7 +13,6 @@ import { useDailyReportActionContext } from "../contexts/daily-report-action-con
13
13
  import { useDailyReportDetail } from "../hooks/use-daily-report"
14
14
  import { useDailyReportComments } from "../hooks/use-daily-report-comments"
15
15
  import { useDynamicViewportHeight } from "../hooks/use-dynamic-viewport-height"
16
- import { cn } from "../ui/cn"
17
16
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"
18
17
  import {
19
18
  DEFAULT_ITEM_HEIGHT,
@@ -505,7 +504,7 @@ const DailyReportSidePane = ({
505
504
  }) => {
506
505
  const [activeTab, setActiveTab] = useState<DailyReportSidePaneTab>("article")
507
506
  const [isEditing, setIsEditing] = useState(false)
508
- const { draftLabelName, fieldLabels, sourceTypeConfigs } = useDailyReportConfig()
507
+ const { draftLabelName, fieldLabels } = useDailyReportConfig()
509
508
  const { report, error, isLoading } = useDailyReportDetail(selectedItem?.reportHubId ?? -1, selectedItem?.businessDate ?? null)
510
509
 
511
510
  const scrollContainerRef = useRef<HTMLDivElement>(null)
@@ -571,11 +570,6 @@ const DailyReportSidePane = ({
571
570
  <div className="flex min-h-4 items-center gap-1 text-slate-500 text-xs dark:text-slate-300">
572
571
  <span>{fieldLabels.author}:</span>
573
572
  {showHeaderSkeleton ? <div className="h-3 w-24 animate-pulse rounded bg-slate-200 dark:bg-slate-700" /> : <span>{headerCreator}</span>}
574
- {(() => {
575
- const sourceType = report?.sourceType
576
- const sourceConfig = sourceType ? sourceTypeConfigs?.[sourceType] : undefined
577
- return sourceConfig ? <span className={cn("ml-2 inline-flex shrink-0 items-center rounded px-2 py-0.5 font-semibold text-[10px] tracking-wide", sourceConfig.badgeClassName)}>{sourceConfig.label}</span> : null
578
- })()}
579
573
  </div>
580
574
  </div>
581
575
  <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as DailyReportSidePaneTab)} className="flex min-h-0 flex-1 flex-col">
@@ -910,7 +904,6 @@ const extractPreviewLines = (content: string | null): string[] => {
910
904
  const ListReportItem = ({ item, isActive, onSelect }: { item: DailyReportItem; isActive: boolean; onSelect: (id: number) => void }) => {
911
905
  const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)
912
906
  const { toggleStar, toggleRead, removeStaleItem } = useDailyReportActionContext()
913
- const { sourceTypeConfigs } = useDailyReportConfig()
914
907
 
915
908
  // フェッチが一度でも試行されたかを追跡する ref
916
909
  const hasEverFetched = useRef(false)
@@ -996,11 +989,6 @@ const ListReportItem = ({ item, isActive, onSelect }: { item: DailyReportItem; i
996
989
  <span className="truncate font-medium text-slate-900 dark:text-slate-100">{creator}</span>
997
990
  </span>
998
991
  <div className="ml-auto flex items-center gap-1.5">
999
- {(() => {
1000
- const sourceType = report?.sourceType
1001
- const sourceConfig = sourceType ? sourceTypeConfigs?.[sourceType] : undefined
1002
- return sourceConfig ? <span className={cn("inline-flex shrink-0 items-center rounded px-2 py-0.5 font-semibold text-[10px] tracking-wide", sourceConfig.badgeClassName)}>{sourceConfig.label}</span> : null
1003
- })()}
1004
992
  <button
1005
993
  type="button"
1006
994
  title={report.isStarred ? "スターを外す" : "スターを付ける"}
@@ -38,11 +38,6 @@ export type DailyReportFieldLabels = {
38
38
  relationsEmpty: string
39
39
  }
40
40
 
41
- export type SourceTypeConfig = {
42
- label: string
43
- badgeClassName?: string
44
- }
45
-
46
41
  /** クライアント層の設定一式。 */
47
42
  export type DailyReportClientConfig = {
48
43
  /** 画面タイトル (ヘッダーへ渡す)。 */
@@ -55,8 +50,6 @@ export type DailyReportClientConfig = {
55
50
  draftLabelName: string
56
51
  /** 詳細/一覧カードのフィールド見出しラベル。既定は中立英語。アプリが自ドメインの表記を注入する。 */
57
52
  fieldLabels: DailyReportFieldLabels
58
- /** 外部ソース区分ごとのバッジ表示設定。 */
59
- sourceTypeConfigs?: Record<string, SourceTypeConfig>
60
53
  /** ヘッダー描画スロット。アプリのヘッダーコンポーネントを差し込む。 */
61
54
  renderHeader: (props: DailyReportHeaderProps) => ReactNode
62
55
  /** 開発向けコントロール (SSE 購読トグル等) を表示するか。 */
@@ -69,7 +62,6 @@ export type DailyReportClientConfig = {
69
62
  */
70
63
  export type DailyReportClientConfigInput = Partial<Omit<DailyReportClientConfig, "fieldLabels">> & {
71
64
  fieldLabels?: Partial<DailyReportFieldLabels>
72
- sourceTypeConfigs?: Record<string, SourceTypeConfig>
73
65
  }
74
66
 
75
67
  /** 中立英語のフィールドラベル既定 (ドメイン非依存)。 */
@@ -102,7 +94,6 @@ export const defaultDailyReportClientConfig: DailyReportClientConfig = {
102
94
  // 中立プレースホルダー。実運用のラベル名 (アプリ固有) は Provider / config prop で上書きする。
103
95
  draftLabelName: "draft",
104
96
  fieldLabels: defaultDailyReportFieldLabels,
105
- sourceTypeConfigs: {},
106
97
  renderHeader: ({ title, annotation }) => (
107
98
  <header className="flex flex-wrap items-baseline justify-between gap-2 px-2 py-1">
108
99
  <span className="text-nowrap text-slate-700 text-sm dark:text-slate-200">{title}</span>
@@ -703,7 +703,6 @@ export const DailyReportActionProvider = ({ children, user, initialItems = [], u
703
703
  createdAt: now,
704
704
  author: getUserDisplayName(user),
705
705
  userId: user?.id ?? "unknown",
706
- sourceType: "Internal",
707
706
  employeeName: getUserDisplayName(user),
708
707
  updatedBy: getUserDisplayName(user),
709
708
  updatedAt: now,
@@ -123,10 +123,6 @@ export class SqlResultCache {
123
123
  // キャッシュ保存時に現在の epoch を記録
124
124
  const epoch = opts.epochKey ? await this.epochStore.getEpoch(opts.epochKey) : 0
125
125
  this.buckets.set(cacheKey, { records, expireAt, epoch })
126
- // 蓄積防止のため、サイズが一定値を超えたら期限切れキャッシュを一括クリーンアップ
127
- if (this.buckets.size > 500) {
128
- this.cleanExpired()
129
- }
130
126
  }
131
127
  return records
132
128
  })()
@@ -166,18 +162,4 @@ export class SqlResultCache {
166
162
  }
167
163
  }
168
164
  }
169
-
170
- /**
171
- * Cleans up all expired cache buckets to prevent memory accumulation.
172
- * メモリー蓄積を防ぐため、期限切れのキャッシュバケットをすべてクリーンアップする処理。
173
- */
174
- private cleanExpired(): void {
175
- const now = Date.now()
176
- // 期限切れのキーを削除
177
- for (const [key, bucket] of this.buckets.entries()) {
178
- if (bucket.expireAt <= now) {
179
- this.buckets.delete(key)
180
- }
181
- }
182
- }
183
165
  }
@@ -326,7 +326,6 @@ export function createDailyReportService(config: DailyReportServiceConfig) {
326
326
  date: formatDateValue(hub.businessDate, "YYYY-MM-DD"),
327
327
  author: row.creatorName ?? maskAuditActor(hub.createdBy) ?? "",
328
328
  userId: hub.userId ? encodeUserId(hub.userId) : "",
329
- sourceType: hub.sourceType ?? "Internal",
330
329
  createdAt: formatDateValue(hub.createdAt, "YYYY-MM-DD HH:mm:ss"),
331
330
  updatedAt: formatDateValue(hub.updatedAt, "YYYY-MM-DD HH:mm:ss"),
332
331
  updatedBy: maskAuditActor(hub.updatedBy),
@@ -1035,7 +1034,6 @@ export function createDailyReportService(config: DailyReportServiceConfig) {
1035
1034
  createdAt: formatDateValue(hub.createdAt, "YYYY-MM-DD HH:mm:ss"),
1036
1035
  author: userName,
1037
1036
  userId: encodeUserId(userId),
1038
- sourceType: "Internal",
1039
1037
  employeeName: userName,
1040
1038
  // 監査列と同様、クライアント公開時は生内部 ID を難読化する (mapHubRecord と整合)
1041
1039
  updatedBy: encodeUserId(userId),
@@ -39,7 +39,6 @@ export const dailyReportDetailSchema = z.object({
39
39
  createdAt: z.string().nullable(),
40
40
  author: z.string(),
41
41
  userId: z.string(),
42
- sourceType: z.string().nullable(),
43
42
  employeeName: z.string().nullable(),
44
43
  updatedBy: z.string().nullable(),
45
44
  updatedAt: z.string().nullable(),
@@ -46,7 +46,6 @@ export type DailyReportDetail = {
46
46
  createdAt: string | null
47
47
  author: string
48
48
  userId: string
49
- sourceType: string | null
50
49
  employeeName: string | null
51
50
  updatedBy: string | null
52
51
  updatedAt: string | null
@@ -1,47 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest"
2
- import { createEpochStore, SqlResultCache } from "./cache"
3
-
4
- describe("SqlResultCache Memory Cleanup", () => {
5
- it("should clean up expired cache entries when map size exceeds 500", async () => {
6
- // Mock epoch store
7
- const epochStore = createEpochStore(undefined)
8
- vi.spyOn(epochStore, "getEpoch").mockResolvedValue(0)
9
-
10
- const cache = new SqlResultCache({ defaultTtlMs: 1000 }, epochStore)
11
-
12
- // 1. 期限切れのキャッシュを 500 件作成 (expireAt を過去に設定)
13
- // 直接プライベートフィールド buckets に流し込むことでモック状態を作る
14
- const buckets = (cache as any).buckets
15
- for (let i = 0; i < 505; i++) {
16
- buckets.set(`expired-key-${i}`, {
17
- records: [i],
18
- expireAt: Date.now() - 1000, // 過去時間
19
- epoch: 0,
20
- })
21
- }
22
-
23
- // 2. 有効なキャッシュを 10 件作成 (expireAt を未来に設定)
24
- for (let i = 0; i < 10; i++) {
25
- buckets.set(`valid-key-${i}`, {
26
- records: [i],
27
- expireAt: Date.now() + 100000, // 未来時間
28
- epoch: 0,
29
- })
30
- }
31
-
32
- expect(buckets.size).toBe(515)
33
-
34
- // 3. getOrFetch を実行して 500 件超えの閾値トリガーを引く
35
- const fetcher = vi.fn().mockResolvedValue(["new-value"])
36
- await cache.getOrFetch({
37
- cacheKey: "trigger-key",
38
- fetcher,
39
- })
40
-
41
- // 4. 検証: 期限切れの 505 件のデータが cleanExpired で削除され、有効な 10 件 + 新規の 1 件のみ残っていること
42
- expect(buckets.size).toBe(11) // 10 (valid) + 1 (trigger-key)
43
- expect(buckets.has("trigger-key")).toBe(true)
44
- expect(buckets.has("valid-key-0")).toBe(true)
45
- expect(buckets.has("expired-key-0")).toBe(false)
46
- })
47
- })