@aiquants/daily-report 0.7.0 → 0.9.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.
Files changed (67) hide show
  1. package/README.md +13 -6
  2. package/dist/client.d.mts +438 -26
  3. package/dist/client.d.ts +438 -26
  4. package/dist/client.js +5 -4
  5. package/dist/client.js.map +1 -1
  6. package/dist/client.mjs +5 -4
  7. package/dist/client.mjs.map +1 -1
  8. package/dist/index.d.mts +115 -3
  9. package/dist/index.d.ts +115 -3
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/server.d.mts +15 -0
  15. package/dist/server.d.ts +15 -0
  16. package/dist/server.js +9 -6
  17. package/dist/server.js.map +1 -1
  18. package/dist/server.mjs +9 -6
  19. package/dist/server.mjs.map +1 -1
  20. package/dist/{sse-schema-Df49KA7B.d.mts → sse-schema-BiUnufrI.d.mts} +12 -0
  21. package/dist/{sse-schema-RHckD7TS.d.ts → sse-schema-YZh7Pz7i.d.ts} +12 -0
  22. package/dist/styles/daily-report.standalone.css +1 -1
  23. package/package.json +3 -3
  24. package/src/client/components/daily-report-comment-item.tsx +6 -1
  25. package/src/client/components/daily-report-detail-list.tsx +65 -34
  26. package/src/client/components/daily-report-edit-form.tsx +6 -0
  27. package/src/client/components/daily-report-ids-stream-status.spec.tsx +93 -0
  28. package/src/client/components/daily-report-ids-stream-status.tsx +67 -0
  29. package/src/client/components/daily-report-list.tsx +88 -55
  30. package/src/client/components/daily-report-page.spec.tsx +119 -0
  31. package/src/client/components/daily-report-page.tsx +62 -69
  32. package/src/client/components/daily-report-resolved-content.tsx +36 -10
  33. package/src/client/components/report-views.spec.tsx +37 -2
  34. package/src/client/config-context.tsx +18 -0
  35. package/src/client/contexts/daily-report-action-context.spec.tsx +278 -0
  36. package/src/client/contexts/daily-report-action-context.tsx +203 -65
  37. package/src/client/contexts/daily-report-error-context.spec.tsx +66 -0
  38. package/src/client/contexts/daily-report-error-context.tsx +98 -0
  39. package/src/client/hooks/use-animated-number.spec.ts +103 -0
  40. package/src/client/hooks/use-animated-number.ts +69 -0
  41. package/src/client/hooks/use-daily-report.spec.ts +50 -2
  42. package/src/client/hooks/use-daily-report.ts +30 -1
  43. package/src/client/route-helpers.spec.ts +100 -0
  44. package/src/client/route-helpers.ts +20 -41
  45. package/src/client/streaming/daily-report-ids-stream-client.spec.ts +704 -0
  46. package/src/client/streaming/daily-report-ids-stream-client.ts +677 -0
  47. package/src/client/streaming/daily-report-ids-stream-session.spec.ts +360 -0
  48. package/src/client/streaming/daily-report-ids-stream-session.ts +280 -0
  49. package/src/client.ts +5 -0
  50. package/src/index.ts +1 -0
  51. package/src/server/handlers.action.spec.ts +44 -0
  52. package/src/server/handlers.ids-stream.spec.ts +414 -0
  53. package/src/server/handlers.ts +324 -8
  54. package/src/server/service.spec.ts +83 -7
  55. package/src/server/service.ts +100 -3
  56. package/src/server/test-helpers/handlers-config.ts +48 -1
  57. package/src/server.spec.ts +49 -0
  58. package/src/server.ts +7 -0
  59. package/src/shared/business-date.spec.ts +22 -1
  60. package/src/shared/business-date.ts +20 -0
  61. package/src/shared/comment-adapter.spec.ts +38 -0
  62. package/src/shared/comment-adapter.ts +9 -7
  63. package/src/shared/ids-stream.spec.ts +65 -0
  64. package/src/shared/ids-stream.ts +139 -0
  65. package/src/shared/sse-schema.ts +5 -0
  66. package/src/shared/text-utils.spec.ts +42 -0
  67. package/src/shared/text-utils.ts +8 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/business-date.ts","../src/shared/comment-adapter.ts","../src/shared/logger.ts","../src/shared/sse-schema.ts","../src/shared/text-utils.ts"],"sourcesContent":["/**\n * Business date normalization and formatting helper functions.\n * 営業日の正規化とフォーマットを支援するユーティリティ群。\n */\n\ntype BusinessDateInput = string | Date | null | undefined\n\nconst ISO_DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst SLASH_DATE_PATTERN = /^\\d{4}\\/\\d{2}\\/\\d{2}$/\n\nconst zeroPad = (value: number): string => {\n return value < 10 ? `0${value}` : `${value}`\n}\n\nconst normalizeFromDate = (value: Date): string | null => {\n if (Number.isNaN(value.getTime())) {\n return null\n }\n\n const year = value.getFullYear()\n const month = zeroPad(value.getMonth() + 1)\n const day = zeroPad(value.getDate())\n return `${year}-${month}-${day}`\n}\n\n/**\n * Normalizes business date input into YYYY-MM-DD format string.\n * 営業日を YYYY-MM-DD 形式の文字列へ正規化する。\n */\nexport const normalizeBusinessDateKey = (value: BusinessDateInput): string | null => {\n if (value === null || value === undefined) {\n return null\n }\n\n if (value instanceof Date) {\n return normalizeFromDate(value)\n }\n\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return null\n }\n\n if (ISO_DATE_PATTERN.test(trimmed)) {\n return trimmed\n }\n\n if (SLASH_DATE_PATTERN.test(trimmed)) {\n return trimmed.replaceAll(\"/\", \"-\")\n }\n\n const parsed = new Date(trimmed)\n return normalizeFromDate(parsed)\n}\n\n/**\n * Converts normalized business date into display format YYYY-MM-DD.\n * 正規化済みの営業日を YYYY-MM-DD 形式の表示文字列へ変換する。\n */\nexport const formatBusinessDateDisplay = (value: BusinessDateInput): string | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n return normalized\n}\n\n/**\n * Parses normalized business date into Date object.\n * 正規化した営業日を Date オブジェクトへ変換する。\n */\nexport const parseBusinessDateKeyToDate = (value: BusinessDateInput): Date | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n\n const [year, month, day] = normalized.split(\"-\").map((part) => Number.parseInt(part, 10))\n if ([year, month, day].some((component) => Number.isNaN(component))) {\n return null\n }\n\n return new Date(year, month - 1, day)\n}\n","/**\n * Adapters merging legacy JSON comments and relational comments for the UI.\n * レガシー JSON コメントとリレーショナルコメントを UI 向けに統合するアダプタ群。\n */\nimport type { DailyReportComment, DailyReportCommentItem } from \"./types\"\n\nexport type UIComment = {\n id: string | number\n authorName: string\n content: string\n createdAt?: string\n isMine: boolean\n color?: string\n isLegacy: boolean\n}\n\n/**\n * Merges legacy JSON comments and modern relational comments into a unified UI format.\n * レガシーな JSON コメントとモダンなリレーショナルコメントを統合された UI 形式にマージする。\n */\nexport const mergeComments = (legacyComments: DailyReportComment[], modernComments: DailyReportCommentItem[], currentUserId?: string | null): UIComment[] => {\n // 1. Legacy コメントの変換\n const legacyUIComments: UIComment[] = legacyComments.map((c, index) => ({\n id: `legacy-${index}`,\n authorName: c.name || \"Unknown\",\n content: c.text || \"\",\n isMine: false,\n color: c.color || undefined,\n isLegacy: true,\n }))\n\n // 2. Modern コメントの変換\n const modernUIComments: UIComment[] = modernComments.map((c) => ({\n id: c.id,\n authorName: c.userName ?? \"\",\n content: c.content ?? \"\",\n createdAt: c.createdAt ?? \"\",\n isMine: currentUserId ? c.userId === currentUserId : c.isMine,\n isLegacy: false,\n }))\n\n // 3. 結合 (Legacy を先に表示)\n return [...legacyUIComments, ...modernUIComments]\n}\n\n/**\n * Resolves comment color tokens into Tailwind text classes.\n * コメント色のキーワードを Tailwind のテキストクラスに変換する関数。\n */\nexport const resolveCommentColorClass = (color: string | null | undefined): string => {\n if (!color) {\n return \"text-gray-700\"\n }\n\n const normalized = color.toLowerCase()\n\n if (normalized.includes(\"red\") || normalized.includes(\"赤\")) {\n return \"text-red-600\"\n }\n\n if (normalized.includes(\"green\") || normalized.includes(\"緑\")) {\n return \"text-green-600\"\n }\n\n if (normalized.includes(\"blue\") || normalized.includes(\"青\")) {\n return \"text-blue-600\"\n }\n\n if (normalized.includes(\"yellow\") || normalized.includes(\"黄\")) {\n return \"text-yellow-600\"\n }\n\n if (normalized.includes(\"orange\") || normalized.includes(\"橙\")) {\n return \"text-orange-500\"\n }\n\n return \"text-gray-700\"\n}\n","/**\n * Minimal level-filtered logger used internally by the daily-report package.\n * daily-report パッケージ内部で使う最小のレベルフィルタ付きロガー。\n *\n * 消費アプリのロガー実装 (console 互換の debug/info/warn/error) を注入でき、\n * 未注入時は console にフォールバックする。\n */\n\n/** 出力フィルタリング用のログレベル。 */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/** Console 互換のロガーインターフェース。 */\nexport interface DailyReportLogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * Creates a prefixed, level-filtered logger delegating to the given implementation.\n * 指定実装へ委譲するプレフィックス付き・レベルフィルタ付きロガーを生成する処理。\n */\nexport const createLogger = (level: LogLevel, prefix: string, impl: DailyReportLogger = console): DailyReportLogger => {\n // 文字列メッセージはプレフィックスを連結、それ以外は先頭引数として付加する\n const format = (message: unknown): unknown[] => (typeof message === \"string\" ? [`${prefix} ${message}`] : [prefix, message])\n return {\n debug: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.DEBUG) impl.debug(...format(message), ...rest)\n },\n info: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.INFO) impl.info(...format(message), ...rest)\n },\n warn: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.WARN) impl.warn(...format(message), ...rest)\n },\n error: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.ERROR) impl.error(...format(message), ...rest)\n },\n }\n}\n","/**\n * Zod schemas for daily-report SSE messages (client/server shared contract).\n * 日報 SSE メッセージの zod スキーマ (client / server 共有契約)。\n */\nimport { z } from \"zod\"\nimport type { DailyReportCommentItem as DeclaredComment, DailyReportDetail as DeclaredDetail } from \"./types\"\n\n// --- サブスキーマ(types.ts の型と構造的に一致させる) ---\n\nexport const dailyReportInterviewerSchema = z.object({\n name: z.string().nullish(),\n affiliation: z.string().nullish(),\n})\n\nexport const dailyReportCommentSchema = z.object({\n name: z.string().nullish(),\n text: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportLabelDefSchema = z.object({\n id: z.number(),\n name: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportCommentItemSchema = z.object({\n id: z.number(),\n userId: z.string().nullish(),\n userName: z.string().nullish(),\n content: z.string().nullish(),\n createdAt: z.string().nullish(),\n isMine: z.boolean(),\n})\n\n/**\n * 添付ファイル 1 件のスキーマ。\n *\n * 周囲のフィールドは `.nullish()` だが、ここは意図して `.nullable()` を使う。\n * `.nullish()` は推論キーを任意 (`?`) にするため、`types.ts` 側の必須 `fileType: string | null`\n * へ代入できず、ファイル末尾の `_AssertDetailCompat` が `never` に落ちてコンパイルエラーになる。\n * 既存の `.nullish()` フィールドはいずれも `types.ts` 側が `?` 付きの任意キーとペアになっており、\n * 「`.nullish()` ↔ 任意キー」「`.nullable()` ↔ 必須 `| null`」の対応で揃っている。\n * `| undefined` を混ぜないことは、詳細型を消費する DuckDB ステージ行の要請でもある。\n */\nexport const dailyReportAttachmentItemSchema = z.object({\n id: z.string(),\n fileName: z.string(),\n fileType: z.string().nullable(),\n fileSize: z.number().nullable(),\n createdAt: z.string().nullable(),\n state: z.enum([\"present\", \"absent\", \"unknown\"]),\n})\n\nexport const dailyReportDetailSchema = z.object({\n reportHubId: z.number(),\n date: z.string().nullish(),\n createdAt: z.string().nullish(),\n author: z.string().nullish(),\n userId: z.string().nullish(),\n sourceType: z.string().nullish(),\n employeeName: z.string().nullish(),\n updatedBy: z.string().nullish(),\n updatedAt: z.string().nullish(),\n category: z.string().nullish(),\n creationCategory: z.string().nullish(),\n visitTimeFrom: z.string().nullish(),\n visitTimeTo: z.string().nullish(),\n customerName: z.string().nullish(),\n interviewers: z.array(dailyReportInterviewerSchema),\n subject: z.string().nullish(),\n content: z.string().nullish(),\n comments: z.array(dailyReportCommentSchema),\n isRead: z.boolean(),\n isStarred: z.boolean(),\n labels: z.array(dailyReportLabelDefSchema),\n commentItems: z.array(dailyReportCommentItemSchema),\n // ❗ `.default([])` は必須。SSE の実体は永続 Redis Stream (MAXLEN 保持) であり、\n // 配備をまたいで**旧形式のエントリが残る**。共有リーダーはプロセス起動時に先頭から\n // 再生し、再接続クライアントの catch-up も同じエントリを読み直すため、\n // このキーを必須にすると添付機能を入れる前に publish された全エントリが\n // fail-closed で捨てられ、再接続タブへ更新が永久に届かなくなる。\n // 送信側の詰め忘れ検知は types.ts の必須フィールドと `_AssertDetailCompat`\n // (出力型は既定値適用後なので必須のまま) が担っており、ここを緩めても失われない。\n attachments: z.array(dailyReportAttachmentItemSchema).default([]),\n})\n\n// --- SSE メッセージスキーマ(8種) ---\n\nexport const connectedMessageSchema = z.object({\n type: z.literal(\"connected\"),\n})\n\nexport const statusUpdateMessageSchema = z.object({\n type: z.literal(\"status-update\"),\n reportHubId: z.number(),\n statusType: z.enum([\"star\", \"read\"]),\n value: z.boolean(),\n clientTempId: z.string(),\n // recipientRawUserId はサーバーサイドフィルタリング専用。\n // SSE ルートが raw JSON から取得してフィルタ後に除去する。\n // クライアントには到達しないが、サーバー側の publish で .parse() を通すため定義が必要。\n recipientRawUserId: z.number().optional(),\n})\n\nexport const commentAddMessageSchema = z.object({\n type: z.literal(\"comment-add\"),\n reportHubId: z.number(),\n comment: dailyReportCommentItemSchema,\n clientTempId: z.string(),\n})\n\nexport const commentDeleteMessageSchema = z.object({\n type: z.literal(\"comment-delete\"),\n reportHubId: z.number(),\n commentId: z.number(),\n clientTempId: z.string(),\n})\n\nexport const reportCreateMessageSchema = z.object({\n type: z.literal(\"report-create\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportUpdateMessageSchema = z.object({\n type: z.literal(\"report-update\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportPublishMessageSchema = z.object({\n type: z.literal(\"report-publish\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportDeleteMessageSchema = z.object({\n type: z.literal(\"report-delete\"),\n reportHubId: z.number(),\n clientTempId: z.string(),\n})\n\n// --- Discriminated Union ---\n\nexport const dailyReportSseMessageSchema = z.discriminatedUnion(\"type\", [\n connectedMessageSchema,\n statusUpdateMessageSchema,\n commentAddMessageSchema,\n commentDeleteMessageSchema,\n reportCreateMessageSchema,\n reportUpdateMessageSchema,\n reportPublishMessageSchema,\n reportDeleteMessageSchema,\n])\n\n// --- 型エクスポート ---\n\nexport type DailyReportSseMessage = z.infer<typeof dailyReportSseMessageSchema>\n\n// --- コンパイル時の型互換チェック ---\n// Zod推論型が既存の型定義に代入可能であることを保証する。\n// ここでエラーが出たらスキーマと types.ts がずれている。\n\ntype _AssertDetailCompat = z.infer<typeof dailyReportDetailSchema> extends DeclaredDetail ? true : never\ntype _AssertCommentCompat = z.infer<typeof dailyReportCommentItemSchema> extends DeclaredComment ? true : never\nconst _detailCheck: _AssertDetailCompat = true\nconst _commentCheck: _AssertCommentCompat = true\nvoid _detailCheck\nvoid _commentCheck\n","/**\n * Shared text helpers for daily report view components.\n * 日報ビューコンポーネント向けの共通テキストヘルパー。\n */\n\n/**\n * Normalizes nullable text by trimming whitespace and applying fallback.\n * null 許容の文字列をトリムしつつフォールバック値を適用する関数。\n */\nexport const fallbackText = (value: string | null | undefined): string => {\n if (!value) {\n return \"-\"\n }\n const trimmed = value.trim()\n return trimmed === \"\" ? \"-\" : trimmed\n}\n\n/**\n * Formats a date string to ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).\n * 日付文字列を ISO 8601 形式 (YYYY-MM-DD または YYYY-MM-DDTHH:mm:ss) に整形する関数。\n */\nexport const formatToIsoDate = (value: unknown): string => {\n if (!value) {\n return \"-\"\n }\n if (typeof value !== \"string\") {\n // If value is not a string (e.g. Date object), try to convert it to string or handle it\n if (value instanceof Date) {\n return formatToIsoDate(value.toISOString())\n }\n return String(value)\n }\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return \"-\"\n }\n\n const date = new Date(trimmed)\n if (Number.isNaN(date.getTime())) {\n return trimmed\n }\n\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, \"0\")\n const day = String(date.getDate()).padStart(2, \"0\")\n\n const hasTime = trimmed.includes(\":\") || trimmed.includes(\"T\")\n\n if (hasTime) {\n const hours = String(date.getHours()).padStart(2, \"0\")\n const minutes = String(date.getMinutes()).padStart(2, \"0\")\n const seconds = String(date.getSeconds()).padStart(2, \"0\")\n return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`\n }\n\n return `${year}-${month}-${day}`\n}\n"],"mappings":"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","names":["ISO_DATE_PATTERN","SLASH_DATE_PATTERN","zeroPad","value","normalizeFromDate","year","month","day","normalizeBusinessDateKey","trimmed","parsed","formatBusinessDateDisplay","normalized","parseBusinessDateKeyToDate","part","component","mergeComments","legacyComments","modernComments","currentUserId","legacyUIComments","c","index","modernUIComments","resolveCommentColorClass","color","normalized","LogLevel","createLogger","level","prefix","impl","format","message","rest","z","dailyReportInterviewerSchema","dailyReportCommentSchema","dailyReportLabelDefSchema","dailyReportCommentItemSchema","dailyReportAttachmentItemSchema","dailyReportDetailSchema","connectedMessageSchema","statusUpdateMessageSchema","commentAddMessageSchema","commentDeleteMessageSchema","reportCreateMessageSchema","reportUpdateMessageSchema","reportPublishMessageSchema","reportDeleteMessageSchema","dailyReportSseMessageSchema","fallbackText","value","trimmed","formatToIsoDate","date","year","month","day","hours","minutes","seconds"]}
1
+ {"version":3,"sources":["../src/shared/business-date.ts","../src/shared/comment-adapter.ts","../src/shared/ids-stream.ts","../src/shared/logger.ts","../src/shared/sse-schema.ts","../src/shared/text-utils.ts"],"sourcesContent":["/**\n * Business date normalization and formatting helper functions.\n * 営業日の正規化とフォーマットを支援するユーティリティ群。\n */\n\ntype BusinessDateInput = string | Date | null | undefined\n\nconst ISO_DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst SLASH_DATE_PATTERN = /^\\d{4}\\/\\d{2}\\/\\d{2}$/\n\nconst zeroPad = (value: number): string => {\n return value < 10 ? `0${value}` : `${value}`\n}\n\nconst normalizeFromDate = (value: Date): string | null => {\n if (Number.isNaN(value.getTime())) {\n return null\n }\n\n const year = value.getFullYear()\n const month = zeroPad(value.getMonth() + 1)\n const day = zeroPad(value.getDate())\n return `${year}-${month}-${day}`\n}\n\n/**\n * Returns today's business-date key (YYYY-MM-DD) in the local calendar.\n * ローカルカレンダー上の「本日」の営業日キー (YYYY-MM-DD) を返す。\n *\n * `new Date().toISOString()` の日付部は UTC 日付になり、UTC より東のタイムゾーン\n * (JST など) では深夜〜午前帯に前日へずれる。営業日はブラウザのローカル暦日で判定する\n * ため、ローカル getter から直接組む。タイムゾーンはパッケージに固定しない (消費側の\n * 実行環境が決める) 方針に沿う。\n *\n * @param now Clock injection for tests (defaults to the current time). テスト用の時刻注入 (既定は現在時刻)。\n * @returns Local calendar day as YYYY-MM-DD. ローカル暦日の YYYY-MM-DD。\n */\nexport const resolveTodayBusinessDateKey = (now: Date = new Date()): string => {\n if (Number.isNaN(now.getTime())) {\n // 不正な Date が注入されたら現在時刻で解決する (空の営業日キーを返さない)\n return resolveTodayBusinessDateKey(new Date())\n }\n return `${now.getFullYear()}-${zeroPad(now.getMonth() + 1)}-${zeroPad(now.getDate())}`\n}\n\n/**\n * Normalizes business date input into YYYY-MM-DD format string.\n * 営業日を YYYY-MM-DD 形式の文字列へ正規化する。\n */\nexport const normalizeBusinessDateKey = (value: BusinessDateInput): string | null => {\n if (value === null || value === undefined) {\n return null\n }\n\n if (value instanceof Date) {\n return normalizeFromDate(value)\n }\n\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return null\n }\n\n if (ISO_DATE_PATTERN.test(trimmed)) {\n return trimmed\n }\n\n if (SLASH_DATE_PATTERN.test(trimmed)) {\n return trimmed.replaceAll(\"/\", \"-\")\n }\n\n const parsed = new Date(trimmed)\n return normalizeFromDate(parsed)\n}\n\n/**\n * Converts normalized business date into display format YYYY-MM-DD.\n * 正規化済みの営業日を YYYY-MM-DD 形式の表示文字列へ変換する。\n */\nexport const formatBusinessDateDisplay = (value: BusinessDateInput): string | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n return normalized\n}\n\n/**\n * Parses normalized business date into Date object.\n * 正規化した営業日を Date オブジェクトへ変換する。\n */\nexport const parseBusinessDateKeyToDate = (value: BusinessDateInput): Date | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n\n const [year, month, day] = normalized.split(\"-\").map((part) => Number.parseInt(part, 10))\n if ([year, month, day].some((component) => Number.isNaN(component))) {\n return null\n }\n\n return new Date(year, month - 1, day)\n}\n","/**\n * Adapters merging legacy JSON comments and relational comments for the UI.\n * レガシー JSON コメントとリレーショナルコメントを UI 向けに統合するアダプタ群。\n */\nimport type { DailyReportComment, DailyReportCommentItem } from \"./types\"\n\nexport type UIComment = {\n id: string | number\n authorName: string\n content: string\n createdAt?: string\n isMine: boolean\n color?: string\n isLegacy: boolean\n}\n\n/**\n * Merges legacy JSON comments and modern relational comments into a unified UI format.\n * レガシーな JSON コメントとモダンなリレーショナルコメントを統合された UI 形式にマージする。\n */\nexport const mergeComments = (legacyComments: DailyReportComment[], modernComments: DailyReportCommentItem[], currentUserId?: string | null): UIComment[] => {\n // 1. Legacy コメントの変換\n const legacyUIComments: UIComment[] = legacyComments.map((c, index) => ({\n id: `legacy-${index}`,\n authorName: c.name || \"Unknown\",\n content: c.text || \"\",\n isMine: false,\n color: c.color || undefined,\n isLegacy: true,\n }))\n\n // 2. Modern コメントの変換\n const modernUIComments: UIComment[] = modernComments.map((c) => ({\n id: c.id,\n authorName: c.userName ?? \"\",\n content: c.content ?? \"\",\n createdAt: c.createdAt ?? \"\",\n isMine: currentUserId ? c.userId === currentUserId : c.isMine,\n isLegacy: false,\n }))\n\n // 3. 結合 (Legacy を先に表示)\n return [...legacyUIComments, ...modernUIComments]\n}\n\n/**\n * Resolves comment color tokens into Tailwind text classes.\n * コメント色のキーワードを Tailwind のテキストクラスに変換する関数。\n */\nexport const resolveCommentColorClass = (color: string | null | undefined): string => {\n // 各色にダークモードの明色対応を必ず添える。ダークカード上で暗色トークン\n // (text-gray-700 など) 単体だと文字が背景に沈んで読めない\n if (!color) {\n return \"text-gray-700 dark:text-gray-300\"\n }\n\n const normalized = color.toLowerCase()\n\n if (normalized.includes(\"red\") || normalized.includes(\"赤\")) {\n return \"text-red-600 dark:text-red-400\"\n }\n\n if (normalized.includes(\"green\") || normalized.includes(\"緑\")) {\n return \"text-green-600 dark:text-green-400\"\n }\n\n if (normalized.includes(\"blue\") || normalized.includes(\"青\")) {\n return \"text-blue-600 dark:text-blue-400\"\n }\n\n if (normalized.includes(\"yellow\") || normalized.includes(\"黄\")) {\n return \"text-yellow-600 dark:text-yellow-400\"\n }\n\n if (normalized.includes(\"orange\") || normalized.includes(\"橙\")) {\n return \"text-orange-500 dark:text-orange-400\"\n }\n\n return \"text-gray-700 dark:text-gray-300\"\n}\n","/**\n * Wire contract and ordering helpers for the daily-report ids NDJSON stream.\n * 日報 ID 一覧 NDJSON ストリームのワイヤ契約と並び順ヘルパー。\n *\n * サーバーは `GET {apiBasePath}/ids-stream` で 1 行 1 JSON の NDJSON を返す。\n * 行は原則 {@link DailyReportIdsStreamChunkLine} で、送出中に予期しない失敗が\n * 起きた場合のみ {@link DailyReportIdsStreamErrorLine} を 1 行流して正常クローズする\n * (クライアントは「isLast 無しで終端」を再開シグナルとして扱う)。\n *\n * 並び順は SQL の `ORDER BY business_date DESC, id DESC` (MSSQL の DESC は NULL を\n * 末尾に置く) と完全一致させる。カーソル再開はこの全順序に対する「厳密に後」の\n * 位置からのスライスとして定義する。\n */\nimport type { DailyReportItem } from \"./types\"\n\n/** ストリーム再開カーソル。直前チャンク末尾アイテムの並び順キー。 */\nexport type DailyReportIdsStreamCursor = {\n /** 営業日 (YYYY-MM-DD)。null は「営業日なし領域」(並び順の末尾) を表す。 */\n businessDate: string | null\n /** 日報 Hub ID。 */\n reportHubId: number\n}\n\n/** データを運ぶ NDJSON 行。 */\nexport type DailyReportIdsStreamChunkLine = {\n /** このチャンクのアイテム群 (並び順どおり)。 */\n items: DailyReportItem[]\n /** 次チャンクの再開カーソル。最終チャンクでは null。 */\n nextCursor: DailyReportIdsStreamCursor | null\n /** これが最終チャンクかどうか。 */\n isLast: boolean\n /** 全体件数。接続ごとの先頭行にのみ載る。 */\n total?: number\n}\n\n/** サーバー側の予期しない失敗を伝える NDJSON 行 (内部情報は含めない)。 */\nexport type DailyReportIdsStreamErrorLine = {\n error: string\n}\n\n/** NDJSON 1 行のユニオン。 */\nexport type DailyReportIdsStreamLine = DailyReportIdsStreamChunkLine | DailyReportIdsStreamErrorLine\n\n/**\n * Type guard for chunk lines (anything else is treated as an error line).\n * チャンク行の型ガード (それ以外はエラー行として扱う)。\n *\n * @param line Parsed NDJSON line. パース済みの NDJSON 行。\n * @returns True when the line carries items. アイテムを運ぶ行なら true。\n */\nexport const isIdsStreamChunkLine = (line: unknown): line is DailyReportIdsStreamChunkLine => {\n return typeof line === \"object\" && line !== null && Array.isArray((line as { items?: unknown }).items)\n}\n\n/**\n * Total order of the ids stream: businessDate DESC (NULL last), then id DESC.\n * ids ストリームの全順序。営業日降順 (NULL は末尾)、同日内は ID 降順。\n *\n * MSSQL の `ORDER BY business_date DESC, id DESC` と一致させることが唯一の存在理由。\n * 営業日は正規化済みの `YYYY-MM-DD` 文字列なので辞書順比較 = 時系列比較になる。\n *\n * @param a Left key. 左辺キー。\n * @param b Right key. 右辺キー。\n * @returns Negative when `a` streams before `b`. `a` が先に流れるなら負値。\n */\nexport const compareIdsStreamOrder = (a: DailyReportIdsStreamCursor, b: DailyReportIdsStreamCursor): number => {\n if (a.businessDate !== b.businessDate) {\n // null 営業日は並び順の末尾 (MSSQL DESC の NULL 末尾挙動へ揃える)\n if (a.businessDate === null) return 1\n if (b.businessDate === null) return -1\n return a.businessDate > b.businessDate ? -1 : 1\n }\n return b.reportHubId - a.reportHubId\n}\n\n/**\n * Checks whether items already follow the ids-stream total order.\n * アイテム配列が ids ストリームの全順序に既に従っているかを検査する処理。\n *\n * ストリームはソート順どおりに届いて末尾へ追記されるため通常は整列済みで、\n * この O(n) 検査で全件ソート (O(n log n)) を省略できる。\n *\n * @param items Items to probe. 検査対象のアイテム配列。\n * @returns True when already ordered. 整列済みなら true。\n */\nexport const isIdsStreamOrdered = (items: readonly DailyReportItem[]): boolean => {\n for (let i = 1; i < items.length; i++) {\n if (compareIdsStreamOrder(items[i - 1], items[i]) > 0) return false\n }\n return true\n}\n\n/**\n * Returns a copy with the item upserted at its ids-stream-ordered position.\n * アイテムを ids ストリーム順の正しい位置へ upsert した複製配列を返す処理。\n *\n * 前提: `items` は ids ストリーム順に整列済み (このモジュールの比較器を使う全書き込み元が\n * 維持する不変条件)。既存の同 ID は除去してから二分探索で挿入位置を求めるため、\n * 全件ソート無しの O(n) で並び順を保てる (以前は SSE 1 件ごとに 20 万件超を\n * localeCompare で全ソートしていた)。\n *\n * @param items Ordered items. 整列済みアイテム配列。\n * @param item Item to upsert. upsert するアイテム。\n * @returns New ordered array. 新しい整列済み配列。\n */\nexport const upsertInIdsStreamOrder = (items: readonly DailyReportItem[], item: DailyReportItem): DailyReportItem[] => {\n // 既存の同 ID を除いた複製 (位置移動を伴う更新もこれで単一経路になる)\n const next = items.filter((existing) => existing.reportHubId !== item.reportHubId)\n let low = 0\n let high = next.length\n while (low < high) {\n const mid = (low + high) >> 1\n if (compareIdsStreamOrder(next[mid], item) <= 0) {\n low = mid + 1\n } else {\n high = mid\n }\n }\n next.splice(low, 0, item)\n return next\n}\n\n/**\n * Finds the first index that streams strictly after the cursor.\n * カーソルより厳密に後ろへ並ぶ最初のインデックスを求める処理。\n *\n * 配列は必ずストリーム順 (SQL の ORDER BY) で渡されるが、比較器とのズレが\n * あっても壊れない線形走査で決定する (件数は高々数万・再開時のみの実行)。\n * 該当なし (カーソルが末尾以降) は `items.length` を返し、空チャンク +\n * `isLast: true` の正常終端に落ちる。\n *\n * @param items Stream-ordered items. ストリーム順のアイテム配列。\n * @param cursor Resume cursor. 再開カーソル。\n * @returns Index to resume emission from. 送出を再開するインデックス。\n */\nexport const findIdsStreamResumeIndex = (items: readonly DailyReportItem[], cursor: DailyReportIdsStreamCursor): number => {\n const index = items.findIndex((item) => compareIdsStreamOrder({ businessDate: item.businessDate, reportHubId: item.reportHubId }, cursor) > 0)\n return index === -1 ? items.length : index\n}\n","/**\n * Minimal level-filtered logger used internally by the daily-report package.\n * daily-report パッケージ内部で使う最小のレベルフィルタ付きロガー。\n *\n * 消費アプリのロガー実装 (console 互換の debug/info/warn/error) を注入でき、\n * 未注入時は console にフォールバックする。\n */\n\n/** 出力フィルタリング用のログレベル。 */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/** Console 互換のロガーインターフェース。 */\nexport interface DailyReportLogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * Creates a prefixed, level-filtered logger delegating to the given implementation.\n * 指定実装へ委譲するプレフィックス付き・レベルフィルタ付きロガーを生成する処理。\n */\nexport const createLogger = (level: LogLevel, prefix: string, impl: DailyReportLogger = console): DailyReportLogger => {\n // 文字列メッセージはプレフィックスを連結、それ以外は先頭引数として付加する\n const format = (message: unknown): unknown[] => (typeof message === \"string\" ? [`${prefix} ${message}`] : [prefix, message])\n return {\n debug: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.DEBUG) impl.debug(...format(message), ...rest)\n },\n info: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.INFO) impl.info(...format(message), ...rest)\n },\n warn: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.WARN) impl.warn(...format(message), ...rest)\n },\n error: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.ERROR) impl.error(...format(message), ...rest)\n },\n }\n}\n","/**\n * Zod schemas for daily-report SSE messages (client/server shared contract).\n * 日報 SSE メッセージの zod スキーマ (client / server 共有契約)。\n */\nimport { z } from \"zod\"\nimport type { DailyReportCommentItem as DeclaredComment, DailyReportDetail as DeclaredDetail } from \"./types\"\n\n// --- サブスキーマ(types.ts の型と構造的に一致させる) ---\n\nexport const dailyReportInterviewerSchema = z.object({\n name: z.string().nullish(),\n affiliation: z.string().nullish(),\n})\n\nexport const dailyReportCommentSchema = z.object({\n name: z.string().nullish(),\n text: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportLabelDefSchema = z.object({\n id: z.number(),\n name: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportCommentItemSchema = z.object({\n id: z.number(),\n userId: z.string().nullish(),\n userName: z.string().nullish(),\n content: z.string().nullish(),\n createdAt: z.string().nullish(),\n isMine: z.boolean(),\n})\n\n/**\n * 添付ファイル 1 件のスキーマ。\n *\n * 周囲のフィールドは `.nullish()` だが、ここは意図して `.nullable()` を使う。\n * `.nullish()` は推論キーを任意 (`?`) にするため、`types.ts` 側の必須 `fileType: string | null`\n * へ代入できず、ファイル末尾の `_AssertDetailCompat` が `never` に落ちてコンパイルエラーになる。\n * 既存の `.nullish()` フィールドはいずれも `types.ts` 側が `?` 付きの任意キーとペアになっており、\n * 「`.nullish()` ↔ 任意キー」「`.nullable()` ↔ 必須 `| null`」の対応で揃っている。\n * `| undefined` を混ぜないことは、詳細型を消費する DuckDB ステージ行の要請でもある。\n */\nexport const dailyReportAttachmentItemSchema = z.object({\n id: z.string(),\n fileName: z.string(),\n fileType: z.string().nullable(),\n fileSize: z.number().nullable(),\n createdAt: z.string().nullable(),\n state: z.enum([\"present\", \"absent\", \"unknown\"]),\n})\n\nexport const dailyReportDetailSchema = z.object({\n reportHubId: z.number(),\n date: z.string().nullish(),\n createdAt: z.string().nullish(),\n author: z.string().nullish(),\n userId: z.string().nullish(),\n sourceType: z.string().nullish(),\n employeeName: z.string().nullish(),\n updatedBy: z.string().nullish(),\n updatedAt: z.string().nullish(),\n category: z.string().nullish(),\n creationCategory: z.string().nullish(),\n visitTimeFrom: z.string().nullish(),\n visitTimeTo: z.string().nullish(),\n customerName: z.string().nullish(),\n interviewers: z.array(dailyReportInterviewerSchema),\n subject: z.string().nullish(),\n content: z.string().nullish(),\n comments: z.array(dailyReportCommentSchema),\n isRead: z.boolean(),\n isStarred: z.boolean(),\n labels: z.array(dailyReportLabelDefSchema),\n commentItems: z.array(dailyReportCommentItemSchema),\n // ❗ `.default([])` は必須。SSE の実体は永続 Redis Stream (MAXLEN 保持) であり、\n // 配備をまたいで**旧形式のエントリが残る**。共有リーダーはプロセス起動時に先頭から\n // 再生し、再接続クライアントの catch-up も同じエントリを読み直すため、\n // このキーを必須にすると添付機能を入れる前に publish された全エントリが\n // fail-closed で捨てられ、再接続タブへ更新が永久に届かなくなる。\n // 送信側の詰め忘れ検知は types.ts の必須フィールドと `_AssertDetailCompat`\n // (出力型は既定値適用後なので必須のまま) が担っており、ここを緩めても失われない。\n attachments: z.array(dailyReportAttachmentItemSchema).default([]),\n})\n\n// --- SSE メッセージスキーマ(8種) ---\n\nexport const connectedMessageSchema = z.object({\n type: z.literal(\"connected\"),\n})\n\nexport const statusUpdateMessageSchema = z.object({\n type: z.literal(\"status-update\"),\n reportHubId: z.number(),\n statusType: z.enum([\"star\", \"read\"]),\n value: z.boolean(),\n clientTempId: z.string(),\n // recipientRawUserId はサーバーサイドフィルタリング専用。\n // SSE ルートが raw JSON から取得してフィルタ後に除去する。\n // クライアントには到達しないが、サーバー側の publish で .parse() を通すため定義が必要。\n recipientRawUserId: z.number().optional(),\n})\n\nexport const commentAddMessageSchema = z.object({\n type: z.literal(\"comment-add\"),\n reportHubId: z.number(),\n comment: dailyReportCommentItemSchema,\n clientTempId: z.string(),\n // 下書き日報のコメントは所有者以外に見えてはならない (本文の全員配信を防ぐ)。\n // サーバーサイドフィルタ専用で、SSE ルートがフィルタ後に除去する\n recipientRawUserId: z.number().optional(),\n})\n\nexport const commentDeleteMessageSchema = z.object({\n type: z.literal(\"comment-delete\"),\n reportHubId: z.number(),\n commentId: z.number(),\n clientTempId: z.string(),\n // comment-add と同じ理由の宛先フィルタ\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportCreateMessageSchema = z.object({\n type: z.literal(\"report-create\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportUpdateMessageSchema = z.object({\n type: z.literal(\"report-update\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportPublishMessageSchema = z.object({\n type: z.literal(\"report-publish\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportDeleteMessageSchema = z.object({\n type: z.literal(\"report-delete\"),\n reportHubId: z.number(),\n clientTempId: z.string(),\n})\n\n// --- Discriminated Union ---\n\nexport const dailyReportSseMessageSchema = z.discriminatedUnion(\"type\", [\n connectedMessageSchema,\n statusUpdateMessageSchema,\n commentAddMessageSchema,\n commentDeleteMessageSchema,\n reportCreateMessageSchema,\n reportUpdateMessageSchema,\n reportPublishMessageSchema,\n reportDeleteMessageSchema,\n])\n\n// --- 型エクスポート ---\n\nexport type DailyReportSseMessage = z.infer<typeof dailyReportSseMessageSchema>\n\n// --- コンパイル時の型互換チェック ---\n// Zod推論型が既存の型定義に代入可能であることを保証する。\n// ここでエラーが出たらスキーマと types.ts がずれている。\n\ntype _AssertDetailCompat = z.infer<typeof dailyReportDetailSchema> extends DeclaredDetail ? true : never\ntype _AssertCommentCompat = z.infer<typeof dailyReportCommentItemSchema> extends DeclaredComment ? true : never\nconst _detailCheck: _AssertDetailCompat = true\nconst _commentCheck: _AssertCommentCompat = true\nvoid _detailCheck\nvoid _commentCheck\n","/**\n * Shared text helpers for daily report view components.\n * 日報ビューコンポーネント向けの共通テキストヘルパー。\n */\n\n/**\n * Normalizes nullable text by trimming whitespace and applying fallback.\n * null 許容の文字列をトリムしつつフォールバック値を適用する関数。\n */\nexport const fallbackText = (value: string | null | undefined): string => {\n if (!value) {\n return \"-\"\n }\n const trimmed = value.trim()\n return trimmed === \"\" ? \"-\" : trimmed\n}\n\n/**\n * Formats a date string to ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).\n * 日付文字列を ISO 8601 形式 (YYYY-MM-DD または YYYY-MM-DDTHH:mm:ss) に整形する関数。\n */\nexport const formatToIsoDate = (value: unknown): string => {\n if (!value) {\n return \"-\"\n }\n if (typeof value !== \"string\") {\n // If value is not a string (e.g. Date object), try to convert it to string or handle it\n if (value instanceof Date) {\n return formatToIsoDate(value.toISOString())\n }\n return String(value)\n }\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return \"-\"\n }\n\n // 日付のみ (YYYY-MM-DD / YYYY/MM/DD) は Date を経由せず整形する。\n // `new Date(\"2026-08-08\")` は UTC 深夜と解釈され、ローカル getter で負オフセット圏\n // (米国など) では前日へずれるため。\n const dateOnly = trimmed.match(/^(\\d{4})[-/](\\d{2})[-/](\\d{2})$/)\n if (dateOnly) {\n return `${dateOnly[1]}-${dateOnly[2]}-${dateOnly[3]}`\n }\n\n const date = new Date(trimmed)\n if (Number.isNaN(date.getTime())) {\n return trimmed\n }\n\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, \"0\")\n const day = String(date.getDate()).padStart(2, \"0\")\n\n const hasTime = trimmed.includes(\":\") || trimmed.includes(\"T\")\n\n if (hasTime) {\n const hours = String(date.getHours()).padStart(2, \"0\")\n const minutes = String(date.getMinutes()).padStart(2, \"0\")\n const seconds = String(date.getSeconds()).padStart(2, \"0\")\n return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`\n }\n\n return `${year}-${month}-${day}`\n}\n"],"mappings":"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","names":["ISO_DATE_PATTERN","SLASH_DATE_PATTERN","zeroPad","value","normalizeFromDate","year","month","day","resolveTodayBusinessDateKey","now","normalizeBusinessDateKey","trimmed","parsed","formatBusinessDateDisplay","normalized","parseBusinessDateKeyToDate","part","component","mergeComments","legacyComments","modernComments","currentUserId","legacyUIComments","c","index","modernUIComments","resolveCommentColorClass","color","normalized","isIdsStreamChunkLine","line","compareIdsStreamOrder","a","b","isIdsStreamOrdered","items","i","upsertInIdsStreamOrder","item","next","existing","low","high","mid","findIdsStreamResumeIndex","cursor","index","LogLevel","createLogger","level","prefix","impl","format","message","rest","z","dailyReportInterviewerSchema","dailyReportCommentSchema","dailyReportLabelDefSchema","dailyReportCommentItemSchema","dailyReportAttachmentItemSchema","dailyReportDetailSchema","connectedMessageSchema","statusUpdateMessageSchema","commentAddMessageSchema","commentDeleteMessageSchema","reportCreateMessageSchema","reportUpdateMessageSchema","reportPublishMessageSchema","reportDeleteMessageSchema","dailyReportSseMessageSchema","fallbackText","value","trimmed","formatToIsoDate","dateOnly","date","year","month","day","hours","minutes","seconds"]}
package/dist/server.d.mts CHANGED
@@ -1613,6 +1613,7 @@ declare function createDailyReportService(config: DailyReportServiceConfig): {
1613
1613
  invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
1614
1614
  getUserIdByExternalId: (externalId: string) => Promise<number | null>;
1615
1615
  getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
1616
+ getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number) => Promise<DailyReportItem[]>;
1616
1617
  getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1617
1618
  getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1618
1619
  getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
@@ -1822,6 +1823,13 @@ type DailyReportHandlersConfig = {
1822
1823
  streamKey: string;
1823
1824
  /** 未ログイン時のリダイレクト先 (index.loader 用、既定 "/auth/login")。 */
1824
1825
  loginRedirectPath?: string;
1826
+ /**
1827
+ * `intent=clearCache` (全サーバーキャッシュの一括破棄) を許可するか。既定 false。
1828
+ * 開発用アフォーダンスであり、有効化すると認証済みユーザーなら誰でも全ワーカーの
1829
+ * キャッシュを無制限に破棄できるため、本番では必ず無効のままにする
1830
+ * (消費アプリは開発環境でのみ true を注入する)。
1831
+ */
1832
+ enableDevCacheClear?: boolean;
1825
1833
  /** ロガー (既定は console ベース)。 */
1826
1834
  logger?: DailyReportLogger;
1827
1835
  };
@@ -1878,6 +1886,12 @@ type DailyReportServerConfig = Omit<DailyReportServiceConfig, "cache" | "epochs"
1878
1886
  attachmentConcurrency?: number;
1879
1887
  /** 未ログイン時のリダイレクト先 (index.loader 用、既定 "/auth/login")。 */
1880
1888
  loginRedirectPath?: string;
1889
+ /**
1890
+ * `intent=clearCache` (全サーバーキャッシュの一括破棄) を許可するか。既定 false。
1891
+ * 開発用アフォーダンス。有効化すると認証済みなら誰でも全ワーカーのキャッシュを
1892
+ * 無制限破棄できるため、本番では無効のままにする (消費アプリは開発環境のみ true 注入)。
1893
+ */
1894
+ enableDevCacheClear?: boolean;
1881
1895
  /** SQL 結果キャッシュの既定 TTL (既定 60,000ms)。 */
1882
1896
  cacheDefaultTtlMs?: number;
1883
1897
  /** 外部ソースアダプタ群 (レガシー日報テーブル等)。 */
@@ -1939,6 +1953,7 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
1939
1953
  invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
1940
1954
  getUserIdByExternalId: (externalId: string) => Promise<number | null>;
1941
1955
  getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
1956
+ getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number) => Promise<DailyReportItem[]>;
1942
1957
  getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1943
1958
  getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1944
1959
  getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
package/dist/server.d.ts CHANGED
@@ -1613,6 +1613,7 @@ declare function createDailyReportService(config: DailyReportServiceConfig): {
1613
1613
  invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
1614
1614
  getUserIdByExternalId: (externalId: string) => Promise<number | null>;
1615
1615
  getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
1616
+ getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number) => Promise<DailyReportItem[]>;
1616
1617
  getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1617
1618
  getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1618
1619
  getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
@@ -1822,6 +1823,13 @@ type DailyReportHandlersConfig = {
1822
1823
  streamKey: string;
1823
1824
  /** 未ログイン時のリダイレクト先 (index.loader 用、既定 "/auth/login")。 */
1824
1825
  loginRedirectPath?: string;
1826
+ /**
1827
+ * `intent=clearCache` (全サーバーキャッシュの一括破棄) を許可するか。既定 false。
1828
+ * 開発用アフォーダンスであり、有効化すると認証済みユーザーなら誰でも全ワーカーの
1829
+ * キャッシュを無制限に破棄できるため、本番では必ず無効のままにする
1830
+ * (消費アプリは開発環境でのみ true を注入する)。
1831
+ */
1832
+ enableDevCacheClear?: boolean;
1825
1833
  /** ロガー (既定は console ベース)。 */
1826
1834
  logger?: DailyReportLogger;
1827
1835
  };
@@ -1878,6 +1886,12 @@ type DailyReportServerConfig = Omit<DailyReportServiceConfig, "cache" | "epochs"
1878
1886
  attachmentConcurrency?: number;
1879
1887
  /** 未ログイン時のリダイレクト先 (index.loader 用、既定 "/auth/login")。 */
1880
1888
  loginRedirectPath?: string;
1889
+ /**
1890
+ * `intent=clearCache` (全サーバーキャッシュの一括破棄) を許可するか。既定 false。
1891
+ * 開発用アフォーダンス。有効化すると認証済みなら誰でも全ワーカーのキャッシュを
1892
+ * 無制限破棄できるため、本番では無効のままにする (消費アプリは開発環境のみ true 注入)。
1893
+ */
1894
+ enableDevCacheClear?: boolean;
1881
1895
  /** SQL 結果キャッシュの既定 TTL (既定 60,000ms)。 */
1882
1896
  cacheDefaultTtlMs?: number;
1883
1897
  /** 外部ソースアダプタ群 (レガシー日報テーブル等)。 */
@@ -1939,6 +1953,7 @@ declare function createDailyReportServer(config: DailyReportServerConfig): {
1939
1953
  invalidateHubReadCaches: (hubIds: readonly number[], businessDates: readonly string[]) => Promise<void>;
1940
1954
  getUserIdByExternalId: (externalId: string) => Promise<number | null>;
1941
1955
  getDailyReportIdsByExternalId: (externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportItem[]>;
1956
+ getDailyReportIdsFirstPageByExternalId: (externalId: string, limit: number) => Promise<DailyReportItem[]>;
1942
1957
  getDailyReportsByBusinessDate: (businessDate: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1943
1958
  getDailyReportsByBusinessDateByExternalId: (businessDate: string, externalId: string, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail[]>;
1944
1959
  getDailyReportDetailById: (reportHubId: number, userId: number, { forceRefresh, snapshot, ttlMsOverride }?: SqlResultCacheQueryOptions) => Promise<DailyReportDetail | null>;
package/dist/server.js CHANGED
@@ -1,10 +1,13 @@
1
- "use strict";var Ne=Object.defineProperty;var pt=Object.getOwnPropertyDescriptor;var yt=Object.getOwnPropertyNames;var ht=Object.prototype.hasOwnProperty;var ft=(r,e)=>{for(var o in e)Ne(r,o,{get:e[o],enumerable:!0})},gt=(r,e,o,l)=>{if(e&&typeof e=="object"||typeof e=="function")for(let p of yt(e))!ht.call(r,p)&&p!==o&&Ne(r,p,{get:()=>e[p],enumerable:!(l=pt(e,p))||l.enumerable});return r};var bt=r=>gt(Ne({},"__esModule",{value:!0}),r);var Jt={};ft(Jt,{DailyReportSseReader:()=>De,SqlResultCache:()=>Re,asciiFallbackFileName:()=>Ze,createDailyReportHandlers:()=>Oe,createDailyReportServer:()=>jt,createDailyReportService:()=>ze,createEpochStore:()=>xe,defineDailyReportAuthzResources:()=>et,defineDailyReportSchema:()=>Kt,encodeRfc8187:()=>Ve,generateETag:()=>ke,isStreamIdLte:()=>we,jsonResponseWithETag:()=>ne,sanitizeMediaType:()=>Ye,seedDailyReportAuthzResources:()=>Ot,transformJsonArray:()=>Ft});module.exports=bt(Jt);var xe=r=>({async getEpoch(e){try{let o=await r?.getClient();if(!o)return 0;let l=await o.get(e);return l?Number(l):0}catch{return 0}},async incrementEpoch(e){try{let o=await r?.getClient();if(!o)return;await o.incr(e)}catch{}}}),Re=class{constructor(e,o){this.config=e;this.epochStore=o;this.buckets=new Map;this.inFlight=new Map;this.invalidate=e=>{this.buckets.delete(e),this.inFlight.delete(e)};this.getOrFetch=async e=>{let{cacheKey:o,forceRefresh:l,snapshot:p}=e,y=this.buckets.get(o);if(l&&(this.buckets.delete(o),this.inFlight.delete(o)),!l&&y&&y.expireAt>Date.now())if(e.epochKey){let h=await this.epochStore.getEpoch(e.epochKey);if(y.epoch===h)return p?y.records.map(O=>structuredClone(O)):y.records;this.buckets.delete(o)}else return p?y.records.map(h=>structuredClone(h)):y.records;if(!l&&this.inFlight.has(o)){let h=await this.inFlight.get(o);return p?h.map(O=>structuredClone(O)):h}let L;L=(async()=>{let h=await e.fetcher();if(l||this.inFlight.get(o)===L){let O=Date.now()+(e.ttlMsOverride??this.config.defaultTtlMs),c=e.epochKey?await this.epochStore.getEpoch(e.epochKey):0;this.buckets.set(o,{records:h,expireAt:O,epoch:c}),this.buckets.size>500&&this.cleanExpired()}return h})(),l||this.inFlight.set(o,L);try{let h=await L;return p?h.map(O=>structuredClone(O)):h}finally{this.inFlight.delete(o)}};this.flush=e=>{this.buckets.delete(e),this.inFlight.delete(e)};this.clearAll=()=>{this.buckets.clear(),this.inFlight.clear()};this.invalidatePrefix=e=>{for(let o of this.buckets.keys())o.startsWith(e)&&this.buckets.delete(o);for(let o of this.inFlight.keys())o.startsWith(e)&&this.inFlight.delete(o)}}cleanExpired(){let e=Date.now();for(let[o,l]of this.buckets.entries())l.expireAt<=e&&this.buckets.delete(o)}};var Rt=/^\d{4}-\d{2}-\d{2}$/,Dt=/^\d{4}\/\d{2}\/\d{2}$/,je=r=>r<10?`0${r}`:`${r}`,Je=r=>{if(Number.isNaN(r.getTime()))return null;let e=r.getFullYear(),o=je(r.getMonth()+1),l=je(r.getDate());return`${e}-${o}-${l}`},ee=r=>{if(r==null)return null;if(r instanceof Date)return Je(r);let e=r.trim();if(e==="")return null;if(Rt.test(e))return e;if(Dt.test(e))return e.replaceAll("/","-");let o=new Date(e);return Je(o)};var re=(r,e,o=console)=>{let l=p=>typeof p=="string"?[`${e} ${p}`]:[e,p];return{debug:(p,...y)=>{r<=0&&o.debug(...l(p),...y)},info:(p,...y)=>{r<=1&&o.info(...l(p),...y)},warn:(p,...y)=>{r<=2&&o.warn(...l(p),...y)},error:(p,...y)=>{r<=3&&o.error(...l(p),...y)}}};var d=require("zod"),wt=d.z.object({name:d.z.string().nullish(),affiliation:d.z.string().nullish()}),St=d.z.object({name:d.z.string().nullish(),text:d.z.string().nullish(),color:d.z.string().nullish()}),It=d.z.object({id:d.z.number(),name:d.z.string().nullish(),color:d.z.string().nullish()}),Qe=d.z.object({id:d.z.number(),userId:d.z.string().nullish(),userName:d.z.string().nullish(),content:d.z.string().nullish(),createdAt:d.z.string().nullish(),isMine:d.z.boolean()}),At=d.z.object({id:d.z.string(),fileName:d.z.string(),fileType:d.z.string().nullable(),fileSize:d.z.number().nullable(),createdAt:d.z.string().nullable(),state:d.z.enum(["present","absent","unknown"])}),Le=d.z.object({reportHubId:d.z.number(),date:d.z.string().nullish(),createdAt:d.z.string().nullish(),author:d.z.string().nullish(),userId:d.z.string().nullish(),sourceType:d.z.string().nullish(),employeeName:d.z.string().nullish(),updatedBy:d.z.string().nullish(),updatedAt:d.z.string().nullish(),category:d.z.string().nullish(),creationCategory:d.z.string().nullish(),visitTimeFrom:d.z.string().nullish(),visitTimeTo:d.z.string().nullish(),customerName:d.z.string().nullish(),interviewers:d.z.array(wt),subject:d.z.string().nullish(),content:d.z.string().nullish(),comments:d.z.array(St),isRead:d.z.boolean(),isStarred:d.z.boolean(),labels:d.z.array(It),commentItems:d.z.array(Qe),attachments:d.z.array(At).default([])}),_t=d.z.object({type:d.z.literal("connected")}),Me=d.z.object({type:d.z.literal("status-update"),reportHubId:d.z.number(),statusType:d.z.enum(["star","read"]),value:d.z.boolean(),clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),$e=d.z.object({type:d.z.literal("comment-add"),reportHubId:d.z.number(),comment:Qe,clientTempId:d.z.string()}),Pe=d.z.object({type:d.z.literal("comment-delete"),reportHubId:d.z.number(),commentId:d.z.number(),clientTempId:d.z.string()}),Be=d.z.object({type:d.z.literal("report-create"),reportHubId:d.z.number(),report:Le,clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),Ue=d.z.object({type:d.z.literal("report-update"),reportHubId:d.z.number(),report:Le,clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),He=d.z.object({type:d.z.literal("report-publish"),reportHubId:d.z.number(),report:Le,clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),qe=d.z.object({type:d.z.literal("report-delete"),reportHubId:d.z.number(),clientTempId:d.z.string()}),Ge=d.z.discriminatedUnion("type",[_t,Me,$e,Pe,Be,Ue,He,qe]);var Xe=require("crypto"),ke=r=>{let e=JSON.stringify(r);return`"${(0,Xe.createHash)("sha256").update(e).digest("hex")}"`};var Ct=re(1,"[Response]"),ne=(r,e,o,l=200,p=Ct)=>{let y=ke(o),L=r.headers.get("If-None-Match");p.info(`[jsonResponseWithETag] ETag: ${y}, If-None-Match: ${L}`);let h=new Headers({"Content-Type":"application/json","Cache-Control":"private, max-age=0, must-revalidate","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'none'",ETag:y});return e&&h.append("Set-Cookie",e),r.method==="GET"&&l===200&&L===y?new Response(null,{status:304,headers:h}):new Response(JSON.stringify(o),{status:l,headers:h})};var We=require("events");var we=(r,e)=>{let[o,l]=r.split("-").map(Number),[p,y]=e.split("-").map(Number);return o!==p?o<p:l<=y},De=class{constructor(e){this.config=e;this._emitter=new We.EventEmitter;this._state="idle";this._refCount=0;this._lastId="0-0";this._emitter.setMaxListeners(0),this._logger=e.logger??re(1,"[SSE Reader]")}subscribe(e,o){return this._emitter.on("entry",e),o&&this._emitter.on("error",o),this._refCount++,this._refCount===1&&this._startLoop(),()=>{this._emitter.removeListener("entry",e),o&&this._emitter.removeListener("error",o),this._refCount--,this._refCount<=0&&(this._refCount=0,this._stopLoop())}}async destroy(){this._refCount=0,await this._stopLoop(),this._emitter.removeAllListeners(),this._lastId="0-0"}async _startLoop(){for(;this._state==="stopping";)await new Promise(e=>setTimeout(e,50));if(this._state!=="running"){this._state="running";try{this._client=await this.config.redis?.createClient()}catch(e){this._state="idle",this._emitter.emit("error",e instanceof Error?e:new Error("Failed to create Redis client for SSE reader"));return}if(!this._client){this._state="idle",this._emitter.emit("error",new Error("Failed to create Redis client for SSE reader"));return}this._client.on("error",e=>{this._logger.error("Redis client error:",e)});try{for(;this._state==="running"&&this._client?.isOpen;){let e=await this._client.xRead([{key:this.config.streamKey,id:this._lastId}],{BLOCK:5e3,COUNT:100});if(e)for(let o of e)for(let l of o.messages){this._lastId=l.id;try{let p=l.message?.data?JSON.parse(l.message.data):null,y=p?.type;if((y==="report-create"||y==="report-delete"||y==="report-publish")&&this.config.cache.invalidatePrefix("daily-report:ids"),y==="comment-add"||y==="comment-delete"||y==="status-update"){let L=p?.reportHubId;typeof L=="number"&&(this.config.cache.invalidate(`daily-report:detail:${L}`),this.config.cache.invalidatePrefix(`daily-report:detail:${L}:user:`))}}catch{}this._emitter.emit("entry",{id:l.id,message:l.message})}}}catch(e){!(e?.constructor?.name==="ClientClosedError")&&this._state==="running"&&(this._logger.error("xRead loop error:",e),this._emitter.emit("error",e))}finally{if(this._client?.isOpen)try{await this._client.quit()}catch{}this._client=void 0,this._state="idle",this._refCount>0&&this._startLoop()}}}async _stopLoop(){if(this._state==="running"){if(this._state="stopping",this._client?.isOpen)try{await this._client.quit()}catch{}for(;this._state!=="idle";)await new Promise(e=>setTimeout(e,50))}}};var x=(r,e)=>new Response(JSON.stringify(r),{status:e?.status??200,headers:{"Content-Type":"application/json"}}),Tt=32*1024*1024,Mt=60,kt=4,vt=12e4,Et=1024,Nt=60,xt=5,Lt=new Set(["image/png","image/jpeg","image/gif","image/webp","application/pdf","text/plain"]),$t=/^[\x20-\x7E]+$/,Pt=/^[\w.+-]+\/[\w.+-]+/,Ye=r=>r&&$t.test(r)&&Pt.test(r)?r:null,Ve=r=>encodeURIComponent(r).replace(/['()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`),Ze=r=>r.replace(/[^\x20-\x7E]/g,"_").replace(/["\\]/g,"_"),Bt=r=>{switch(r){case"not_found":case"denied":case"invalid_path":return 404;case"too_large":return 413;default:return 502}},Ut={404:"Attachment not found",413:"Attachment too large",502:"Attachment storage unavailable"},Ht=r=>{let e=new Map;return(o,l)=>{let p=e.get(o);p&&e.delete(o);let y=p??{tokens:r,lastRefillMs:l},L=Math.max(0,l-y.lastRefillMs);L>0&&(y.tokens=Math.min(r,y.tokens+L*r/6e4),y.lastRefillMs=l);let h=y.tokens>=1;for(h&&(y.tokens-=1),e.set(o,y);e.size>Et;){let O=e.keys().next();if(O.done)break;e.delete(O.value)}return h}},qt=r=>{let e=0;return{tryAcquire:()=>e>=r?!1:(e+=1,!0),release:()=>{e>0&&(e-=1)}}},Yt=(r,e,o)=>{let l=setTimeout(e,o);l.unref?.();let p=()=>{clearTimeout(l),e()},y=!1;return new ReadableStream({pull(L){if(y){L.close(),p();return}y=!0,L.enqueue(r)},cancel(){p()}})};function Oe(r){let{authenticate:e,service:o,encodeUserId:l,redis:p,sseReader:y,streamKey:L}=r,h=r.loginRedirectPath??"/auth/login",O=r.logger??re(3,"[DailyReportAPI]"),c=r.logger??re(1,"[DailyReportSSE]"),$=r.logger??re(1,"[DailyReportAttachment]"),m=r.attachmentMaxBytes??Tt,K=Ht(r.attachmentRateLimitPerMinute??Mt),j=qt(r.attachmentConcurrency??kt),B=async({request:M})=>{let{user:U,cookie:v}=await e(M,{failureRedirect:h}),k=new Headers;v&&k.append("Set-Cookie",v);let b=null;U&&(b=await o.getUserIdByExternalId(U.id));let S=b?l(b):null;return{data:{user:U,userId:S},headers:k}},he={"business-date":async(M,U,v,k)=>{let b=ee(M.searchParams.get("businessDate")),S=M.searchParams.get("forceRefresh")==="true";if(!b)return ne(v,U,{error:{message:"Invalid business date"}},400);let H=await o.getDailyReportsByBusinessDateByExternalId(b,k.id,{forceRefresh:S});return ne(v,U,{businessDate:b,reports:H},200)},ids:async(M,U,v,k)=>{let b=M.searchParams.get("forceRefresh")==="true",S=await o.getDailyReportIdsByExternalId(k.id,{forceRefresh:b});return ne(v,U,{ids:S},200)},report:async(M,U,v,k)=>{let b=M.searchParams.get("reportHubId"),S=M.searchParams.get("forceRefresh")==="true",H=b?Number.parseInt(b,10):NaN;if(!Number.isFinite(H)||H<=0)return ne(v,U,{error:{message:"Invalid reportHubId"}},400);let E=await o.getDailyReportDetailByIdByExternalId(H,k.id,{snapshot:!0,forceRefresh:S});return E?ne(v,U,{report:E},200):ne(v,U,{error:{message:"Report not found"}},404)}};return{index:{loader:B},api:{loader:async({request:M,params:U})=>{let{user:v,cookie:k}=await e(M,{failureRedirect:null}),b=k??null;if(!v)return ne(M,b,{error:{message:"Unauthorized"}},401);if(M.method!=="GET")return ne(M,b,{error:{message:"Method not allowed"}},405);let S=U.endpoint??"",H=he[S];if(!H)return ne(M,b,{error:{message:"Unknown endpoint"}},404);let E=Date.now();try{let R=new URL(M.url);return await H(R,b,M,v)}catch(R){let A=Date.now()-E,_=R instanceof Error?R:new Error(String(R));return O.error(`500 endpoint=${S} elapsed=${A}ms name=${_.name} code=${"code"in _?_.code:"N/A"} message=${_.message}`),O.error("Stack:",_.stack),ne(M,b,{error:{message:"Internal Server Error"}},500)}},action:async({request:M,params:U})=>{let{user:v}=await e(M,{failureRedirect:null});if(!v)return x({error:"Unauthorized"},{status:401});if(U.endpoint!=="action")return x({error:"Unknown endpoint"},{status:404});let b=await o.getUserIdByExternalId(v.id);if(!b)return x({error:"User not found"},{status:404});let S=await M.formData(),H=S.get("intent"),E=S.get("reportHubId"),R=E?Number(E):NaN,A=S.get("businessDate"),_=Number(S.get("operationTimestamp")),T=S.get("clientTempId");if(H==="clearCache")return await o.clearCache(),x({status:"OK",intent:"clearCache"});if(!T)return x({error:"clientTempId required"},{status:400});if(H!=="create"&&(!R||Number.isNaN(R)))return x({error:"Invalid reportHubId"},{status:400});switch(H){case"create":{if(!A)return x({error:"businessDate required"},{status:400});let w=await o.createDailyReport(b,A,T);return x({status:"OK",intent:"create",report:w,reportHubId:String(w.reportHubId),clientTempId:T})}case"update":{let w=S.get("title"),Y=S.get("content");try{return await o.updateDailyReport(R,b,{title:w,content:Y},T),x({status:"OK",intent:"update",reportHubId:String(R),clientTempId:T})}catch(q){if(q instanceof Error){if(q.message==="Unauthorized")return x({error:"Unauthorized"},{status:403});if(q.message==="Not Found")return x({error:"Report not found"},{status:404})}throw q}}case"publish":try{let w=await o.publishDailyReport(R,b,T);return x({status:"OK",intent:"publish",reportHubId:String(R),clientTempId:T,report:w})}catch(w){if(w instanceof Error){if(w.message==="Unauthorized")return x({error:"Unauthorized"},{status:403});if(w.message==="Not Found")return x({error:"Report not found"},{status:404})}throw w}case"delete":try{return await o.deleteDailyReport(R,b,T),x({status:"OK",intent:"delete",reportHubId:String(R),clientTempId:T})}catch(w){if(w instanceof Error){if(w.message==="Unauthorized")return x({error:"Unauthorized"},{status:403});if(w.message==="Not Found")return x({error:"Report not found"},{status:404})}throw w}case"toggleStar":{let w=S.get("isStarred");if(w===null)return x({error:"isStarred required"},{status:400});let Y=w==="true",q=await o.setStarStatus(b,R,A,Y,T);return x({status:"OK",intent:"toggleStar",updatedStatus:{isStarred:q.isStarred,isRead:q.isRead},operationTimestamp:_,reportHubId:String(R),clientTempId:T})}case"toggleRead":{let w=S.get("isRead");if(w===null)return x({error:"isRead required"},{status:400});let Y=w==="true",q=await o.setReadStatus(b,R,A,Y,T);return x({status:"OK",intent:"toggleRead",updatedStatus:{isStarred:q.isStarred,isRead:q.isRead},operationTimestamp:_,reportHubId:String(R),clientTempId:T})}case"addComment":{let w=S.get("content");if(!w)return x({error:"Content required"},{status:400});let Y=await o.addComment(b,R,w,A,T),q={...Y,userId:Y.userId};return x({status:"OK",intent:"addComment",newComment:q,reportHubId:String(R),clientTempId:T})}case"deleteComment":{let w=Number(S.get("commentId"));if(!w||Number.isNaN(w))return x({error:"Invalid commentId"},{status:400});try{return await o.deleteComment(b,R,w,A,T),x({status:"OK",reportHubId:String(R),deletedCommentId:String(w),clientTempId:T})}catch(Y){if(Y instanceof Error){if(Y.message==="Unauthorized")return x({error:"Unauthorized"},{status:403});if(Y.message==="Not Found")return x({error:"Comment not found"},{status:404})}throw Y}}default:return x({error:"Invalid intent"},{status:400})}}},sse:{loader:async({request:M,params:U})=>{let{user:v}=await e(M,{failureRedirect:null});if(!v)return new Response("Unauthorized",{status:401});let k=await o.getUserIdByExternalId(v.id);if(!k)return new Response("Forbidden",{status:403});if(U.endpoint!=="updates")return new Response("Not Found",{status:404});let S=new URL(M.url).searchParams.get("lastEventId")||M.headers.get("Last-Event-ID"),H=new TextEncoder,E=null,R=null,A=null,_=!1,T=()=>{if(!_){if(_=!0,R&&(clearInterval(R),R=null),A&&(A(),A=null),E)try{E.desiredSize!==null&&E.close()}catch{}finally{E=null}_=!1}},w=(q,Q)=>{if(Q.data)try{let I=JSON.parse(Q.data),X=Ge.safeParse(I);if(!X.success){c.error("SSE message validation failed:",X.error.format());return}let V=X.data,ae=typeof I.recipientRawUserId=="number"?I.recipientRawUserId:void 0;if(V.type==="status-update"){if(ae===void 0||ae!==k)return}else if(ae!==void 0&&ae!==k)return;let de;if("recipientRawUserId"in I){let{recipientRawUserId:ge,...oe}=I;de=JSON.stringify(oe)}else de=Q.data;E&&E.desiredSize!==null&&E.enqueue(H.encode(`id: ${q}
2
- data: ${de}
1
+ "use strict";var Le=Object.defineProperty;var wt=Object.getOwnPropertyDescriptor;var It=Object.getOwnPropertyNames;var St=Object.prototype.hasOwnProperty;var At=(t,e)=>{for(var s in e)Le(t,s,{get:e[s],enumerable:!0})},Ct=(t,e,s,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let y of It(e))!St.call(t,y)&&y!==s&&Le(t,y,{get:()=>e[y],enumerable:!(i=wt(e,y))||i.enumerable});return t};var _t=t=>Ct(Le({},"__esModule",{value:!0}),t);var or={};At(or,{DailyReportSseReader:()=>De,SqlResultCache:()=>Re,asciiFallbackFileName:()=>it,createDailyReportHandlers:()=>ze,createDailyReportServer:()=>sr,createDailyReportService:()=>Ke,createEpochStore:()=>$e,defineDailyReportAuthzResources:()=>lt,defineDailyReportSchema:()=>ar,encodeRfc8187:()=>ot,generateETag:()=>Ee,isStreamIdLte:()=>we,jsonResponseWithETag:()=>ae,sanitizeMediaType:()=>Fe,seedDailyReportAuthzResources:()=>tr,transformJsonArray:()=>nr});module.exports=_t(or);var $e=t=>({async getEpoch(e){try{let s=await t?.getClient();if(!s)return 0;let i=await s.get(e);return i?Number(i):0}catch{return 0}},async incrementEpoch(e){try{let s=await t?.getClient();if(!s)return;await s.incr(e)}catch{}}}),Re=class{constructor(e,s){this.config=e;this.epochStore=s;this.buckets=new Map;this.inFlight=new Map;this.invalidate=e=>{this.buckets.delete(e),this.inFlight.delete(e)};this.getOrFetch=async e=>{let{cacheKey:s,forceRefresh:i,snapshot:y}=e,m=this.buckets.get(s);if(i&&(this.buckets.delete(s),this.inFlight.delete(s)),!i&&m&&m.expireAt>Date.now())if(e.epochKey){let h=await this.epochStore.getEpoch(e.epochKey);if(m.epoch===h)return y?m.records.map(O=>structuredClone(O)):m.records;this.buckets.delete(s)}else return y?m.records.map(h=>structuredClone(h)):m.records;if(!i&&this.inFlight.has(s)){let h=await this.inFlight.get(s);return y?h.map(O=>structuredClone(O)):h}let M;M=(async()=>{let h=await e.fetcher();if(i||this.inFlight.get(s)===M){let O=Date.now()+(e.ttlMsOverride??this.config.defaultTtlMs),u=e.epochKey?await this.epochStore.getEpoch(e.epochKey):0;this.buckets.set(s,{records:h,expireAt:O,epoch:u}),this.buckets.size>500&&this.cleanExpired()}return h})(),i||this.inFlight.set(s,M);try{let h=await M;return y?h.map(O=>structuredClone(O)):h}finally{this.inFlight.delete(s)}};this.flush=e=>{this.buckets.delete(e),this.inFlight.delete(e)};this.clearAll=()=>{this.buckets.clear(),this.inFlight.clear()};this.invalidatePrefix=e=>{for(let s of this.buckets.keys())s.startsWith(e)&&this.buckets.delete(s);for(let s of this.inFlight.keys())s.startsWith(e)&&this.inFlight.delete(s)}}cleanExpired(){let e=Date.now();for(let[s,i]of this.buckets.entries())i.expireAt<=e&&this.buckets.delete(s)}};var Tt=/^\d{4}-\d{2}-\d{2}$/,Mt=/^\d{4}\/\d{2}\/\d{2}$/,Qe=t=>t<10?`0${t}`:`${t}`,Xe=t=>{if(Number.isNaN(t.getTime()))return null;let e=t.getFullYear(),s=Qe(t.getMonth()+1),i=Qe(t.getDate());return`${e}-${s}-${i}`};var ee=t=>{if(t==null)return null;if(t instanceof Date)return Xe(t);let e=t.trim();if(e==="")return null;if(Tt.test(e))return e;if(Mt.test(e))return e.replaceAll("/","-");let s=new Date(e);return Xe(s)};var Et=(t,e)=>t.businessDate!==e.businessDate?t.businessDate===null?1:e.businessDate===null||t.businessDate>e.businessDate?-1:1:e.reportHubId-t.reportHubId;var We=(t,e)=>{let s=t.findIndex(i=>Et({businessDate:i.businessDate,reportHubId:i.reportHubId},e)>0);return s===-1?t.length:s};var ne=(t,e,s=console)=>{let i=y=>typeof y=="string"?[`${e} ${y}`]:[e,y];return{debug:(y,...m)=>{t<=0&&s.debug(...i(y),...m)},info:(y,...m)=>{t<=1&&s.info(...i(y),...m)},warn:(y,...m)=>{t<=2&&s.warn(...i(y),...m)},error:(y,...m)=>{t<=3&&s.error(...i(y),...m)}}};var d=require("zod"),kt=d.z.object({name:d.z.string().nullish(),affiliation:d.z.string().nullish()}),vt=d.z.object({name:d.z.string().nullish(),text:d.z.string().nullish(),color:d.z.string().nullish()}),xt=d.z.object({id:d.z.number(),name:d.z.string().nullish(),color:d.z.string().nullish()}),Ve=d.z.object({id:d.z.number(),userId:d.z.string().nullish(),userName:d.z.string().nullish(),content:d.z.string().nullish(),createdAt:d.z.string().nullish(),isMine:d.z.boolean()}),Nt=d.z.object({id:d.z.string(),fileName:d.z.string(),fileType:d.z.string().nullable(),fileSize:d.z.number().nullable(),createdAt:d.z.string().nullable(),state:d.z.enum(["present","absent","unknown"])}),Pe=d.z.object({reportHubId:d.z.number(),date:d.z.string().nullish(),createdAt:d.z.string().nullish(),author:d.z.string().nullish(),userId:d.z.string().nullish(),sourceType:d.z.string().nullish(),employeeName:d.z.string().nullish(),updatedBy:d.z.string().nullish(),updatedAt:d.z.string().nullish(),category:d.z.string().nullish(),creationCategory:d.z.string().nullish(),visitTimeFrom:d.z.string().nullish(),visitTimeTo:d.z.string().nullish(),customerName:d.z.string().nullish(),interviewers:d.z.array(kt),subject:d.z.string().nullish(),content:d.z.string().nullish(),comments:d.z.array(vt),isRead:d.z.boolean(),isStarred:d.z.boolean(),labels:d.z.array(xt),commentItems:d.z.array(Ve),attachments:d.z.array(Nt).default([])}),Lt=d.z.object({type:d.z.literal("connected")}),Me=d.z.object({type:d.z.literal("status-update"),reportHubId:d.z.number(),statusType:d.z.enum(["star","read"]),value:d.z.boolean(),clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),Be=d.z.object({type:d.z.literal("comment-add"),reportHubId:d.z.number(),comment:Ve,clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),Ue=d.z.object({type:d.z.literal("comment-delete"),reportHubId:d.z.number(),commentId:d.z.number(),clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),He=d.z.object({type:d.z.literal("report-create"),reportHubId:d.z.number(),report:Pe,clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),Ye=d.z.object({type:d.z.literal("report-update"),reportHubId:d.z.number(),report:Pe,clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),Oe=d.z.object({type:d.z.literal("report-publish"),reportHubId:d.z.number(),report:Pe,clientTempId:d.z.string(),recipientRawUserId:d.z.number().optional()}),qe=d.z.object({type:d.z.literal("report-delete"),reportHubId:d.z.number(),clientTempId:d.z.string()}),Ze=d.z.discriminatedUnion("type",[Lt,Me,Be,Ue,He,Ye,Oe,qe]);var et=require("crypto"),Ee=t=>{let e=JSON.stringify(t);return`"${(0,et.createHash)("sha256").update(e).digest("hex")}"`};var $t=ne(1,"[Response]"),ae=(t,e,s,i=200,y=$t)=>{let m=Ee(s),M=t.headers.get("If-None-Match");y.info(`[jsonResponseWithETag] ETag: ${m}, If-None-Match: ${M}`);let h=new Headers({"Content-Type":"application/json","Cache-Control":"private, max-age=0, must-revalidate","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'none'",ETag:m});return e&&h.append("Set-Cookie",e),t.method==="GET"&&i===200&&M===m?new Response(null,{status:304,headers:h}):new Response(JSON.stringify(s),{status:i,headers:h})};var tt=require("events");var we=(t,e)=>{let[s,i]=t.split("-").map(Number),[y,m]=e.split("-").map(Number);return s!==y?s<y:i<=m},De=class{constructor(e){this.config=e;this._emitter=new tt.EventEmitter;this._state="idle";this._refCount=0;this._lastId="0-0";this._emitter.setMaxListeners(0),this._logger=e.logger??ne(1,"[SSE Reader]")}subscribe(e,s){return this._emitter.on("entry",e),s&&this._emitter.on("error",s),this._refCount++,this._refCount===1&&this._startLoop(),()=>{this._emitter.removeListener("entry",e),s&&this._emitter.removeListener("error",s),this._refCount--,this._refCount<=0&&(this._refCount=0,this._stopLoop())}}async destroy(){this._refCount=0,await this._stopLoop(),this._emitter.removeAllListeners(),this._lastId="0-0"}async _startLoop(){for(;this._state==="stopping";)await new Promise(e=>setTimeout(e,50));if(this._state!=="running"){this._state="running";try{this._client=await this.config.redis?.createClient()}catch(e){this._state="idle",this._emitter.emit("error",e instanceof Error?e:new Error("Failed to create Redis client for SSE reader"));return}if(!this._client){this._state="idle",this._emitter.emit("error",new Error("Failed to create Redis client for SSE reader"));return}this._client.on("error",e=>{this._logger.error("Redis client error:",e)});try{for(;this._state==="running"&&this._client?.isOpen;){let e=await this._client.xRead([{key:this.config.streamKey,id:this._lastId}],{BLOCK:5e3,COUNT:100});if(e)for(let s of e)for(let i of s.messages){this._lastId=i.id;try{let y=i.message?.data?JSON.parse(i.message.data):null,m=y?.type;if((m==="report-create"||m==="report-delete"||m==="report-publish")&&this.config.cache.invalidatePrefix("daily-report:ids"),m==="comment-add"||m==="comment-delete"||m==="status-update"){let M=y?.reportHubId;typeof M=="number"&&(this.config.cache.invalidate(`daily-report:detail:${M}`),this.config.cache.invalidatePrefix(`daily-report:detail:${M}:user:`))}}catch{}this._emitter.emit("entry",{id:i.id,message:i.message})}}}catch(e){!(e?.constructor?.name==="ClientClosedError")&&this._state==="running"&&(this._logger.error("xRead loop error:",e),this._emitter.emit("error",e))}finally{if(this._client?.isOpen)try{await this._client.quit()}catch{}this._client=void 0,this._state="idle",this._refCount>0&&this._startLoop()}}}async _stopLoop(){if(this._state==="running"){if(this._state="stopping",this._client?.isOpen)try{await this._client.quit()}catch{}for(;this._state!=="idle";)await new Promise(e=>setTimeout(e,50))}}};var L=(t,e)=>new Response(JSON.stringify(t),{status:e?.status??200,headers:{"Content-Type":"application/json"}}),ve=200,st=4e3,Pt="stream interrupted by server error",Bt=/^\d{4}-\d{2}-\d{2}$/,Ut=15e3,rt=Symbol("ids-stream-heartbeat"),Ht=t=>{let e=t.searchParams.get("cursor_report_hub_id"),s=t.searchParams.get("cursor_business_date");if(e===null)return s===null?{cursor:null}:{invalid:!0};let i=Number.parseInt(e,10);return!Number.isSafeInteger(i)||String(i)!==e||i<=0?{invalid:!0}:s===null?{cursor:{businessDate:null,reportHubId:i}}:Bt.test(s)?{cursor:{businessDate:s,reportHubId:i}}:{invalid:!0}};async function*nt(t,e,s=ve){let i=e?We(t,e):0,y=!0;for(;;){let m=t.slice(i,i+(y?s:st));i+=m.length;let M=i>=t.length,h=m[m.length-1];if(yield{items:m,nextCursor:M||!h?null:{businessDate:h.businessDate,reportHubId:h.reportHubId},isLast:M,...y?{total:t.length}:{}},y=!1,M)return}}var at=(t,e,s,i)=>{let y=new TextEncoder,m=!1,M=null,h=new ReadableStream({async pull(u){if(m||s.signal.aborted){await e.return(void 0).catch(()=>{});try{u.close()}catch{}return}let N=null;try{M===null&&(M=e.next());let p=new Promise(me=>{N=setTimeout(()=>me(rt),Ut)}),K=await Promise.race([M,p]);if(N!==null&&(clearTimeout(N),N=null),K===rt){if(m||s.signal.aborted){await e.return(void 0).catch(()=>{});try{u.close()}catch{}return}u.enqueue(y.encode(`
2
+ `));return}M=null;let{value:q,done:U}=K;if(U){u.close();return}if(m||s.signal.aborted){await e.return(void 0).catch(()=>{});try{u.close()}catch{}return}u.enqueue(y.encode(`${JSON.stringify(q)}
3
+ `))}catch(p){if(N!==null&&clearTimeout(N),M=null,m||s.signal.aborted)return;i.error(`ids-stream emission failed: ${p instanceof Error?p.message:String(p)}`);try{u.enqueue(y.encode(`${JSON.stringify({error:Pt})}
4
+ `)),u.close()}catch{}}},cancel(){m=!0,e.return(void 0).catch(()=>{})}}),O=new Headers({"Content-Type":"application/x-ndjson; charset=utf-8","Cache-Control":"private, no-cache, no-transform","X-Content-Type-Options":"nosniff"});return t&&O.append("Set-Cookie",t),new Response(h,{status:200,headers:O})},Yt=32*1024*1024,Ot=60,qt=4,Ft=12e4,zt=1024,Kt=60,Jt=5,jt=new Set(["image/png","image/jpeg","image/gif","image/webp","application/pdf","text/plain"]),Gt=/^[\x20-\x7E]+$/,Qt=/^[\w.+-]+\/[\w.+-]+/,Fe=t=>t&&Gt.test(t)&&Qt.test(t)?t:null,ot=t=>encodeURIComponent(t).replace(/['()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`),it=t=>t.replace(/[^\x20-\x7E]/g,"_").replace(/["\\]/g,"_"),Xt=t=>{switch(t){case"not_found":case"denied":case"invalid_path":return 404;case"too_large":return 413;default:return 502}},Wt={404:"Attachment not found",413:"Attachment too large",502:"Attachment storage unavailable"},Vt=t=>{let e=new Map;return(s,i)=>{let y=e.get(s);y&&e.delete(s);let m=y??{tokens:t,lastRefillMs:i},M=Math.max(0,i-m.lastRefillMs);M>0&&(m.tokens=Math.min(t,m.tokens+M*t/6e4),m.lastRefillMs=i);let h=m.tokens>=1;for(h&&(m.tokens-=1),e.set(s,m);e.size>zt;){let O=e.keys().next();if(O.done)break;e.delete(O.value)}return h}},Zt=t=>{let e=0;return{tryAcquire:()=>e>=t?!1:(e+=1,!0),release:()=>{e>0&&(e-=1)}}},er=(t,e,s)=>{let i=setTimeout(e,s);i.unref?.();let y=()=>{clearTimeout(i),e()},m=!1;return new ReadableStream({pull(M){if(m){M.close(),y();return}m=!0,M.enqueue(t)},cancel(){y()}})};function ze(t){let{authenticate:e,service:s,encodeUserId:i,redis:y,sseReader:m,streamKey:M}=t,h=t.loginRedirectPath??"/auth/login",O=t.logger??ne(3,"[DailyReportAPI]"),u=t.logger??ne(1,"[DailyReportSSE]"),N=t.logger??ne(1,"[DailyReportAttachment]"),p=t.attachmentMaxBytes??Yt,K=Vt(t.attachmentRateLimitPerMinute??Ot),q=Zt(t.attachmentConcurrency??qt),U=async({request:k})=>{let{user:H,cookie:v}=await e(k,{failureRedirect:h}),x=new Headers;v&&x.append("Set-Cookie",v);let R=null;H&&(R=await s.getUserIdByExternalId(H.id));let C=R?i(R):null;return{data:{user:H,userId:C},headers:x}},me={"business-date":async(k,H,v,x)=>{let R=ee(k.searchParams.get("businessDate")),C=k.searchParams.get("forceRefresh")==="true";if(!R)return ae(v,H,{error:{message:"Invalid business date"}},400);let $=await s.getDailyReportsByBusinessDateByExternalId(R,x.id,{forceRefresh:C});return ae(v,H,{businessDate:R,reports:$},200)},"ids-stream":async(k,H,v,x)=>{let R=Ht(k);if("invalid"in R)return ae(v,H,{error:{message:"Invalid cursor"}},400);let C=k.searchParams.get("forceRefresh")==="true",$=s.getDailyReportIdsByExternalId(x.id,{forceRefresh:C});if($.catch(()=>{}),R.cursor)return at(H,nt(await $,R.cursor),v,O);let E=s.getDailyReportIdsFirstPageByExternalId(x.id,ve);E.catch(()=>{});let D=(async function*(){let w=await Promise.race([$.then(g=>({kind:"full",items:g}),g=>({kind:"full-error",error:g})),E.then(g=>({kind:"page",items:g}),()=>({kind:"page-error"}))]),I=null,_=null;if(w.kind==="full")_=w.items;else if(w.kind==="page"||w.kind==="full-error"){let g=w.kind==="page"?w.items:await E;if(g.length<ve){yield{items:g,nextCursor:null,isLast:!0,total:g.length};return}let P=g[g.length-1];I={businessDate:P.businessDate,reportHubId:P.reportHubId},yield{items:g,nextCursor:I,isLast:!1}}_===null&&(_=await $),yield*nt(_,I,I?st:ve)})();return at(H,D,v,O)},report:async(k,H,v,x)=>{let R=k.searchParams.get("reportHubId"),C=k.searchParams.get("forceRefresh")==="true",$=R?Number.parseInt(R,10):NaN;if(!Number.isFinite($)||$<=0)return ae(v,H,{error:{message:"Invalid reportHubId"}},400);let E=await s.getDailyReportDetailByIdByExternalId($,x.id,{snapshot:!0,forceRefresh:C});return E?ae(v,H,{report:E},200):ae(v,H,{error:{message:"Report not found"}},404)}};return{index:{loader:U},api:{loader:async({request:k,params:H})=>{let{user:v,cookie:x}=await e(k,{failureRedirect:null}),R=x??null;if(!v)return ae(k,R,{error:{message:"Unauthorized"}},401);if(k.method!=="GET")return ae(k,R,{error:{message:"Method not allowed"}},405);let C=H.endpoint??"",$=me[C];if(!$)return ae(k,R,{error:{message:"Unknown endpoint"}},404);let E=Date.now();try{let D=new URL(k.url);return await $(D,R,k,v)}catch(D){let w=Date.now()-E,I=D instanceof Error?D:new Error(String(D));return O.error(`500 endpoint=${C} elapsed=${w}ms name=${I.name} code=${"code"in I?I.code:"N/A"} message=${I.message}`),O.error("Stack:",I.stack),ae(k,R,{error:{message:"Internal Server Error"}},500)}},action:async({request:k,params:H})=>{let{user:v}=await e(k,{failureRedirect:null});if(!v)return L({error:"Unauthorized"},{status:401});if(H.endpoint!=="action")return L({error:"Unknown endpoint"},{status:404});let R=await s.getUserIdByExternalId(v.id);if(!R)return L({error:"User not found"},{status:404});let C=await k.formData(),$=C.get("intent"),E=C.get("reportHubId"),D=E?Number(E):NaN,w=C.get("businessDate"),I=Number(C.get("operationTimestamp")),_=C.get("clientTempId");if($==="clearCache")return t.enableDevCacheClear?(await s.clearCache(),L({status:"OK",intent:"clearCache"})):L({error:"Invalid intent"},{status:400});if(!_)return L({error:"clientTempId required"},{status:400});if($!=="create"&&(!D||Number.isNaN(D)))return L({error:"Invalid reportHubId"},{status:400});switch($){case"create":{if(!w)return L({error:"businessDate required"},{status:400});let g=await s.createDailyReport(R,w,_);return L({status:"OK",intent:"create",report:g,reportHubId:String(g.reportHubId),clientTempId:_})}case"update":{let g=C.get("title"),P=C.get("content");try{return await s.updateDailyReport(D,R,{title:g,content:P},_),L({status:"OK",intent:"update",reportHubId:String(D),clientTempId:_})}catch(F){if(F instanceof Error){if(F.message==="Unauthorized")return L({error:"Unauthorized"},{status:403});if(F.message==="Not Found")return L({error:"Report not found"},{status:404})}throw F}}case"publish":try{let g=await s.publishDailyReport(D,R,_);return L({status:"OK",intent:"publish",reportHubId:String(D),clientTempId:_,report:g})}catch(g){if(g instanceof Error){if(g.message==="Unauthorized")return L({error:"Unauthorized"},{status:403});if(g.message==="Not Found")return L({error:"Report not found"},{status:404})}throw g}case"delete":try{return await s.deleteDailyReport(D,R,_),L({status:"OK",intent:"delete",reportHubId:String(D),clientTempId:_})}catch(g){if(g instanceof Error){if(g.message==="Unauthorized")return L({error:"Unauthorized"},{status:403});if(g.message==="Not Found")return L({error:"Report not found"},{status:404})}throw g}case"toggleStar":{let g=C.get("isStarred");if(g===null)return L({error:"isStarred required"},{status:400});let P=g==="true",F=await s.setStarStatus(R,D,w,P,_);return L({status:"OK",intent:"toggleStar",updatedStatus:{isStarred:F.isStarred,isRead:F.isRead},operationTimestamp:I,reportHubId:String(D),clientTempId:_})}case"toggleRead":{let g=C.get("isRead");if(g===null)return L({error:"isRead required"},{status:400});let P=g==="true",F=await s.setReadStatus(R,D,w,P,_);return L({status:"OK",intent:"toggleRead",updatedStatus:{isStarred:F.isStarred,isRead:F.isRead},operationTimestamp:I,reportHubId:String(D),clientTempId:_})}case"addComment":{let g=C.get("content");if(!g)return L({error:"Content required"},{status:400});try{let P=await s.addComment(R,D,g,w,_),F={...P,userId:P.userId};return L({status:"OK",intent:"addComment",newComment:F,reportHubId:String(D),clientTempId:_})}catch(P){if(P instanceof Error){if(P.message==="Not Found")return L({error:"Report not found"},{status:404});if(P.message.startsWith("Comments are restricted"))return L({error:"Comments are restricted for this report"},{status:403})}throw P}}case"deleteComment":{let g=Number(C.get("commentId"));if(!g||Number.isNaN(g))return L({error:"Invalid commentId"},{status:400});try{return await s.deleteComment(R,D,g,w,_),L({status:"OK",reportHubId:String(D),deletedCommentId:String(g),clientTempId:_})}catch(P){if(P instanceof Error){if(P.message==="Unauthorized")return L({error:"Unauthorized"},{status:403});if(P.message==="Not Found")return L({error:"Comment not found"},{status:404})}throw P}}default:return L({error:"Invalid intent"},{status:400})}}},sse:{loader:async({request:k,params:H})=>{let{user:v}=await e(k,{failureRedirect:null});if(!v)return new Response("Unauthorized",{status:401});let x=await s.getUserIdByExternalId(v.id);if(!x)return new Response("Forbidden",{status:403});if(H.endpoint!=="updates")return new Response("Not Found",{status:404});let C=new URL(k.url).searchParams.get("lastEventId")||k.headers.get("Last-Event-ID"),$=new TextEncoder,E=null,D=null,w=null,I=!1,_=()=>{if(!I){if(I=!0,D&&(clearInterval(D),D=null),w&&(w(),w=null),E)try{E.desiredSize!==null&&E.close()}catch{}finally{E=null}I=!1}},g=(F,G)=>{if(G.data)try{let T=JSON.parse(G.data),W=Ze.safeParse(T);if(!W.success){u.error("SSE message validation failed:",W.error.format());return}let te=W.data,se=typeof T.recipientRawUserId=="number"?T.recipientRawUserId:void 0;if(te.type==="status-update"){if(se===void 0||se!==x)return}else if(se!==void 0&&se!==x)return;let re;if("recipientRawUserId"in T){let{recipientRawUserId:be,...ye}=T;re=JSON.stringify(ye)}else re=G.data;E&&E.desiredSize!==null&&E.enqueue($.encode(`id: ${F}
5
+ data: ${re}
3
6
 
4
- `))}catch(I){c.error(`[SSE:${k}] processEntry error:`,I)}},Y=new ReadableStream({async start(q){E=q,R=setInterval(()=>{try{E&&E.desiredSize!==null?E.enqueue(H.encode(`: keep-alive
7
+ `))}catch(T){u.error(`[SSE:${x}] processEntry error:`,T)}},P=new ReadableStream({async start(F){E=F,D=setInterval(()=>{try{E&&E.desiredSize!==null?E.enqueue($.encode(`: keep-alive
5
8
 
6
- `)):T()}catch{T()}},5e3),M.signal.addEventListener("abort",()=>{T()});let Q=S||"0-0";if(A=y.subscribe(I=>{we(I.id,Q)||(Q=I.id,w(I.id,I.message))},I=>{c.error(`[SSE:${k}] Fan-Out reader error:`,I),T()}),!S)try{let I=await p?.getClient();if(I){let X=await I.xRevRange(L,"+","-",{COUNT:1});X.length>0&&!we(X[0].id,Q)&&(Q=X[0].id)}}catch{}if(E&&E.desiredSize!==null){let I=`data: ${JSON.stringify({type:"connected"})}
9
+ `)):_()}catch{_()}},5e3),k.signal.addEventListener("abort",()=>{_()});let G=C||"0-0";if(w=m.subscribe(T=>{we(T.id,G)||(G=T.id,g(T.id,T.message))},T=>{u.error(`[SSE:${x}] Fan-Out reader error:`,T),_()}),!C)try{let T=await y?.getClient();if(T){let W=await T.xRevRange(M,"+","-",{COUNT:1});W.length>0&&!we(W[0].id,G)&&(G=W[0].id)}}catch{}if(E&&E.desiredSize!==null){let T=`data: ${JSON.stringify({type:"connected"})}
7
10
 
8
- `,X=Q!=="0-0"?`id: ${Q}
9
- ${I}`:I;E.enqueue(H.encode(X))}if(S)try{let I=await p?.getClient();if(I){let X=await I.xRange(L,S,"+",{COUNT:1e3});for(let V of X)V.id!==S&&(we(V.id,Q)||(Q=V.id,w(V.id,V.message)))}}catch(I){c.error(`[SSE:${k}] catch-up xRange error:`,I)}},cancel(){T()}});return new Response(Y,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive"}})}},attachment:{loader:async({request:M,params:U})=>{let v=null,k=(b,S,H)=>{let E=new Headers({"Content-Type":"application/json"});return H!==void 0&&E.set("Retry-After",String(H)),v&&E.append("Set-Cookie",v),new Response(JSON.stringify({error:{message:S}}),{status:b,headers:E})};try{let{user:b,cookie:S}=await e(M,{failureRedirect:null});if(v=S??null,!b)return k(401,"Unauthorized");if(M.method!=="GET"&&M.method!=="HEAD")return k(405,"Method not allowed");let H=r.attachmentIdCodec,E=r.readAttachment;if(!(H&&E))return k(404,"Attachment not found");let R=U.token??"",A=H.decode(R);if(A===null||!Number.isSafeInteger(A)||A<=0)return k(400,"Invalid attachment token");let _=await o.getUserIdByExternalId(b.id);if(!_)return k(403,"Forbidden");if(!K(_,Date.now()))return $.error(`429 attachment=${A} viewer=${_} reason=rate_limit`),k(429,"Too many requests",Nt);let T=await o.getAttachmentForUser(A,_);if(!T)return $.error(`404 attachment=${A} viewer=${_} reason=not_visible`),k(404,"Attachment not found");if(T.fileSize!=null&&T.fileSize>m)return $.error(`413 attachment=${A} viewer=${_} reason=db_size`),k(413,"Attachment too large");if(!j.tryAcquire())return $.error(`503 attachment=${A} viewer=${_} reason=concurrency`),k(503,"Too many concurrent downloads",xt);let w=!1,Y=()=>{w||(w=!0,j.release())},q=!1;try{let Q=M.method==="HEAD",I=await E(T.filePath,{maxBytes:m,head:Q,principal:l(_)});if(!I.ok){let se=Bt(I.reason);return I.reason==="not_found"&&o.markAttachmentMissing(A).catch(pe=>{$.error(`markAttachmentMissing failed attachment=${A} message=${pe instanceof Error?pe.message:"unknown"}`)}),$.error(`${se} attachment=${A} viewer=${_} reason=${I.reason} code=${I.code??"-"}`),k(se,Ut[se]??"Attachment storage unavailable")}let X=Ye(T.fileType)??Ye(I.contentType)??"application/octet-stream",V=Lt.has(X),ae=new URL(M.url).searchParams.get("download")==="1",de=V?X:"application/octet-stream",ge=V&&!ae?"inline":"attachment",oe=I.size??I.bytes.byteLength,ue=new Headers({"Content-Type":de,"Content-Length":String(oe),"Content-Disposition":`${ge}; filename="${Ze(T.fileName)}"; filename*=UTF-8''${Ve(T.fileName)}`,"Cache-Control":"private, no-store","X-Content-Type-Options":"nosniff","Content-Security-Policy":"default-src 'none'; sandbox","X-Frame-Options":"SAMEORIGIN"});return v&&ue.append("Set-Cookie",v),o.markAttachmentPresent(A).catch(se=>{$.error(`markAttachmentPresent failed attachment=${A} message=${se instanceof Error?se.message:"unknown"}`)}),$.info(`200 attachment=${A} viewer=${_} type=${de} disposition=${ge} bytes=${oe}`),Q?new Response(null,{status:200,headers:ue}):(q=!0,new Response(Yt(I.bytes,Y,vt),{status:200,headers:ue}))}finally{q||Y()}}catch(b){if(b instanceof Response)throw b;return $.error(`500 attachment=? viewer=? reason=unexpected message=${b instanceof Error?b.message:"unknown"}`),k(500,"Attachment request failed")}}}}}var a=require("drizzle-orm");var me=r=>(0,a.getColumns)(r);function ze(r){let{db:e,tables:o,userTable:l,resolveUserId:p,encodeUserId:y,redis:L,cache:h,epochs:O}=r,{hub:c,internal:$,comment:m,label:K,hubLabel:j,userStatus:B}=o,he=r.externalSources??[],Ae=r.draftLabelNames&&r.draftLabelNames.length>0?r.draftLabelNames:[r.draftLabelName??"\u4E0B\u66F8\u304D"],_e=Ae[0]??"\u4E0B\u66F8\u304D",W=r.logger??re(1,"[DailyReportService]"),fe="daily-report:ids",M=r.idsTtlMs??18e4,U="daily-report:ids:epoch",v="daily-report:business-date:",k=r.businessDateTtlMs??3e5,b="daily-report:date-epoch:",S="daily-report:detail-epoch:",H=r.streamKey??"daily-report:sse-stream",E=r.streamMaxLen??1e4,R=t=>O.incrementEpoch(t),A=async(t,n)=>{let s=await L?.getClient();if(!s){W.warn(`[SSE] Redis client unavailable (${n})`);return}let u=Date.now();try{await s.xAdd(H,"*",{data:JSON.stringify(t)},{TRIM:{strategy:"MAXLEN",strategyModifier:"~",threshold:E}});let g=Date.now()-u;g>1e3&&W.warn(`[SSE] Slow publish (${n}): ${g}ms`)}catch(g){W.error(`[SSE] Redis publish failed (${n}):`,g)}},_=(t,n)=>{if(!t)return null;let s=t instanceof Date?t:new Date(t);if(Number.isNaN(s.getTime()))return typeof t=="string"?t:null;let u=n==="YYYY-MM-DD HH:mm:ss"?{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}:{year:"numeric",month:"2-digit",day:"2-digit"};return(n==="YYYY-MM-DD HH:mm:ss"?s.toLocaleString("ja-JP",u):s.toLocaleDateString("ja-JP",u)).replace(/\//g,"-")},T=t=>t==null||t===""?null:/^\d+$/.test(t)?y(Number(t)):t,w=()=>{let t={};for(let n of he)t[`ext_${n.sourceType}`]=me(n.table);return t},Y=t=>{let n=t;for(let s of he)n=n.leftJoin(s.table,(0,a.and)((0,a.eq)(c.sourceType,s.sourceType),(0,a.eq)(c.sourceIdNum,s.idColumn)));return n},q=(t,n)=>{let{hub:s,internal:u}=t,g=s.summary,f=[],D=null,C=null,z=null,J=null,P=null,te=null,ie=[],ce=he.find(N=>N.sourceType===s.sourceType),ye=ce?t[`ext_${ce.sourceType}`]:void 0;if(ce&&ye){let N=ce.mapRow(ye);N.content!==void 0&&(g=N.content),N.employeeName!==void 0&&(te=N.employeeName),N.category!==void 0&&(D=N.category),N.creationCategory!==void 0&&(C=N.creationCategory),N.visitTimeFrom!==void 0&&(z=N.visitTimeFrom),N.visitTimeTo!==void 0&&(J=N.visitTimeTo),N.customerName!==void 0&&(P=N.customerName),N.interviewers!==void 0&&(f=N.interviewers),N.comments!==void 0&&(ie=N.comments)}else s.sourceType==="Internal"&&u&&(g=u.body);let le=(t.labels??[]).map(N=>({id:N.id,name:N.name,color:N.color})),Z=t.comments??[],F=C?Ae.includes(C):!1,G=oe?.[0]??null;return F&&G&&!le.some(N=>N.id===G)&&le.push({id:G,name:_e,color:null}),{reportHubId:s.id,date:_(s.businessDate,"YYYY-MM-DD"),author:t.creatorName??T(s.createdBy)??"",userId:s.userId?y(s.userId):"",sourceType:s.sourceType??"Internal",createdAt:_(s.createdAt,"YYYY-MM-DD HH:mm:ss"),updatedAt:_(s.updatedAt,"YYYY-MM-DD HH:mm:ss"),updatedBy:T(s.updatedBy),employeeName:te??t.creatorName??T(s.createdBy),category:D,creationCategory:C,visitTimeFrom:z,visitTimeTo:J,customerName:P,interviewers:f,subject:s.title,content:g,comments:ie,isRead:t.isRead??!1,isStarred:t.isStarred??!1,labels:le,commentItems:Z.map(N=>({...N,userId:y(N.userId),isMine:n?N.userId===n:!1})),attachments:t.attachments}},Q=async t=>{let n=(0,a.aliasedTable)(j,"draft_label"),s=await se();return(await e.select({reportHubId:c.id,businessDate:c.businessDate,sourceType:c.sourceType}).from(c).leftJoin(n,(0,a.and)((0,a.eq)(n.hubId,c.id),(0,a.inArray)(n.labelId,s))).where((0,a.and)((0,a.isNull)(c.deletedAt),(0,a.or)((0,a.eq)(c.userId,t),(0,a.isNull)(c.userId),(0,a.isNull)(n.hubId)))).orderBy((0,a.desc)(c.businessDate),(0,a.desc)(c.id))).map(g=>({...g,businessDate:_(g.businessDate,"YYYY-MM-DD")}))},I=async t=>{let n=await Y(e.select({hub:me(c),internal:me($),creatorName:l.displayName,...w()}).from(c)).leftJoin($,(0,a.eq)(c.id,$.hubId)).leftJoin(l,(0,a.eq)(c.userId,l.id)).where((0,a.and)((0,a.eq)(c.businessDate,a.sql`${t}`),(0,a.isNull)(c.deletedAt))).orderBy((0,a.desc)(c.id)),s=await Ce(n.map(u=>u.hub.id));return n.map(u=>q({...u,attachments:s.get(u.hub.id)??[]}))},X=t=>`${v}${t}`,V=new Map,ae=async t=>{if(!r.disableUserIdCache&&V.has(t))return V.get(t)??null;let n=await p(t);return n!==null&&!r.disableUserIdCache&&V.set(t,n),n},de=async(t,{forceRefresh:n=!1,snapshot:s=!1,ttlMsOverride:u}={})=>{let g=await ae(t);if(!g)return[];let f=`${fe}:user:${g}`;return h.getOrFetch({cacheKey:f,fetcher:async()=>await Q(g),forceRefresh:n,snapshot:s,ttlMsOverride:u??M,epochKey:U})},ge=(t,{forceRefresh:n=!1,snapshot:s=!1,ttlMsOverride:u}={})=>{let g=ee(t);return g?h.getOrFetch({cacheKey:X(g),fetcher:()=>I(g),forceRefresh:n,snapshot:s,ttlMsOverride:u??k}):Promise.resolve([])},oe=null,ue=async()=>(oe!==null||(oe=(await e.select({id:K.id}).from(K).where((0,a.inArray)(K.name,Ae)).orderBy(K.id)).map(n=>n.id)),oe),se=async()=>{let t=await ue();return t.length>0?t:[-1]},pe=async()=>{let t=await ue();if(t.length===0)return null;let[n]=await e.select({id:K.id}).top(1).from(K).where((0,a.eq)(K.name,_e));return n?.id??t[0]},tt=(t,n)=>n!=null?"absent":t!=null?"present":"unknown",Ce=async t=>{let n=new Map,s=o.attachment,u=r.attachmentIdCodec;if(t.length===0||!s||!u)return n;let g=await e.select({id:s.id,hubId:s.hubId,fileName:s.fileName,fileType:s.fileType,fileSize:s.fileSize,createdAt:s.createdAt,objectCheckedAt:s.objectCheckedAt,objectMissingAt:s.objectMissingAt}).from(s).where((0,a.inArray)(s.hubId,t)).orderBy((0,a.asc)(s.createdAt),(0,a.asc)(s.id));for(let f of g){let D={id:u.encode(Number(f.id)),fileName:String(f.fileName),fileType:f.fileType?String(f.fileType):null,fileSize:f.fileSize!=null?Number(f.fileSize):null,createdAt:_(f.createdAt,"YYYY-MM-DD HH:mm:ss"),state:tt(f.objectCheckedAt,f.objectMissingAt)},C=n.get(Number(f.hubId));C?C.push(D):n.set(Number(f.hubId),[D])}return n},rt=async(t,n)=>{let s=o.attachment;if(!s)return null;let u=await se(),g=(0,a.aliasedTable)(j,"draft_label");return(await e.select({fileName:s.fileName,filePath:s.filePath,fileType:s.fileType,fileSize:s.fileSize}).top(1).from(s).innerJoin(c,(0,a.eq)(c.id,s.hubId)).leftJoin(g,(0,a.and)((0,a.eq)(g.hubId,c.id),(0,a.inArray)(g.labelId,u))).where((0,a.and)((0,a.eq)(s.id,t),(0,a.isNull)(c.deletedAt),(0,a.or)((0,a.eq)(c.userId,n),(0,a.isNull)(g.hubId)))))[0]??null},nt=async t=>{let n=o.attachment;if(!n)return;let s=new Date;try{await e.update(n).set({objectCheckedAt:s,objectMissingAt:s,updatedAt:s}).where((0,a.eq)(n.id,t))}catch(u){W.error("markAttachmentMissing failed",u)}},at=async t=>{let n=o.attachment;if(!n)return;let s=new Date;try{await e.update(n).set({objectCheckedAt:s,objectMissingAt:null,updatedAt:s}).where((0,a.eq)(n.id,t))}catch(u){W.error("markAttachmentPresent failed",u)}},ot=async(t,n,{forceRefresh:s=!1,snapshot:u=!1,ttlMsOverride:g}={})=>{let f=await ae(n);if(!f)return[];let D=ee(t);if(!D)return[];let C=`daily-report:date:${D}:user:${f}`;return h.getOrFetch({cacheKey:C,fetcher:async()=>{let z=await se(),J=(0,a.aliasedTable)(j,"draft_label"),P=await Y(e.select({hub:me(c),internal:me($),isRead:B.isRead,isStarred:B.isStarred,creatorName:l.displayName,...w()}).from(c)).leftJoin($,(0,a.eq)(c.id,$.hubId)).leftJoin(B,(0,a.and)((0,a.eq)(c.id,B.hubId),(0,a.eq)(B.userId,f))).leftJoin(J,(0,a.and)((0,a.eq)(J.hubId,c.id),(0,a.inArray)(J.labelId,z))).leftJoin(l,(0,a.eq)(c.userId,l.id)).where((0,a.and)((0,a.eq)(c.businessDate,a.sql`${D}`),(0,a.isNull)(c.deletedAt),(0,a.or)((0,a.eq)(c.userId,f),(0,a.isNull)(J.hubId)))).orderBy((0,a.desc)(c.id));if(P.length===0)return[];let te=P.map(F=>F.hub.id),ie=await e.select({hubId:j.hubId,id:K.id,name:K.name,color:K.color}).from(j).innerJoin(K,(0,a.eq)(j.labelId,K.id)).where((0,a.inArray)(j.hubId,te)),ce=await e.select({hubId:m.hubId,id:m.id,body:m.body,createdAt:m.createdAt,userId:m.userId,userName:l.displayName}).from(m).leftJoin(l,(0,a.eq)(m.userId,l.id)).where((0,a.inArray)(m.hubId,te)).orderBy((0,a.asc)(m.createdAt)),ye=await Ce(te),le=new Map,Z=new Map;for(let F of ie){let G=le.get(F.hubId);G||(G=[],le.set(F.hubId,G)),G.push({id:F.id,name:F.name,color:F.color})}for(let F of ce){let G=Z.get(F.hubId);G||(G=[],Z.set(F.hubId,G)),G.push({id:F.id,content:F.body,createdAt:_(F.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:F.userId,userName:F.userName||""})}return P.map(F=>{let G=F.hub.id;return q({...F,attachments:ye.get(G)??[],labels:le.get(G)||[],comments:Z.get(G)||[]},f)})},forceRefresh:s,snapshot:u,ttlMsOverride:g??k,epochKey:`${b}${D}`})},be=async(t,n,{forceRefresh:s=!1,snapshot:u=!1,ttlMsOverride:g}={})=>{let f=`daily-report:detail:${t}:user:${n}`;return(await h.getOrFetch({cacheKey:f,fetcher:async()=>{let C=await se(),z=(0,a.aliasedTable)(j,"draft_label"),J=await Y(e.select({hub:me(c),internal:me($),isRead:B.isRead,isStarred:B.isStarred,creatorName:l.displayName,...w()}).from(c)).leftJoin($,(0,a.eq)(c.id,$.hubId)).leftJoin(B,(0,a.and)((0,a.eq)(c.id,B.hubId),(0,a.eq)(B.userId,n))).leftJoin(z,(0,a.and)((0,a.eq)(z.hubId,c.id),(0,a.inArray)(z.labelId,C))).leftJoin(l,(0,a.eq)(c.userId,l.id)).where((0,a.and)((0,a.eq)(c.id,t),(0,a.isNull)(c.deletedAt),(0,a.or)((0,a.eq)(c.userId,n),(0,a.isNull)(z.hubId))));if(J.length===0)return[];let P=J[0].hub.id,te=await e.select({id:K.id,name:K.name,color:K.color}).from(j).innerJoin(K,(0,a.eq)(j.labelId,K.id)).where((0,a.eq)(j.hubId,P)),ie=await e.select({id:m.id,body:m.body,createdAt:m.createdAt,userId:m.userId,userName:l.displayName}).from(m).leftJoin(l,(0,a.eq)(m.userId,l.id)).where((0,a.eq)(m.hubId,P)).orderBy((0,a.asc)(m.createdAt)),ce=await Ce([P]),ye=te.map(Z=>({id:Z.id,name:Z.name,color:Z.color})),le=ie.map(Z=>({id:Z.id,content:Z.body,createdAt:_(Z.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:Z.userId,userName:Z.userName||""}));return[q({...J[0],attachments:ce.get(P)??[],labels:ye,comments:le},n)]},forceRefresh:s,snapshot:u,ttlMsOverride:g??k,epochKey:`${S}${t}`}))[0]??null},st=async(t,n,s={})=>{let u=await ae(n);return u?be(t,u,s):null},it=async(t,n,s,u,g)=>{let f=await e.select().top(1).from(B).where((0,a.and)((0,a.eq)(B.hubId,n),(0,a.eq)(B.userId,t))),D;if(f.length>0?f[0].isStarred!==u?D=(await e.update(B).set({isStarred:u,updatedAt:new Date,updatedBy:String(t)}).output().where((0,a.and)((0,a.eq)(B.hubId,n),(0,a.eq)(B.userId,t))))[0]:D=f[0]:D=(await e.insert(B).output().values({hubId:n,userId:t,isStarred:u,isRead:!1,createdAt:new Date,createdBy:String(t),updatedAt:new Date,updatedBy:String(t)}))[0],s){let C=ee(s);C&&(h.invalidate(`daily-report:date:${C}:user:${t}`),await R(`${b}${C}`))}return h.invalidate(`daily-report:detail:${n}`),h.invalidate(`daily-report:detail:${n}:user:${t}`),await R(`${S}${n}`),await A(Me.parse({type:"status-update",reportHubId:n,recipientRawUserId:t,statusType:"star",value:u,clientTempId:g}),"setStarStatus"),D},lt=async(t,n,s,u,g)=>{let f=await e.select().top(1).from(B).where((0,a.and)((0,a.eq)(B.hubId,n),(0,a.eq)(B.userId,t))),D;if(f.length>0?f[0].isRead!==u?D=(await e.update(B).set({isRead:u,updatedAt:new Date,updatedBy:String(t)}).output().where((0,a.and)((0,a.eq)(B.hubId,n),(0,a.eq)(B.userId,t))))[0]:D=f[0]:D=(await e.insert(B).output().values({hubId:n,userId:t,isRead:u,isStarred:!1,createdAt:new Date,createdBy:String(t),updatedAt:new Date,updatedBy:String(t)}))[0],s){let C=ee(s);C&&(h.invalidate(`daily-report:date:${C}:user:${t}`),await R(`${b}${C}`))}return h.invalidate(`daily-report:detail:${n}`),h.invalidate(`daily-report:detail:${n}:user:${t}`),await R(`${S}${n}`),await A(Me.parse({type:"status-update",reportHubId:n,recipientRawUserId:t,statusType:"read",value:u,clientTempId:g}),"setReadStatus"),D},dt=async(t,n,s,u,g)=>{let[f]=await e.select({sourceType:c.sourceType}).top(1).from(c).where((0,a.eq)(c.id,n));if(f&&f.sourceType.toLowerCase()!=="internal")throw new Error("Comments are restricted for external daily report sources");let[D]=await e.insert(m).output().values({hubId:n,userId:t,body:s,createdAt:new Date,createdBy:String(t),updatedAt:new Date,updatedBy:String(t)}),[C]=await e.select({displayName:l.displayName}).top(1).from(l).where((0,a.eq)(l.id,t)),z=C?.displayName??"Unknown";if(u){let P=ee(u);P&&(h.invalidatePrefix(`daily-report:date:${P}:user:`),await R(`${b}${P}`))}h.invalidate(`daily-report:detail:${n}`),h.invalidatePrefix(`daily-report:detail:${n}:user:`),await R(`${S}${n}`);let J={id:D.id,userId:y(D.userId),userName:z,content:D.body,createdAt:_(D.createdAt,"YYYY-MM-DD HH:mm:ss")??"",isMine:!0};return await A($e.parse({type:"comment-add",reportHubId:n,comment:J,clientTempId:g}),"addComment"),J},ut=async(t,n)=>await t.select().top(1).from(m).where((0,a.eq)(m.id,n)),ct=async(t,n,s,u,g)=>{let f=await ut(e,s);if(f.length===0)throw new Error("Not Found");if(f[0].userId!==t)throw new Error("Unauthorized");if(await e.delete(m).where((0,a.eq)(m.id,s)),u){let D=ee(u);D&&(h.invalidatePrefix(`daily-report:date:${D}:user:`),await R(`${b}${D}`))}h.invalidate(`daily-report:detail:${n}`),h.invalidatePrefix(`daily-report:detail:${n}:user:`),await R(`${S}${n}`),await A(Pe.parse({type:"comment-delete",reportHubId:n,commentId:s,clientTempId:g}),"deleteComment")},mt=async(t,n,s)=>{W.info("createDailyReport called",{userId:t,businessDate:n});let[u]=await e.select({displayName:l.displayName}).from(l).where((0,a.eq)(l.id,t)),g=u?.displayName??y(t);try{let f=await e.transaction(async z=>{W.info("Starting transaction");let J=`internal-temp-${Date.now()}-${Math.random()}`,[P]=await z.insert(c).output().values({sourceType:"Internal",sourceId:J,businessDate:new Date(n),userId:t,title:"(\u7121\u984C)",createdAt:new Date,createdBy:String(t),updatedAt:new Date,updatedBy:String(t)});await z.update(c).set({sourceId:String(P.id)}).where((0,a.eq)(c.id,P.id)),W.info("Hub created",P),await z.insert($).values({hubId:P.id,body:"",createdAt:new Date,createdBy:String(t),updatedAt:new Date,updatedBy:String(t)}),W.info("Internal created"),W.info("Calling getDraftLabelId");let te=await pe();W.info("draftLabelId",te);let ie=[];return te&&(await z.insert(j).values({hubId:P.id,labelId:te,createdAt:new Date,createdBy:String(t)}),ie.push({id:te,name:_e,color:null})),W.info("Label assigned"),{reportHubId:P.id,date:n,createdAt:_(P.createdAt,"YYYY-MM-DD HH:mm:ss"),author:g,userId:y(t),sourceType:"Internal",employeeName:g,updatedBy:y(t),updatedAt:_(P.updatedAt,"YYYY-MM-DD HH:mm:ss"),category:null,creationCategory:null,visitTimeFrom:null,visitTimeTo:null,customerName:null,interviewers:[],subject:P.title,content:"",comments:[],isRead:!0,isStarred:!1,labels:ie,commentItems:[],attachments:[]}}),D=ee(n);D&&h.invalidate(`daily-report:date:${D}:user:${t}`),h.invalidatePrefix(v),h.invalidate(`${fe}:user:${t}`),await R(U),D&&await R(`${b}${D}`);let C=await be(f.reportHubId,t,{forceRefresh:!0});if(C){let z=await ue(),J=C.labels.some(P=>z.includes(P.id));await A(Be.parse({type:"report-create",reportHubId:C.reportHubId,report:C,clientTempId:s,recipientRawUserId:J?t:void 0}),"createDailyReport")}return C??f}catch(f){throw W.error("Error in createDailyReport",f),f}},Te=async(t,n)=>await t.select().top(1).from(c).where((0,a.eq)(c.id,n)),Ee=async(t,n,s)=>{await t.update(c).set(s).where((0,a.eq)(c.id,n))},Fe=async(t,n,s)=>{await t.update($).set(s).where((0,a.eq)($.hubId,n))},Ke=async(t,n,s)=>{await t.delete(j).where((0,a.and)((0,a.eq)(j.hubId,n),(0,a.eq)(j.labelId,s)))};return{streamKey:H,streamMaxLen:E,clearCache:async()=>{h.clearAll(),V.clear(),oe=null,O&&await R(U),W.info("[DailyReportService] Server-side DB/SQL caches cleared successfully.")},invalidateHubReadCaches:async(t,n)=>{h.invalidatePrefix(v);for(let s of new Set(t))h.invalidatePrefix(`daily-report:detail:${s}:user:`),await R(`${S}${s}`);for(let s of new Set(n)){let u=ee(s);u&&await R(`${b}${u}`)}},getUserIdByExternalId:ae,getDailyReportIdsByExternalId:de,getDailyReportsByBusinessDate:ge,getDailyReportsByBusinessDateByExternalId:ot,getDailyReportDetailById:be,getDailyReportDetailByIdByExternalId:st,getDraftLabelId:pe,getAttachmentsByHubIds:Ce,getAttachmentForUser:rt,markAttachmentMissing:nt,markAttachmentPresent:at,setStarStatus:it,setReadStatus:lt,addComment:dt,deleteComment:ct,createDailyReport:mt,updateDailyReport:async(t,n,s,u)=>{let g=await Te(e,t);if(!g.length||g[0].deletedAt)throw new Error("Not Found");if(g[0].userId!==n)throw new Error("Unauthorized");await e.transaction(async D=>{s.title!==void 0&&await Ee(D,t,{title:s.title,updatedAt:new Date,updatedBy:String(n)}),s.content!==void 0&&await Fe(D,t,{body:s.content,updatedAt:new Date,updatedBy:String(n)})}),h.invalidatePrefix(`daily-report:detail:${t}:user:`),h.invalidatePrefix(v);let f=await be(t,n,{forceRefresh:!0});if(f){let D=await ue(),C=f.labels.some(z=>D.includes(z.id));await A(Ue.parse({type:"report-update",reportHubId:f.reportHubId,report:f,clientTempId:u,recipientRawUserId:C?n:void 0}),"updateDailyReport")}},publishDailyReport:async(t,n,s)=>{let u=await Te(e,t);if(!u.length)throw new Error("Not Found");if(u[0].userId!==n)throw new Error("Unauthorized");let g=await pe();if(!g)return null;if(await Ke(e,t,g),h.invalidatePrefix(`daily-report:detail:${t}:user:`),h.invalidatePrefix(v),h.invalidatePrefix(fe),await R(U),u[0].businessDate){let C=ee(_(u[0].businessDate,"YYYY-MM-DD"));C&&await R(`${b}${C}`)}let f=ee(_(u[0].businessDate,"YYYY-MM-DD"));f&&h.invalidate(`daily-report:date:${f}:user:${n}`);let D=await be(t,n,{forceRefresh:!0});return D&&await A(He.parse({type:"report-publish",reportHubId:D.reportHubId,report:D,clientTempId:s}),"publishDailyReport"),D??null},deleteDailyReport:async(t,n,s)=>{let u=await Te(e,t);if(!u.length||u[0].deletedAt)throw new Error("Not Found");if(u[0].userId!==n)throw new Error("Unauthorized");if(await Ee(e,t,{deletedAt:new Date,deletedBy:String(n),updatedAt:new Date,updatedBy:String(n)}),h.invalidatePrefix(`daily-report:detail:${t}:user:`),h.invalidatePrefix(v),h.invalidatePrefix(fe),await R(U),u[0].businessDate){let g=ee(_(u[0].businessDate,"YYYY-MM-DD"));g&&await R(`${b}${g}`)}await A(qe.parse({type:"report-delete",reportHubId:t,clientTempId:s}),"deleteDailyReport")},findDailyReportHubById:Te,updateDailyReportHub:Ee,updateDailyReportInternal:Fe,deleteDailyReportLabel:Ke}}var Se=require("drizzle-orm");function et(r){let e=[],o=r&&r.length>0?r:[{key:"internal",name:"Internal",description:"Internal daily reports"},{key:"external",name:"External",description:"External daily reports"}];for(let l of o){let y=`daily_report_${l.key.toLowerCase().replace(/[^a-z0-9_]/g,"_")}`;e.push({resourceKey:y,name:l.name,description:l.description??`${l.name} daily report access`}),l.includeCommentResource!==!1&&e.push({resourceKey:`${y}_comment`,name:`${l.name} Comment`,description:`${l.name} daily report comment access`})}return e}async function Ot(r,e,o){let l=r,p=e.TMResource,y=o.appKey,L=o.actor??"system:daily-report",h=new Date,O=o.resources??et(o.sources);for(let c of O){let $=await l.select().from(p).where((0,Se.and)((0,Se.eq)(p.appKey,y),(0,Se.eq)(p.resourceKey,c.resourceKey)));(!$||$.length===0)&&await l.insert(p).values({appKey:y,resourceKey:c.resourceKey,name:c.name,description:c.description??null,createdAt:h,createdBy:L,updatedAt:h,updatedBy:L})}}var zt=re(1,"[DailyReportExternalSource]"),Ft=(r,e,o,l=zt)=>{if(!r)return[];try{let p=JSON.parse(r);return Array.isArray(p)?p.map(o).filter(y=>y!==null):(l.warn(`Unexpected ${e} format: not an array`),[])}catch(p){return l.warn(`Failed to parse ${e}:`,p),[]}};var Ie=require("drizzle-orm"),i=require("drizzle-orm/mssql-core");function Kt(r,e){let o=(0,i.mssqlSchema)(r),l=e.userTable,p=o.table("DailyReportHub",{id:(0,i.bigint)("id",{mode:"number"}).identity().notNull(),sourceType:(0,i.nvarchar)("source_type",{length:20}).notNull(),sourceId:(0,i.nvarchar)("source_id",{length:100}).notNull(),sourceIdNum:(0,i.bigint)("source_id_num",{mode:"number"}).generatedAlwaysAs(Ie.sql`TRY_CAST(source_id AS BIGINT)`),businessDate:(0,i.date)("business_date"),userId:(0,i.bigint)("user_id",{mode:"number"}),title:(0,i.nvarchar)("title",{length:200}),summary:(0,i.nvarchar)("summary",{length:"max"}),createdAt:(0,i.datetime2)("created_at").notNull(),createdBy:(0,i.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,i.datetime2)("updated_at").notNull(),updatedBy:(0,i.nvarchar)("updated_by",{length:50}).notNull(),deletedAt:(0,i.datetime2)("deleted_at"),deletedBy:(0,i.nvarchar)("deleted_by",{length:50})},m=>[(0,i.primaryKey)({name:`${r}_DailyReportHub_pk`,columns:[m.id]}),(0,i.foreignKey)({name:`${r}_DailyReportHub_user_id_fk`,columns:[m.userId],foreignColumns:[l.id]}),(0,i.index)(`${r}_DailyReportHub_business_date_index`).on(m.businessDate),(0,i.index)(`${r}_DailyReportHub_updated_at_index`).on(m.updatedAt),(0,i.index)(`${r}_DailyReportHub_source_index`).on(m.sourceType,m.sourceId),(0,i.index)(`${r}_DailyReportHub_user_id_index`).on(m.userId),(0,i.index)(`${r}_DailyReportHub_business_date_id_index`).on((0,Ie.desc)(m.businessDate),(0,Ie.desc)(m.id)),(0,i.index)(`${r}_DailyReportHub_deleted_at_index`).on(m.deletedAt)]),y=o.table("DailyReportInternal",{hubId:(0,i.bigint)("hub_id",{mode:"number"}).notNull(),body:(0,i.nvarchar)("body",{length:"max"}),metadata:(0,i.nvarchar)("metadata",{length:"max"}),createdAt:(0,i.datetime2)("created_at").notNull(),createdBy:(0,i.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,i.datetime2)("updated_at").notNull(),updatedBy:(0,i.nvarchar)("updated_by",{length:50}).notNull()},m=>[(0,i.primaryKey)({name:`${r}_DailyReportInternal_pk`,columns:[m.hubId]}),(0,i.foreignKey)({name:`${r}_DailyReportInternal_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]})]),L=o.table("DailyReportComment",{id:(0,i.bigint)("id",{mode:"number"}).identity().notNull(),hubId:(0,i.bigint)("hub_id",{mode:"number"}).notNull(),userId:(0,i.bigint)("user_id",{mode:"number"}).notNull(),body:(0,i.nvarchar)("body",{length:"max"}).notNull(),createdAt:(0,i.datetime2)("created_at").notNull(),createdBy:(0,i.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,i.datetime2)("updated_at").notNull(),updatedBy:(0,i.nvarchar)("updated_by",{length:50}).notNull()},m=>[(0,i.primaryKey)({name:`${r}_DailyReportComment_pk`,columns:[m.id]}),(0,i.foreignKey)({name:`${r}_DailyReportComment_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),(0,i.foreignKey)({name:`${r}_DailyReportComment_user_id_fk`,columns:[m.userId],foreignColumns:[l.id]}),(0,i.index)(`${r}_DailyReportComment_hub_id_index`).on(m.hubId)]),h=o.table("DailyReportLabel",{id:(0,i.bigint)("id",{mode:"number"}).identity().notNull(),userId:(0,i.bigint)("user_id",{mode:"number"}),name:(0,i.nvarchar)("name",{length:50}).notNull(),color:(0,i.nvarchar)("color",{length:20}),sortOrder:(0,i.int)("sort_order"),createdAt:(0,i.datetime2)("created_at").notNull(),createdBy:(0,i.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,i.datetime2)("updated_at").notNull(),updatedBy:(0,i.nvarchar)("updated_by",{length:50}).notNull()},m=>[(0,i.primaryKey)({name:`${r}_DailyReportLabel_pk`,columns:[m.id]}),(0,i.foreignKey)({name:`${r}_DailyReportLabel_user_id_fk`,columns:[m.userId],foreignColumns:[l.id]})]),O=o.table("DailyReportHub_Label",{hubId:(0,i.bigint)("hub_id",{mode:"number"}).notNull(),labelId:(0,i.bigint)("label_id",{mode:"number"}).notNull(),createdAt:(0,i.datetime2)("created_at").notNull(),createdBy:(0,i.nvarchar)("created_by",{length:50}).notNull()},m=>[(0,i.primaryKey)({name:`${r}_DailyReportHub_Label_pk`,columns:[m.hubId,m.labelId]}),(0,i.foreignKey)({name:`${r}_DailyReportHub_Label_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),(0,i.foreignKey)({name:`${r}_DailyReportHub_Label_label_id_fk`,columns:[m.labelId],foreignColumns:[h.id]})]),c=o.table("DailyReportUserStatus",{hubId:(0,i.bigint)("hub_id",{mode:"number"}).notNull(),userId:(0,i.bigint)("user_id",{mode:"number"}).notNull(),isRead:(0,i.bit)("is_read").default(!1).notNull(),isStarred:(0,i.bit)("is_starred").default(!1).notNull(),createdAt:(0,i.datetime2)("created_at").notNull(),createdBy:(0,i.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,i.datetime2)("updated_at").notNull(),updatedBy:(0,i.nvarchar)("updated_by",{length:50}).notNull()},m=>[(0,i.primaryKey)({name:`${r}_DailyReportUserStatus_pk`,columns:[m.hubId,m.userId]}),(0,i.foreignKey)({name:`${r}_DailyReportUserStatus_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),(0,i.foreignKey)({name:`${r}_DailyReportUserStatus_user_id_fk`,columns:[m.userId],foreignColumns:[l.id]})]),$=o.table("DailyReportAttachment",{id:(0,i.bigint)("id",{mode:"number"}).identity().notNull(),hubId:(0,i.bigint)("hub_id",{mode:"number"}).notNull(),uploadedBy:(0,i.bigint)("uploaded_by",{mode:"number"}),sourceKey:(0,i.nvarchar)("source_key",{length:200}).notNull(),fileName:(0,i.nvarchar)("file_name",{length:255}).notNull(),filePath:(0,i.nvarchar)("file_path",{length:"max"}).notNull(),fileType:(0,i.nvarchar)("file_type",{length:100}),fileSize:(0,i.bigint)("file_size",{mode:"number"}),objectCheckedAt:(0,i.datetime2)("object_checked_at"),objectMissingAt:(0,i.datetime2)("object_missing_at"),createdAt:(0,i.datetime2)("created_at").notNull(),createdBy:(0,i.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,i.datetime2)("updated_at").notNull(),updatedBy:(0,i.nvarchar)("updated_by",{length:50}).notNull()},m=>[(0,i.primaryKey)({name:`${r}_DailyReportAttachment_pk`,columns:[m.id]}),(0,i.foreignKey)({name:`${r}_DailyReportAttachment_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),(0,i.foreignKey)({name:`${r}_DailyReportAttachment_uploaded_by_fk`,columns:[m.uploadedBy],foreignColumns:[l.id]}),(0,i.uniqueIndex)(`${r}_DailyReportAttachment_source_unique`).on(m.hubId,m.sourceKey),(0,i.index)(`${r}_DailyReportAttachment_hub_id_index`).on(m.hubId,m.createdAt)]);return{hub:p,internal:y,comment:L,attachment:$,label:h,hubLabel:O,userStatus:c}}function jt(r){let e=xe(r.redis),o=new Re({defaultTtlMs:r.cacheDefaultTtlMs??6e4},e),l=ze({...r,cache:o,epochs:e}),p=new De({redis:r.redis,streamKey:l.streamKey,cache:o,logger:r.logger}),y=Oe({authenticate:r.authenticate,service:l,encodeUserId:r.encodeUserId,attachmentIdCodec:r.attachmentIdCodec,readAttachment:r.readAttachment,attachmentMaxBytes:r.attachmentMaxBytes,attachmentRateLimitPerMinute:r.attachmentRateLimitPerMinute,attachmentConcurrency:r.attachmentConcurrency,redis:r.redis,sseReader:p,streamKey:l.streamKey,loginRedirectPath:r.loginRedirectPath,logger:r.logger});return{service:l,cache:o,epochs:e,sseReader:p,streamKey:l.streamKey,...y}}0&&(module.exports={DailyReportSseReader,SqlResultCache,asciiFallbackFileName,createDailyReportHandlers,createDailyReportServer,createDailyReportService,createEpochStore,defineDailyReportAuthzResources,defineDailyReportSchema,encodeRfc8187,generateETag,isStreamIdLte,jsonResponseWithETag,sanitizeMediaType,seedDailyReportAuthzResources,transformJsonArray});
11
+ `,W=G!=="0-0"?`id: ${G}
12
+ ${T}`:T;E.enqueue($.encode(W))}if(C)try{let T=await y?.getClient();if(T){let W=await T.xRange(M,C,"+",{COUNT:1e3});for(let te of W)te.id!==C&&(we(te.id,G)||(G=te.id,g(te.id,te.message)))}}catch(T){u.error(`[SSE:${x}] catch-up xRange error:`,T)}},cancel(){_()}});return new Response(P,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive"}})}},attachment:{loader:async({request:k,params:H})=>{let v=null,x=(R,C,$)=>{let E=new Headers({"Content-Type":"application/json"});return $!==void 0&&E.set("Retry-After",String($)),v&&E.append("Set-Cookie",v),new Response(JSON.stringify({error:{message:C}}),{status:R,headers:E})};try{let{user:R,cookie:C}=await e(k,{failureRedirect:null});if(v=C??null,!R)return x(401,"Unauthorized");if(k.method!=="GET"&&k.method!=="HEAD")return x(405,"Method not allowed");let $=t.attachmentIdCodec,E=t.readAttachment;if(!($&&E))return x(404,"Attachment not found");let D=H.token??"",w=$.decode(D);if(w===null||!Number.isSafeInteger(w)||w<=0)return x(400,"Invalid attachment token");let I=await s.getUserIdByExternalId(R.id);if(!I)return x(403,"Forbidden");if(!K(I,Date.now()))return N.error(`429 attachment=${w} viewer=${I} reason=rate_limit`),x(429,"Too many requests",Kt);let _=await s.getAttachmentForUser(w,I);if(!_)return N.error(`404 attachment=${w} viewer=${I} reason=not_visible`),x(404,"Attachment not found");if(_.fileSize!=null&&_.fileSize>p)return N.error(`413 attachment=${w} viewer=${I} reason=db_size`),x(413,"Attachment too large");if(!q.tryAcquire())return N.error(`503 attachment=${w} viewer=${I} reason=concurrency`),x(503,"Too many concurrent downloads",Jt);let g=!1,P=()=>{g||(g=!0,q.release())},F=!1;try{let G=k.method==="HEAD",T=await E(_.filePath,{maxBytes:p,head:G,principal:i(I)});if(!T.ok){let oe=Xt(T.reason);return T.reason==="not_found"&&s.markAttachmentMissing(w).catch(le=>{N.error(`markAttachmentMissing failed attachment=${w} message=${le instanceof Error?le.message:"unknown"}`)}),N.error(`${oe} attachment=${w} viewer=${I} reason=${T.reason} code=${T.code??"-"}`),x(oe,Wt[oe]??"Attachment storage unavailable")}let W=Fe(_.fileType)??Fe(T.contentType)??"application/octet-stream",te=jt.has(W),se=new URL(k.url).searchParams.get("download")==="1",re=te?W:"application/octet-stream",be=te&&!se?"inline":"attachment",ye=T.size??T.bytes.byteLength,ie=new Headers({"Content-Type":re,"Content-Length":String(ye),"Content-Disposition":`${be}; filename="${it(_.fileName)}"; filename*=UTF-8''${ot(_.fileName)}`,"Cache-Control":"private, no-store","X-Content-Type-Options":"nosniff","Content-Security-Policy":"default-src 'none'; sandbox","X-Frame-Options":"SAMEORIGIN"});return v&&ie.append("Set-Cookie",v),s.markAttachmentPresent(w).catch(oe=>{N.error(`markAttachmentPresent failed attachment=${w} message=${oe instanceof Error?oe.message:"unknown"}`)}),N.info(`200 attachment=${w} viewer=${I} type=${re} disposition=${be} bytes=${ye}`),G?new Response(null,{status:200,headers:ie}):(F=!0,new Response(er(T.bytes,P,Ft),{status:200,headers:ie}))}finally{F||P()}}catch(R){if(R instanceof Response)throw R;return N.error(`500 attachment=? viewer=? reason=unexpected message=${R instanceof Error?R.message:"unknown"}`),x(500,"Attachment request failed")}}}}}var n=require("drizzle-orm");var pe=t=>(0,n.getColumns)(t);function Ke(t){let{db:e,tables:s,userTable:i,resolveUserId:y,encodeUserId:m,redis:M,cache:h,epochs:O}=t,{hub:u,internal:N,comment:p,label:K,hubLabel:q,userStatus:U}=s,me=t.externalSources??[],Ae=t.draftLabelNames&&t.draftLabelNames.length>0?t.draftLabelNames:[t.draftLabelName??"\u4E0B\u66F8\u304D"],Ce=Ae[0]??"\u4E0B\u66F8\u304D",V=t.logger??ne(1,"[DailyReportService]"),fe="daily-report:ids",k=t.idsTtlMs??18e4,H="daily-report:ids:epoch",v="daily-report:business-date:",x=t.businessDateTtlMs??3e5,R="daily-report:date-epoch:",C="daily-report:detail-epoch:",$=t.streamKey??"daily-report:sse-stream",E=t.streamMaxLen??1e4,D=r=>O.incrementEpoch(r),w=async(r,a)=>{let o=await M?.getClient();if(!o){V.warn(`[SSE] Redis client unavailable (${a})`);return}let c=Date.now();try{await o.xAdd($,"*",{data:JSON.stringify(r)},{TRIM:{strategy:"MAXLEN",strategyModifier:"~",threshold:E}});let b=Date.now()-c;b>1e3&&V.warn(`[SSE] Slow publish (${a}): ${b}ms`)}catch(b){V.error(`[SSE] Redis publish failed (${a}):`,b)}},I=(r,a)=>{if(!r)return null;let o=r instanceof Date?r:new Date(r);if(Number.isNaN(o.getTime()))return typeof r=="string"?r:null;let c=a==="YYYY-MM-DD HH:mm:ss"?{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}:{year:"numeric",month:"2-digit",day:"2-digit"};return(a==="YYYY-MM-DD HH:mm:ss"?o.toLocaleString("ja-JP",c):o.toLocaleDateString("ja-JP",c)).replace(/\//g,"-")},_=r=>r==null||r===""?null:/^\d+$/.test(r)?m(Number(r)):r,g=()=>{let r={};for(let a of me)r[`ext_${a.sourceType}`]=pe(a.table);return r},P=r=>{let a=r;for(let o of me)a=a.leftJoin(o.table,(0,n.and)((0,n.eq)(u.sourceType,o.sourceType),(0,n.eq)(u.sourceIdNum,o.idColumn)));return a},F=(r,a)=>{let{hub:o,internal:c}=r,b=o.summary,f=[],S=null,A=null,z=null,j=null,Y=null,Q=null,ue=[],ce=me.find(B=>B.sourceType===o.sourceType),he=ce?r[`ext_${ce.sourceType}`]:void 0;if(ce&&he){let B=ce.mapRow(he);B.content!==void 0&&(b=B.content),B.employeeName!==void 0&&(Q=B.employeeName),B.category!==void 0&&(S=B.category),B.creationCategory!==void 0&&(A=B.creationCategory),B.visitTimeFrom!==void 0&&(z=B.visitTimeFrom),B.visitTimeTo!==void 0&&(j=B.visitTimeTo),B.customerName!==void 0&&(Y=B.customerName),B.interviewers!==void 0&&(f=B.interviewers),B.comments!==void 0&&(ue=B.comments)}else o.sourceType==="Internal"&&c&&(b=c.body);let de=(r.labels??[]).map(B=>({id:B.id,name:B.name,color:B.color})),Z=r.comments??[],J=A?Ae.includes(A):!1,X=ie?.[0]??null;return J&&X&&!de.some(B=>B.id===X)&&de.push({id:X,name:Ce,color:null}),{reportHubId:o.id,date:I(o.businessDate,"YYYY-MM-DD"),author:r.creatorName??_(o.createdBy)??"",userId:o.userId?m(o.userId):"",sourceType:o.sourceType??"Internal",createdAt:I(o.createdAt,"YYYY-MM-DD HH:mm:ss"),updatedAt:I(o.updatedAt,"YYYY-MM-DD HH:mm:ss"),updatedBy:_(o.updatedBy),employeeName:Q??r.creatorName??_(o.createdBy),category:S,creationCategory:A,visitTimeFrom:z,visitTimeTo:j,customerName:Y,interviewers:f,subject:o.title,content:b,comments:ue,isRead:r.isRead??!1,isStarred:r.isStarred??!1,labels:de,commentItems:Z.map(B=>({...B,userId:m(B.userId),isMine:a?B.userId===a:!1})),attachments:r.attachments}},G=async r=>{let a=(0,n.aliasedTable)(q,"draft_label"),o=await le();return(await e.select({reportHubId:u.id,businessDate:u.businessDate,sourceType:u.sourceType}).from(u).leftJoin(a,(0,n.and)((0,n.eq)(a.hubId,u.id),(0,n.inArray)(a.labelId,o))).where((0,n.and)((0,n.isNull)(u.deletedAt),(0,n.or)((0,n.eq)(u.userId,r),(0,n.isNull)(u.userId),(0,n.isNull)(a.hubId)))).orderBy((0,n.desc)(u.businessDate),(0,n.desc)(u.id))).map(b=>({...b,businessDate:I(b.businessDate,"YYYY-MM-DD")}))},T=async(r,a)=>{let o=await re(r);if(!o)return[];let c=(0,n.aliasedTable)(q,"draft_label"),b=await le();return(await e.select({reportHubId:u.id,businessDate:u.businessDate,sourceType:u.sourceType}).top(a).from(u).leftJoin(c,(0,n.and)((0,n.eq)(c.hubId,u.id),(0,n.inArray)(c.labelId,b))).where((0,n.and)((0,n.isNull)(u.deletedAt),(0,n.or)((0,n.eq)(u.userId,o),(0,n.isNull)(u.userId),(0,n.isNull)(c.hubId)))).orderBy((0,n.desc)(u.businessDate),(0,n.desc)(u.id))).map(S=>({...S,businessDate:I(S.businessDate,"YYYY-MM-DD")}))},W=async r=>{let a=await P(e.select({hub:pe(u),internal:pe(N),creatorName:i.displayName,...g()}).from(u)).leftJoin(N,(0,n.eq)(u.id,N.hubId)).leftJoin(i,(0,n.eq)(u.userId,i.id)).where((0,n.and)((0,n.eq)(u.businessDate,n.sql`${r}`),(0,n.isNull)(u.deletedAt))).orderBy((0,n.desc)(u.id)),o=await _e(a.map(c=>c.hub.id));return a.map(c=>F({...c,attachments:o.get(c.hub.id)??[]}))},te=r=>`${v}${r}`,se=new Map,re=async r=>{if(!t.disableUserIdCache&&se.has(r))return se.get(r)??null;let a=await y(r);return a!==null&&!t.disableUserIdCache&&se.set(r,a),a},be=async(r,{forceRefresh:a=!1,snapshot:o=!1,ttlMsOverride:c}={})=>{let b=await re(r);if(!b)return[];let f=`${fe}:user:${b}`;return h.getOrFetch({cacheKey:f,fetcher:async()=>await G(b),forceRefresh:a,snapshot:o,ttlMsOverride:c??k,epochKey:H})},ye=(r,{forceRefresh:a=!1,snapshot:o=!1,ttlMsOverride:c}={})=>{let b=ee(r);return b?h.getOrFetch({cacheKey:te(b),fetcher:()=>W(b),forceRefresh:a,snapshot:o,ttlMsOverride:c??x}):Promise.resolve([])},ie=null,oe=async()=>(ie!==null||(ie=(await e.select({id:K.id}).from(K).where((0,n.inArray)(K.name,Ae)).orderBy(K.id)).map(a=>a.id)),ie),le=async()=>{let r=await oe();return r.length>0?r:[-1]},xe=async()=>{let r=await oe();if(r.length===0)return null;let[a]=await e.select({id:K.id}).top(1).from(K).where((0,n.eq)(K.name,Ce));return a?.id??r[0]},ut=(r,a)=>a!=null?"absent":r!=null?"present":"unknown",_e=async r=>{let a=new Map,o=s.attachment,c=t.attachmentIdCodec;if(r.length===0||!o||!c)return a;let b=await e.select({id:o.id,hubId:o.hubId,fileName:o.fileName,fileType:o.fileType,fileSize:o.fileSize,createdAt:o.createdAt,objectCheckedAt:o.objectCheckedAt,objectMissingAt:o.objectMissingAt}).from(o).where((0,n.inArray)(o.hubId,r)).orderBy((0,n.asc)(o.createdAt),(0,n.asc)(o.id));for(let f of b){let S={id:c.encode(Number(f.id)),fileName:String(f.fileName),fileType:f.fileType?String(f.fileType):null,fileSize:f.fileSize!=null?Number(f.fileSize):null,createdAt:I(f.createdAt,"YYYY-MM-DD HH:mm:ss"),state:ut(f.objectCheckedAt,f.objectMissingAt)},A=a.get(Number(f.hubId));A?A.push(S):a.set(Number(f.hubId),[S])}return a},dt=async(r,a)=>{let o=s.attachment;if(!o)return null;let c=await le(),b=(0,n.aliasedTable)(q,"draft_label");return(await e.select({fileName:o.fileName,filePath:o.filePath,fileType:o.fileType,fileSize:o.fileSize}).top(1).from(o).innerJoin(u,(0,n.eq)(u.id,o.hubId)).leftJoin(b,(0,n.and)((0,n.eq)(b.hubId,u.id),(0,n.inArray)(b.labelId,c))).where((0,n.and)((0,n.eq)(o.id,r),(0,n.isNull)(u.deletedAt),(0,n.or)((0,n.eq)(u.userId,a),(0,n.isNull)(b.hubId)))))[0]??null},ct=async r=>{let a=s.attachment;if(!a)return;let o=new Date;try{await e.update(a).set({objectCheckedAt:o,objectMissingAt:o,updatedAt:o}).where((0,n.eq)(a.id,r))}catch(c){V.error("markAttachmentMissing failed",c)}},mt=async r=>{let a=s.attachment;if(!a)return;let o=new Date;try{await e.update(a).set({objectCheckedAt:o,objectMissingAt:null,updatedAt:o}).where((0,n.eq)(a.id,r))}catch(c){V.error("markAttachmentPresent failed",c)}},pt=async(r,a,{forceRefresh:o=!1,snapshot:c=!1,ttlMsOverride:b}={})=>{let f=await re(a);if(!f)return[];let S=ee(r);if(!S)return[];let A=`daily-report:date:${S}:user:${f}`;return h.getOrFetch({cacheKey:A,fetcher:async()=>{let z=await le(),j=(0,n.aliasedTable)(q,"draft_label"),Y=await P(e.select({hub:pe(u),internal:pe(N),isRead:U.isRead,isStarred:U.isStarred,creatorName:i.displayName,...g()}).from(u)).leftJoin(N,(0,n.eq)(u.id,N.hubId)).leftJoin(U,(0,n.and)((0,n.eq)(u.id,U.hubId),(0,n.eq)(U.userId,f))).leftJoin(j,(0,n.and)((0,n.eq)(j.hubId,u.id),(0,n.inArray)(j.labelId,z))).leftJoin(i,(0,n.eq)(u.userId,i.id)).where((0,n.and)((0,n.eq)(u.businessDate,n.sql`${S}`),(0,n.isNull)(u.deletedAt),(0,n.or)((0,n.eq)(u.userId,f),(0,n.isNull)(j.hubId)))).orderBy((0,n.desc)(u.id));if(Y.length===0)return[];let Q=Y.map(J=>J.hub.id),ue=await e.select({hubId:q.hubId,id:K.id,name:K.name,color:K.color}).from(q).innerJoin(K,(0,n.eq)(q.labelId,K.id)).where((0,n.inArray)(q.hubId,Q)),ce=await e.select({hubId:p.hubId,id:p.id,body:p.body,createdAt:p.createdAt,userId:p.userId,userName:i.displayName}).from(p).leftJoin(i,(0,n.eq)(p.userId,i.id)).where((0,n.inArray)(p.hubId,Q)).orderBy((0,n.asc)(p.createdAt)),he=await _e(Q),de=new Map,Z=new Map;for(let J of ue){let X=de.get(J.hubId);X||(X=[],de.set(J.hubId,X)),X.push({id:J.id,name:J.name,color:J.color})}for(let J of ce){let X=Z.get(J.hubId);X||(X=[],Z.set(J.hubId,X)),X.push({id:J.id,content:J.body,createdAt:I(J.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:J.userId,userName:J.userName||""})}return Y.map(J=>{let X=J.hub.id;return F({...J,attachments:he.get(X)??[],labels:de.get(X)||[],comments:Z.get(X)||[]},f)})},forceRefresh:o,snapshot:c,ttlMsOverride:b??x,epochKey:`${R}${S}`})},ge=async(r,a,{forceRefresh:o=!1,snapshot:c=!1,ttlMsOverride:b}={})=>{let f=`daily-report:detail:${r}:user:${a}`;return(await h.getOrFetch({cacheKey:f,fetcher:async()=>{let A=await le(),z=(0,n.aliasedTable)(q,"draft_label"),j=await P(e.select({hub:pe(u),internal:pe(N),isRead:U.isRead,isStarred:U.isStarred,creatorName:i.displayName,...g()}).from(u)).leftJoin(N,(0,n.eq)(u.id,N.hubId)).leftJoin(U,(0,n.and)((0,n.eq)(u.id,U.hubId),(0,n.eq)(U.userId,a))).leftJoin(z,(0,n.and)((0,n.eq)(z.hubId,u.id),(0,n.inArray)(z.labelId,A))).leftJoin(i,(0,n.eq)(u.userId,i.id)).where((0,n.and)((0,n.eq)(u.id,r),(0,n.isNull)(u.deletedAt),(0,n.or)((0,n.eq)(u.userId,a),(0,n.isNull)(z.hubId))));if(j.length===0)return[];let Y=j[0].hub.id,Q=await e.select({id:K.id,name:K.name,color:K.color}).from(q).innerJoin(K,(0,n.eq)(q.labelId,K.id)).where((0,n.eq)(q.hubId,Y)),ue=await e.select({id:p.id,body:p.body,createdAt:p.createdAt,userId:p.userId,userName:i.displayName}).from(p).leftJoin(i,(0,n.eq)(p.userId,i.id)).where((0,n.eq)(p.hubId,Y)).orderBy((0,n.asc)(p.createdAt)),ce=await _e([Y]),he=Q.map(Z=>({id:Z.id,name:Z.name,color:Z.color})),de=ue.map(Z=>({id:Z.id,content:Z.body,createdAt:I(Z.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:Z.userId,userName:Z.userName||""}));return[F({...j[0],attachments:ce.get(Y)??[],labels:he,comments:de},a)]},forceRefresh:o,snapshot:c,ttlMsOverride:b??x,epochKey:`${C}${r}`}))[0]??null},yt=async(r,a,o={})=>{let c=await re(a);return c?ge(r,c,o):null},ht=async(r,a,o,c,b)=>{let f=await e.select().top(1).from(U).where((0,n.and)((0,n.eq)(U.hubId,a),(0,n.eq)(U.userId,r))),S;if(f.length>0?f[0].isStarred!==c?S=(await e.update(U).set({isStarred:c,updatedAt:new Date,updatedBy:String(r)}).output().where((0,n.and)((0,n.eq)(U.hubId,a),(0,n.eq)(U.userId,r))))[0]:S=f[0]:S=(await e.insert(U).output().values({hubId:a,userId:r,isStarred:c,isRead:!1,createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}))[0],o){let A=ee(o);A&&(h.invalidate(`daily-report:date:${A}:user:${r}`),await D(`${R}${A}`))}return h.invalidate(`daily-report:detail:${a}`),h.invalidate(`daily-report:detail:${a}:user:${r}`),await D(`${C}${a}`),await w(Me.parse({type:"status-update",reportHubId:a,recipientRawUserId:r,statusType:"star",value:c,clientTempId:b}),"setStarStatus"),S},ft=async(r,a,o,c,b)=>{let f=await e.select().top(1).from(U).where((0,n.and)((0,n.eq)(U.hubId,a),(0,n.eq)(U.userId,r))),S;if(f.length>0?f[0].isRead!==c?S=(await e.update(U).set({isRead:c,updatedAt:new Date,updatedBy:String(r)}).output().where((0,n.and)((0,n.eq)(U.hubId,a),(0,n.eq)(U.userId,r))))[0]:S=f[0]:S=(await e.insert(U).output().values({hubId:a,userId:r,isRead:c,isStarred:!1,createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}))[0],o){let A=ee(o);A&&(h.invalidate(`daily-report:date:${A}:user:${r}`),await D(`${R}${A}`))}return h.invalidate(`daily-report:detail:${a}`),h.invalidate(`daily-report:detail:${a}:user:${r}`),await D(`${C}${a}`),await w(Me.parse({type:"status-update",reportHubId:a,recipientRawUserId:r,statusType:"read",value:c,clientTempId:b}),"setReadStatus"),S},Je=async(r,a)=>{if(a===null)return;let o=await le(),[c]=await e.select({hubId:q.hubId}).top(1).from(q).where((0,n.and)((0,n.eq)(q.hubId,r),(0,n.inArray)(q.labelId,o)));return c?a:void 0},bt=async(r,a,o,c,b)=>{let[f]=await e.select({sourceType:u.sourceType,deletedAt:u.deletedAt,userId:u.userId}).top(1).from(u).where((0,n.eq)(u.id,a));if(!f||f.deletedAt)throw new Error("Not Found");if(f.sourceType.toLowerCase()!=="internal")throw new Error("Comments are restricted for external daily report sources");let S=await Je(a,f.userId),[A]=await e.insert(p).output().values({hubId:a,userId:r,body:o,createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}),[z]=await e.select({displayName:i.displayName}).top(1).from(i).where((0,n.eq)(i.id,r)),j=z?.displayName??"Unknown";if(c){let Q=ee(c);Q&&(h.invalidatePrefix(`daily-report:date:${Q}:user:`),await D(`${R}${Q}`))}h.invalidate(`daily-report:detail:${a}`),h.invalidatePrefix(`daily-report:detail:${a}:user:`),await D(`${C}${a}`);let Y={id:A.id,userId:m(A.userId),userName:j,content:A.body,createdAt:I(A.createdAt,"YYYY-MM-DD HH:mm:ss")??"",isMine:!0};return await w(Be.parse({type:"comment-add",reportHubId:a,comment:Y,clientTempId:b,recipientRawUserId:S}),"addComment"),Y},gt=async(r,a)=>await r.select().top(1).from(p).where((0,n.eq)(p.id,a)),Rt=async(r,a,o,c,b)=>{let f=await gt(e,o);if(f.length===0)throw new Error("Not Found");if(f[0].userId!==r)throw new Error("Unauthorized");if(f[0].hubId!==a)throw new Error("Not Found");let[S]=await e.select({userId:u.userId}).top(1).from(u).where((0,n.eq)(u.id,a)),A=await Je(a,S?.userId??null);if(await e.delete(p).where((0,n.eq)(p.id,o)),c){let z=ee(c);z&&(h.invalidatePrefix(`daily-report:date:${z}:user:`),await D(`${R}${z}`))}h.invalidate(`daily-report:detail:${a}`),h.invalidatePrefix(`daily-report:detail:${a}:user:`),await D(`${C}${a}`),await w(Ue.parse({type:"comment-delete",reportHubId:a,commentId:o,clientTempId:b,recipientRawUserId:A}),"deleteComment")},Dt=async(r,a,o)=>{V.info("createDailyReport called",{userId:r,businessDate:a});let[c]=await e.select({displayName:i.displayName}).from(i).where((0,n.eq)(i.id,r)),b=c?.displayName??m(r);try{let f=await e.transaction(async z=>{V.info("Starting transaction");let j=`internal-temp-${Date.now()}-${Math.random()}`,[Y]=await z.insert(u).output().values({sourceType:"Internal",sourceId:j,businessDate:new Date(a),userId:r,title:"(\u7121\u984C)",createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)});await z.update(u).set({sourceId:String(Y.id)}).where((0,n.eq)(u.id,Y.id)),V.info("Hub created",Y),await z.insert(N).values({hubId:Y.id,body:"",createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}),V.info("Internal created"),V.info("Calling getDraftLabelId");let Q=await xe();V.info("draftLabelId",Q);let ue=[];return Q&&(await z.insert(q).values({hubId:Y.id,labelId:Q,createdAt:new Date,createdBy:String(r)}),ue.push({id:Q,name:Ce,color:null})),V.info("Label assigned"),{reportHubId:Y.id,date:a,createdAt:I(Y.createdAt,"YYYY-MM-DD HH:mm:ss"),author:b,userId:m(r),sourceType:"Internal",employeeName:b,updatedBy:m(r),updatedAt:I(Y.updatedAt,"YYYY-MM-DD HH:mm:ss"),category:null,creationCategory:null,visitTimeFrom:null,visitTimeTo:null,customerName:null,interviewers:[],subject:Y.title,content:"",comments:[],isRead:!0,isStarred:!1,labels:ue,commentItems:[],attachments:[]}}),S=ee(a);S&&h.invalidate(`daily-report:date:${S}:user:${r}`),h.invalidatePrefix(v),h.invalidate(`${fe}:user:${r}`),await D(H),S&&await D(`${R}${S}`);let A=await ge(f.reportHubId,r,{forceRefresh:!0});if(A){let z=await oe(),j=A.labels.some(Y=>z.includes(Y.id));await w(He.parse({type:"report-create",reportHubId:A.reportHubId,report:A,clientTempId:o,recipientRawUserId:j?r:void 0}),"createDailyReport")}return A??f}catch(f){throw V.error("Error in createDailyReport",f),f}},Te=async(r,a)=>await r.select().top(1).from(u).where((0,n.eq)(u.id,a)),Ne=async(r,a,o)=>{await r.update(u).set(o).where((0,n.eq)(u.id,a))},je=async(r,a,o)=>{await r.update(N).set(o).where((0,n.eq)(N.hubId,a))},Ge=async(r,a,o)=>{await r.delete(q).where((0,n.and)((0,n.eq)(q.hubId,a),(0,n.eq)(q.labelId,o)))};return{streamKey:$,streamMaxLen:E,clearCache:async()=>{h.clearAll(),se.clear(),ie=null,O&&await D(H),V.info("[DailyReportService] Server-side DB/SQL caches cleared successfully.")},invalidateHubReadCaches:async(r,a)=>{h.invalidatePrefix(v);for(let o of new Set(r))h.invalidatePrefix(`daily-report:detail:${o}:user:`),await D(`${C}${o}`);for(let o of new Set(a)){let c=ee(o);c&&await D(`${R}${c}`)}},getUserIdByExternalId:re,getDailyReportIdsByExternalId:be,getDailyReportIdsFirstPageByExternalId:T,getDailyReportsByBusinessDate:ye,getDailyReportsByBusinessDateByExternalId:pt,getDailyReportDetailById:ge,getDailyReportDetailByIdByExternalId:yt,getDraftLabelId:xe,getAttachmentsByHubIds:_e,getAttachmentForUser:dt,markAttachmentMissing:ct,markAttachmentPresent:mt,setStarStatus:ht,setReadStatus:ft,addComment:bt,deleteComment:Rt,createDailyReport:Dt,updateDailyReport:async(r,a,o,c)=>{let b=await Te(e,r);if(!b.length||b[0].deletedAt)throw new Error("Not Found");if(b[0].userId!==a)throw new Error("Unauthorized");await e.transaction(async A=>{o.title!==void 0&&await Ne(A,r,{title:o.title,updatedAt:new Date,updatedBy:String(a)}),o.content!==void 0&&await je(A,r,{body:o.content,updatedAt:new Date,updatedBy:String(a)})}),h.invalidate(`daily-report:detail:${r}`),h.invalidatePrefix(`daily-report:detail:${r}:user:`),h.invalidatePrefix(v),await D(`${C}${r}`);let f=ee(I(b[0].businessDate,"YYYY-MM-DD"));f&&(h.invalidatePrefix(`daily-report:date:${f}:user:`),await D(`${R}${f}`));let S=await ge(r,a,{forceRefresh:!0});if(S){let A=await oe(),z=S.labels.some(j=>A.includes(j.id));await w(Ye.parse({type:"report-update",reportHubId:S.reportHubId,report:S,clientTempId:c,recipientRawUserId:z?a:void 0}),"updateDailyReport")}},publishDailyReport:async(r,a,o)=>{let c=await Te(e,r);if(!c.length)throw new Error("Not Found");if(c[0].userId!==a)throw new Error("Unauthorized");let b=await xe();if(!b)return null;if(await Ge(e,r,b),h.invalidatePrefix(`daily-report:detail:${r}:user:`),h.invalidatePrefix(v),h.invalidatePrefix(fe),await D(H),c[0].businessDate){let A=ee(I(c[0].businessDate,"YYYY-MM-DD"));A&&await D(`${R}${A}`)}let f=ee(I(c[0].businessDate,"YYYY-MM-DD"));f&&h.invalidate(`daily-report:date:${f}:user:${a}`);let S=await ge(r,a,{forceRefresh:!0});return S&&await w(Oe.parse({type:"report-publish",reportHubId:S.reportHubId,report:S,clientTempId:o}),"publishDailyReport"),S??null},deleteDailyReport:async(r,a,o)=>{let c=await Te(e,r);if(!c.length||c[0].deletedAt)throw new Error("Not Found");if(c[0].userId!==a)throw new Error("Unauthorized");if(await Ne(e,r,{deletedAt:new Date,deletedBy:String(a),updatedAt:new Date,updatedBy:String(a)}),h.invalidatePrefix(`daily-report:detail:${r}:user:`),h.invalidatePrefix(v),h.invalidatePrefix(fe),await D(H),c[0].businessDate){let b=ee(I(c[0].businessDate,"YYYY-MM-DD"));b&&await D(`${R}${b}`)}await w(qe.parse({type:"report-delete",reportHubId:r,clientTempId:o}),"deleteDailyReport")},findDailyReportHubById:Te,updateDailyReportHub:Ne,updateDailyReportInternal:je,deleteDailyReportLabel:Ge}}var Ie=require("drizzle-orm");function lt(t){let e=[],s=t&&t.length>0?t:[{key:"internal",name:"Internal",description:"Internal daily reports"},{key:"external",name:"External",description:"External daily reports"}];for(let i of s){let m=`daily_report_${i.key.toLowerCase().replace(/[^a-z0-9_]/g,"_")}`;e.push({resourceKey:m,name:i.name,description:i.description??`${i.name} daily report access`}),i.includeCommentResource!==!1&&e.push({resourceKey:`${m}_comment`,name:`${i.name} Comment`,description:`${i.name} daily report comment access`})}return e}async function tr(t,e,s){let i=t,y=e.TMResource,m=s.appKey,M=s.actor??"system:daily-report",h=new Date,O=s.resources??lt(s.sources);for(let u of O){let N=await i.select().from(y).where((0,Ie.and)((0,Ie.eq)(y.appKey,m),(0,Ie.eq)(y.resourceKey,u.resourceKey)));(!N||N.length===0)&&await i.insert(y).values({appKey:m,resourceKey:u.resourceKey,name:u.name,description:u.description??null,createdAt:h,createdBy:M,updatedAt:h,updatedBy:M})}}var rr=ne(1,"[DailyReportExternalSource]"),nr=(t,e,s,i=rr)=>{if(!t)return[];try{let y=JSON.parse(t);return Array.isArray(y)?y.map(s).filter(m=>m!==null):(i.warn(`Unexpected ${e} format: not an array`),[])}catch(y){return i.warn(`Failed to parse ${e}:`,y),[]}};var Se=require("drizzle-orm"),l=require("drizzle-orm/mssql-core");function ar(t,e){let s=(0,l.mssqlSchema)(t),i=e.userTable,y=s.table("DailyReportHub",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),sourceType:(0,l.nvarchar)("source_type",{length:20}).notNull(),sourceId:(0,l.nvarchar)("source_id",{length:100}).notNull(),sourceIdNum:(0,l.bigint)("source_id_num",{mode:"number"}).generatedAlwaysAs(Se.sql`TRY_CAST(source_id AS BIGINT)`),businessDate:(0,l.date)("business_date"),userId:(0,l.bigint)("user_id",{mode:"number"}),title:(0,l.nvarchar)("title",{length:200}),summary:(0,l.nvarchar)("summary",{length:"max"}),createdAt:(0,l.datetime2)("created_at").notNull(),createdBy:(0,l.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,l.datetime2)("updated_at").notNull(),updatedBy:(0,l.nvarchar)("updated_by",{length:50}).notNull(),deletedAt:(0,l.datetime2)("deleted_at"),deletedBy:(0,l.nvarchar)("deleted_by",{length:50})},p=>[(0,l.primaryKey)({name:`${t}_DailyReportHub_pk`,columns:[p.id]}),(0,l.foreignKey)({name:`${t}_DailyReportHub_user_id_fk`,columns:[p.userId],foreignColumns:[i.id]}),(0,l.index)(`${t}_DailyReportHub_business_date_index`).on(p.businessDate),(0,l.index)(`${t}_DailyReportHub_updated_at_index`).on(p.updatedAt),(0,l.index)(`${t}_DailyReportHub_source_index`).on(p.sourceType,p.sourceId),(0,l.index)(`${t}_DailyReportHub_user_id_index`).on(p.userId),(0,l.index)(`${t}_DailyReportHub_business_date_id_index`).on((0,Se.desc)(p.businessDate),(0,Se.desc)(p.id)),(0,l.index)(`${t}_DailyReportHub_deleted_at_index`).on(p.deletedAt)]),m=s.table("DailyReportInternal",{hubId:(0,l.bigint)("hub_id",{mode:"number"}).notNull(),body:(0,l.nvarchar)("body",{length:"max"}),metadata:(0,l.nvarchar)("metadata",{length:"max"}),createdAt:(0,l.datetime2)("created_at").notNull(),createdBy:(0,l.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,l.datetime2)("updated_at").notNull(),updatedBy:(0,l.nvarchar)("updated_by",{length:50}).notNull()},p=>[(0,l.primaryKey)({name:`${t}_DailyReportInternal_pk`,columns:[p.hubId]}),(0,l.foreignKey)({name:`${t}_DailyReportInternal_hub_id_fk`,columns:[p.hubId],foreignColumns:[y.id]})]),M=s.table("DailyReportComment",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),hubId:(0,l.bigint)("hub_id",{mode:"number"}).notNull(),userId:(0,l.bigint)("user_id",{mode:"number"}).notNull(),body:(0,l.nvarchar)("body",{length:"max"}).notNull(),createdAt:(0,l.datetime2)("created_at").notNull(),createdBy:(0,l.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,l.datetime2)("updated_at").notNull(),updatedBy:(0,l.nvarchar)("updated_by",{length:50}).notNull()},p=>[(0,l.primaryKey)({name:`${t}_DailyReportComment_pk`,columns:[p.id]}),(0,l.foreignKey)({name:`${t}_DailyReportComment_hub_id_fk`,columns:[p.hubId],foreignColumns:[y.id]}),(0,l.foreignKey)({name:`${t}_DailyReportComment_user_id_fk`,columns:[p.userId],foreignColumns:[i.id]}),(0,l.index)(`${t}_DailyReportComment_hub_id_index`).on(p.hubId)]),h=s.table("DailyReportLabel",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),userId:(0,l.bigint)("user_id",{mode:"number"}),name:(0,l.nvarchar)("name",{length:50}).notNull(),color:(0,l.nvarchar)("color",{length:20}),sortOrder:(0,l.int)("sort_order"),createdAt:(0,l.datetime2)("created_at").notNull(),createdBy:(0,l.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,l.datetime2)("updated_at").notNull(),updatedBy:(0,l.nvarchar)("updated_by",{length:50}).notNull()},p=>[(0,l.primaryKey)({name:`${t}_DailyReportLabel_pk`,columns:[p.id]}),(0,l.foreignKey)({name:`${t}_DailyReportLabel_user_id_fk`,columns:[p.userId],foreignColumns:[i.id]})]),O=s.table("DailyReportHub_Label",{hubId:(0,l.bigint)("hub_id",{mode:"number"}).notNull(),labelId:(0,l.bigint)("label_id",{mode:"number"}).notNull(),createdAt:(0,l.datetime2)("created_at").notNull(),createdBy:(0,l.nvarchar)("created_by",{length:50}).notNull()},p=>[(0,l.primaryKey)({name:`${t}_DailyReportHub_Label_pk`,columns:[p.hubId,p.labelId]}),(0,l.foreignKey)({name:`${t}_DailyReportHub_Label_hub_id_fk`,columns:[p.hubId],foreignColumns:[y.id]}),(0,l.foreignKey)({name:`${t}_DailyReportHub_Label_label_id_fk`,columns:[p.labelId],foreignColumns:[h.id]})]),u=s.table("DailyReportUserStatus",{hubId:(0,l.bigint)("hub_id",{mode:"number"}).notNull(),userId:(0,l.bigint)("user_id",{mode:"number"}).notNull(),isRead:(0,l.bit)("is_read").default(!1).notNull(),isStarred:(0,l.bit)("is_starred").default(!1).notNull(),createdAt:(0,l.datetime2)("created_at").notNull(),createdBy:(0,l.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,l.datetime2)("updated_at").notNull(),updatedBy:(0,l.nvarchar)("updated_by",{length:50}).notNull()},p=>[(0,l.primaryKey)({name:`${t}_DailyReportUserStatus_pk`,columns:[p.hubId,p.userId]}),(0,l.foreignKey)({name:`${t}_DailyReportUserStatus_hub_id_fk`,columns:[p.hubId],foreignColumns:[y.id]}),(0,l.foreignKey)({name:`${t}_DailyReportUserStatus_user_id_fk`,columns:[p.userId],foreignColumns:[i.id]})]),N=s.table("DailyReportAttachment",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),hubId:(0,l.bigint)("hub_id",{mode:"number"}).notNull(),uploadedBy:(0,l.bigint)("uploaded_by",{mode:"number"}),sourceKey:(0,l.nvarchar)("source_key",{length:200}).notNull(),fileName:(0,l.nvarchar)("file_name",{length:255}).notNull(),filePath:(0,l.nvarchar)("file_path",{length:"max"}).notNull(),fileType:(0,l.nvarchar)("file_type",{length:100}),fileSize:(0,l.bigint)("file_size",{mode:"number"}),objectCheckedAt:(0,l.datetime2)("object_checked_at"),objectMissingAt:(0,l.datetime2)("object_missing_at"),createdAt:(0,l.datetime2)("created_at").notNull(),createdBy:(0,l.nvarchar)("created_by",{length:50}).notNull(),updatedAt:(0,l.datetime2)("updated_at").notNull(),updatedBy:(0,l.nvarchar)("updated_by",{length:50}).notNull()},p=>[(0,l.primaryKey)({name:`${t}_DailyReportAttachment_pk`,columns:[p.id]}),(0,l.foreignKey)({name:`${t}_DailyReportAttachment_hub_id_fk`,columns:[p.hubId],foreignColumns:[y.id]}),(0,l.foreignKey)({name:`${t}_DailyReportAttachment_uploaded_by_fk`,columns:[p.uploadedBy],foreignColumns:[i.id]}),(0,l.uniqueIndex)(`${t}_DailyReportAttachment_source_unique`).on(p.hubId,p.sourceKey),(0,l.index)(`${t}_DailyReportAttachment_hub_id_index`).on(p.hubId,p.createdAt)]);return{hub:y,internal:m,comment:M,attachment:N,label:h,hubLabel:O,userStatus:u}}function sr(t){let e=$e(t.redis),s=new Re({defaultTtlMs:t.cacheDefaultTtlMs??6e4},e),i=Ke({...t,cache:s,epochs:e}),y=new De({redis:t.redis,streamKey:i.streamKey,cache:s,logger:t.logger}),m=ze({authenticate:t.authenticate,service:i,encodeUserId:t.encodeUserId,attachmentIdCodec:t.attachmentIdCodec,readAttachment:t.readAttachment,attachmentMaxBytes:t.attachmentMaxBytes,attachmentRateLimitPerMinute:t.attachmentRateLimitPerMinute,attachmentConcurrency:t.attachmentConcurrency,redis:t.redis,sseReader:y,streamKey:i.streamKey,loginRedirectPath:t.loginRedirectPath,enableDevCacheClear:t.enableDevCacheClear,logger:t.logger});return{service:i,cache:s,epochs:e,sseReader:y,streamKey:i.streamKey,...m}}0&&(module.exports={DailyReportSseReader,SqlResultCache,asciiFallbackFileName,createDailyReportHandlers,createDailyReportServer,createDailyReportService,createEpochStore,defineDailyReportAuthzResources,defineDailyReportSchema,encodeRfc8187,generateETag,isStreamIdLte,jsonResponseWithETag,sanitizeMediaType,seedDailyReportAuthzResources,transformJsonArray});
10
13
  //# sourceMappingURL=server.js.map