@spfn/core 0.2.0-beta.6 → 0.2.0-beta.66

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 (73) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +183 -1281
  3. package/dist/authz/index.d.ts +34 -0
  4. package/dist/authz/index.js +415 -0
  5. package/dist/authz/index.js.map +1 -0
  6. package/dist/{boss-DI1r4kTS.d.ts → boss-gXhgctn6.d.ts} +40 -0
  7. package/dist/cache/index.js +42 -30
  8. package/dist/cache/index.js.map +1 -1
  9. package/dist/codegen/index.d.ts +55 -8
  10. package/dist/codegen/index.js +183 -7
  11. package/dist/codegen/index.js.map +1 -1
  12. package/dist/config/index.d.ts +585 -6
  13. package/dist/config/index.js +116 -5
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/db/index.d.ts +379 -75
  16. package/dist/db/index.js +625 -113
  17. package/dist/db/index.js.map +1 -1
  18. package/dist/define-middleware-DuXD8Hvu.d.ts +167 -0
  19. package/dist/env/index.d.ts +26 -2
  20. package/dist/env/index.js +15 -5
  21. package/dist/env/index.js.map +1 -1
  22. package/dist/env/loader.d.ts +26 -19
  23. package/dist/env/loader.js +32 -25
  24. package/dist/env/loader.js.map +1 -1
  25. package/dist/errors/index.d.ts +10 -0
  26. package/dist/errors/index.js +20 -2
  27. package/dist/errors/index.js.map +1 -1
  28. package/dist/event/index.d.ts +33 -3
  29. package/dist/event/index.js +24 -3
  30. package/dist/event/index.js.map +1 -1
  31. package/dist/event/sse/client.d.ts +42 -3
  32. package/dist/event/sse/client.js +128 -45
  33. package/dist/event/sse/client.js.map +1 -1
  34. package/dist/event/sse/index.d.ts +12 -5
  35. package/dist/event/sse/index.js +271 -32
  36. package/dist/event/sse/index.js.map +1 -1
  37. package/dist/event/ws/client.d.ts +59 -0
  38. package/dist/event/ws/client.js +273 -0
  39. package/dist/event/ws/client.js.map +1 -0
  40. package/dist/event/ws/index.d.ts +94 -0
  41. package/dist/event/ws/index.js +272 -0
  42. package/dist/event/ws/index.js.map +1 -0
  43. package/dist/job/index.d.ts +2 -2
  44. package/dist/job/index.js +155 -42
  45. package/dist/job/index.js.map +1 -1
  46. package/dist/logger/index.d.ts +5 -0
  47. package/dist/logger/index.js +14 -0
  48. package/dist/logger/index.js.map +1 -1
  49. package/dist/middleware/index.d.ts +243 -2
  50. package/dist/middleware/index.js +1323 -13
  51. package/dist/middleware/index.js.map +1 -1
  52. package/dist/nextjs/index.d.ts +2 -2
  53. package/dist/nextjs/index.js +77 -31
  54. package/dist/nextjs/index.js.map +1 -1
  55. package/dist/nextjs/server.d.ts +53 -23
  56. package/dist/nextjs/server.js +197 -66
  57. package/dist/nextjs/server.js.map +1 -1
  58. package/dist/route/index.d.ts +138 -146
  59. package/dist/route/index.js +238 -22
  60. package/dist/route/index.js.map +1 -1
  61. package/dist/security/index.d.ts +83 -0
  62. package/dist/security/index.js +173 -0
  63. package/dist/security/index.js.map +1 -0
  64. package/dist/server/index.d.ts +458 -17
  65. package/dist/server/index.js +1756 -277
  66. package/dist/server/index.js.map +1 -1
  67. package/dist/{router-Di7ENoah.d.ts → token-manager-jKD_EsSE.d.ts} +121 -1
  68. package/dist/{types-D_N_U-Py.d.ts → types-7Mhoxnnt.d.ts} +21 -1
  69. package/dist/types-BFB72jbM.d.ts +282 -0
  70. package/dist/types-DVjf37yO.d.ts +205 -0
  71. package/docs/file-upload.md +717 -0
  72. package/package.json +237 -208
  73. package/dist/types-B-e_f2dQ.d.ts +0 -121
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/logger/types.ts","../../src/logger/formatters.ts","../../src/logger/logger.ts","../../src/logger/transports/console.ts","../../src/logger/config.ts","../../src/logger/factory.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Logger Type Definitions\n *\n * 로깅 시스템 타입 정의\n *\n * ✅ 구현 완료:\n * - LogLevel 타입 정의\n * - LogMetadata 인터페이스\n * - Transport 인터페이스\n * - 환경별 설정 타입\n *\n * 🔗 관련 파일:\n * - src/logger/logger.ts (Logger 클래스)\n * - src/logger/transports/ (Transport 구현체)\n * - src/logger/config.ts (설정)\n */\n\n/**\n * 로그 레벨\n * debug < info < warn < error < fatal\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\n/**\n * 로그 레벨 우선순위\n */\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n fatal: 4,\n};\n\n/**\n * 로그 메타데이터\n */\nexport interface LogMetadata\n{\n timestamp: Date;\n level: LogLevel;\n message: string;\n module?: string;\n error?: Error;\n context?: Record<string, unknown>;\n}\n\n/**\n * Transport 인터페이스\n * 모든 Transport는 이 인터페이스를 구현해야 함\n */\nexport interface Transport\n{\n /**\n * Transport 이름\n */\n name: string;\n\n /**\n * 최소 로그 레벨 (이 레벨 이상만 처리)\n */\n level: LogLevel;\n\n /**\n * 활성화 여부\n */\n enabled: boolean;\n\n /**\n * 로그 처리 함수\n */\n log(metadata: LogMetadata): Promise<void>;\n\n /**\n * Transport 종료 (리소스 정리)\n */\n close?(): Promise<void>;\n}\n\n/**\n * Logger 설정\n */\nexport interface LoggerConfig\n{\n /**\n * 기본 로그 레벨\n */\n level: LogLevel;\n\n /**\n * 모듈명 (context)\n */\n module?: string;\n\n /**\n * Transport 리스트\n */\n transports: Transport[];\n}\n\n/**\n * Transport 설정 (공통)\n */\nexport interface TransportConfig\n{\n level: LogLevel;\n enabled: boolean;\n}\n\n/**\n * Console Transport 설정\n */\nexport interface ConsoleTransportConfig extends TransportConfig\n{\n colorize?: boolean;\n}","/**\n * Logger Formatters\n *\n * Log formatting utilities for console and JSON outputs with sensitive data masking.\n */\n\nimport type { LogLevel, LogMetadata } from './types';\n\n/**\n * 민감 정보로 간주되는 키 목록\n * 이 키들을 포함하는 필드는 자동으로 마스킹됨\n */\nconst SENSITIVE_KEYS = [\n 'password',\n 'passwd',\n 'pwd',\n 'secret',\n 'token',\n 'apikey',\n 'api_key',\n 'accesstoken',\n 'access_token',\n 'refreshtoken',\n 'refresh_token',\n 'authorization',\n 'auth',\n 'cookie',\n 'session',\n 'sessionid',\n 'session_id',\n 'privatekey',\n 'private_key',\n 'creditcard',\n 'credit_card',\n 'cardnumber',\n 'card_number',\n 'cvv',\n 'ssn',\n 'pin',\n];\n\n/**\n * 마스킹된 값\n */\nconst MASKED_VALUE = '***MASKED***';\n\n/**\n * 키가 민감 정보를 포함하는지 확인\n */\nfunction isSensitiveKey(key: string): boolean\n{\n const lowerKey = key.toLowerCase();\n return SENSITIVE_KEYS.some(sensitive => lowerKey.includes(sensitive));\n}\n\n/**\n * 민감 정보 마스킹\n * Context 객체에서 민감한 정보(비밀번호, 토큰 등)를 마스킹\n * Circular reference를 안전하게 처리\n *\n * @param data - 원본 데이터\n * @param seen - 순환 참조 감지용 WeakSet (내부 사용)\n * @returns 마스킹된 데이터\n */\nexport function maskSensitiveData(data: unknown, seen = new WeakSet<object>()): unknown\n{\n // null, undefined 처리\n if (data === null || data === undefined)\n {\n return data;\n }\n\n // 기본 타입은 그대로 반환\n if (typeof data !== 'object')\n {\n return data;\n }\n\n // Circular reference 감지\n if (seen.has(data as object))\n {\n return '[Circular]';\n }\n seen.add(data as object);\n\n // 배열 처리\n if (Array.isArray(data))\n {\n return data.map(item => maskSensitiveData(item, seen));\n }\n\n // 객체 처리\n const masked: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data))\n {\n if (isSensitiveKey(key))\n {\n // 민감 정보 키는 마스킹\n masked[key] = MASKED_VALUE;\n }\n else if (typeof value === 'object' && value !== null)\n {\n // 중첩된 객체는 재귀 처리 (seen 전달)\n masked[key] = maskSensitiveData(value, seen);\n }\n else\n {\n // 일반 값은 그대로 유지\n masked[key] = value;\n }\n }\n\n return masked;\n}\n\n/**\n * ANSI 컬러 코드\n */\nconst COLORS = {\n reset: '\\x1b[0m',\n bright: '\\x1b[1m',\n dim: '\\x1b[2m',\n\n // 로그 레벨 컬러\n debug: '\\x1b[36m', // cyan\n info: '\\x1b[32m', // green\n warn: '\\x1b[33m', // yellow\n error: '\\x1b[31m', // red\n fatal: '\\x1b[35m', // magenta\n\n // 추가 컬러\n gray: '\\x1b[90m',\n};\n\n/**\n * 로그 레벨을 컬러 문자열로 변환\n */\nexport function colorizeLevel(level: LogLevel): string\n{\n const color = COLORS[level];\n const levelStr = level.toUpperCase().padEnd(5);\n return `${color}${levelStr}${COLORS.reset}`;\n}\n\n/**\n * 타임스탬프 포맷 (ISO 8601)\n */\nexport function formatTimestamp(date: Date): string\n{\n return date.toISOString();\n}\n\n/**\n * 타임스탬프 포맷 (사람이 읽기 쉬운 형식)\n */\nexport function formatTimestampHuman(date: Date): string\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 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 const ms = String(date.getMilliseconds()).padStart(3, '0');\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;\n}\n\n/**\n * 에러 객체를 문자열로 변환 (스택 트레이스 포함)\n */\nexport function formatError(error: Error): string\n{\n const lines: string[] = [];\n\n lines.push(`${error.name}: ${error.message}`);\n\n if (error.stack)\n {\n const stackLines = error.stack.split('\\n').slice(1);\n lines.push(...stackLines);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Context 객체를 문자열로 변환\n */\nexport function formatContext(context: Record<string, unknown>): string\n{\n try\n {\n return JSON.stringify(context, null, 2);\n }\n catch (error)\n {\n return '[Context serialization failed]';\n }\n}\n\n/**\n * 콘솔용 컬러 포맷\n */\nexport function formatConsole(metadata: LogMetadata, colorize = true): string\n{\n const parts: string[] = [];\n\n // [타임스탬프]\n const timestamp = formatTimestampHuman(metadata.timestamp);\n if (colorize)\n {\n parts.push(`${COLORS.gray}[${timestamp}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[${timestamp}]`);\n }\n\n // [pid=12345]\n const pid = process.pid;\n if (colorize)\n {\n parts.push(`${COLORS.dim}[pid=${pid}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[pid=${pid}]`);\n }\n\n // [module=value]\n if (metadata.module)\n {\n if (colorize)\n {\n parts.push(`${COLORS.dim}[module=${metadata.module}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[module=${metadata.module}]`);\n }\n }\n\n // Context를 각각 [key=value] 형태로 추가\n if (metadata.context && Object.keys(metadata.context).length > 0)\n {\n Object.entries(metadata.context).forEach(([key, value]) =>\n {\n let valueStr: string;\n if (typeof value === 'string')\n {\n valueStr = value;\n }\n else if (typeof value === 'object' && value !== null)\n {\n try\n {\n valueStr = JSON.stringify(value);\n }\n catch (error)\n {\n valueStr = '[circular]';\n }\n }\n else\n {\n valueStr = String(value);\n }\n\n if (colorize)\n {\n parts.push(`${COLORS.dim}[${key}=${valueStr}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[${key}=${valueStr}]`);\n }\n });\n }\n\n // (LEVEL):\n const levelStr = metadata.level.toUpperCase();\n if (colorize)\n {\n const color = COLORS[metadata.level];\n parts.push(`${color}(${levelStr})${COLORS.reset}:`);\n }\n else\n {\n parts.push(`(${levelStr}):`);\n }\n\n // 메시지\n if (colorize)\n {\n parts.push(`${COLORS.bright}${metadata.message}${COLORS.reset}`);\n }\n else\n {\n parts.push(metadata.message);\n }\n\n let output = parts.join(' ');\n\n // 에러는 별도 줄로 추가\n if (metadata.error)\n {\n output += '\\n' + formatError(metadata.error);\n }\n\n return output;\n}\n\n/**\n * JSON 포맷 (파일 저장 및 전송용)\n */\nexport function formatJSON(metadata: LogMetadata): string\n{\n const obj: Record<string, unknown> = {\n timestamp: formatTimestamp(metadata.timestamp),\n level: metadata.level,\n message: metadata.message,\n };\n\n if (metadata.module)\n {\n obj.module = metadata.module;\n }\n\n if (metadata.context)\n {\n obj.context = metadata.context;\n }\n\n if (metadata.error)\n {\n obj.error = {\n name: metadata.error.name,\n message: metadata.error.message,\n stack: metadata.error.stack,\n };\n }\n\n return JSON.stringify(obj);\n}\n\n/**\n * Drizzle ORM 에러에서 쿼리 정보 추출\n *\n * Drizzle ORM은 에러 메시지에 다음 형식으로 정보를 포함:\n * - \"Failed query: <QUERY>\\nparams: <PARAMS>\"\n * - \"Query: <QUERY>\"\n *\n * @param error - Error 객체\n * @returns 쿼리 정보 (query, params, table)\n */\nexport function extractQueryInfo(error: Error): {\n query?: string;\n params?: unknown;\n table?: string;\n} | null\n{\n const message = error.message;\n\n if (!message) return null;\n\n const result: {\n query?: string;\n params?: unknown;\n table?: string;\n } = {};\n\n // Extract query from \"Failed query: ...\" or \"Query: ...\"\n const queryMatch = message.match(/(?:Failed query:|Query:)\\s*([^\\n]+)/);\n if (queryMatch)\n {\n result.query = queryMatch[1].trim();\n\n // Extract table name from query (e.g., UPDATE \"table_name\" or INSERT INTO \"table_name\")\n const tableMatch = result.query.match(/(?:UPDATE|INSERT INTO|DELETE FROM|FROM)\\s+\"?([a-zA-Z_][a-zA-Z0-9_]*)\"?\\.\"?([a-zA-Z_][a-zA-Z0-9_]*)\"?|(?:UPDATE|INSERT INTO|DELETE FROM|FROM)\\s+\"?([a-zA-Z_][a-zA-Z0-9_]*)\"?/i);\n if (tableMatch)\n {\n // Schema.Table or just Table\n result.table = tableMatch[2] || tableMatch[3] || tableMatch[1];\n }\n }\n\n // Extract params from \"params: ...\"\n const paramsMatch = message.match(/params:\\s*(.+?)(?:\\n|$)/);\n if (paramsMatch)\n {\n const paramsStr = paramsMatch[1].trim();\n try\n {\n // Try to parse as comma-separated values\n result.params = paramsStr.split(',').map(p => p.trim());\n }\n catch (e)\n {\n result.params = paramsStr;\n }\n }\n\n return Object.keys(result).length > 0 ? result : null;\n}\n\n/**\n * Promise rejection의 호출 스택에서 실제 발생 위치 추출\n *\n * 스택 트레이스에서 다음 정보를 추출:\n * - 실제 에러 발생 파일 경로\n * - 라인 번호\n * - 함수명/메서드명\n * - Repository 정보 (있는 경우)\n *\n * @param error - Error 객체\n * @returns Promise context 정보\n */\nexport function extractPromiseContext(error: Error): Record<string, unknown>\n{\n const context: Record<string, unknown> = {};\n\n if (!error.stack) return context;\n\n const stackLines = error.stack.split('\\n');\n\n // Skip first line (error message) and find first meaningful stack frame\n // Ignore node_modules and internal Node.js paths\n for (let i = 1; i < stackLines.length; i++)\n {\n const line = stackLines[i].trim();\n\n // Skip node_modules and node internals\n if (line.includes('node_modules') || line.includes('node:internal')) continue;\n\n // Extract file, line number, and function name\n // Format: \"at ClassName.methodName (file.ts:line:col)\"\n // or: \"at functionName (file.ts:line:col)\"\n // or: \"at file.ts:line:col\"\n\n const match = line.match(/at\\s+(?:([a-zA-Z_$][\\w$]*(?:\\.[a-zA-Z_$][\\w$]*)*)\\s+)?\\(?([^)]+):(\\d+):(\\d+)\\)?/);\n\n if (match)\n {\n const [, functionName, filePath, lineNumber, columnNumber] = match;\n\n // Extract just the filename from the full path\n const fileNameMatch = filePath.match(/([^/\\\\]+)$/);\n const fileName = fileNameMatch ? fileNameMatch[1] : filePath;\n\n context.file = fileName;\n context.line = parseInt(lineNumber, 10);\n context.column = parseInt(columnNumber, 10);\n\n if (functionName)\n {\n // Check if it's a class method (e.g., \"ClassName.methodName\")\n const methodMatch = functionName.match(/^(.+)\\.([^.]+)$/);\n if (methodMatch)\n {\n const [, className, methodName] = methodMatch;\n\n context.class = className;\n context.method = methodName;\n\n // Check if it's a Repository\n if (className.includes('Repository'))\n {\n context.repository = className;\n }\n }\n else\n {\n context.function = functionName;\n }\n }\n\n // Found first relevant frame, stop here\n break;\n }\n }\n\n return context;\n}\n\n/**\n * Unhandled rejection 에러를 상세하게 포맷팅\n *\n * Promise context와 DB 쿼리 정보를 자동으로 추출하여\n * 에러 발생 위치와 원인을 명확하게 파악할 수 있도록 함\n *\n * @param reason - Rejection 원인 (Error 또는 기타)\n * @param promise - Promise 객체\n * @returns 상세 context 정보\n */\nexport function formatUnhandledRejection(reason: unknown, promise: Promise<unknown>): {\n error: Error;\n context: Record<string, unknown>;\n}\n{\n // Convert reason to Error if not already\n let error: Error;\n if (reason instanceof Error)\n {\n error = reason;\n }\n else if (typeof reason === 'string')\n {\n error = new Error(reason);\n }\n else\n {\n error = new Error(JSON.stringify(reason));\n }\n\n const context: Record<string, unknown> = {\n promise: String(promise),\n };\n\n // Extract promise context (file, line, function, etc.)\n const promiseContext = extractPromiseContext(error);\n if (Object.keys(promiseContext).length > 0)\n {\n context.promiseContext = promiseContext;\n }\n\n // Extract DB query info if available\n const queryInfo = extractQueryInfo(error);\n if (queryInfo)\n {\n context.queryInfo = queryInfo;\n }\n\n return { error, context };\n}","/**\n * Logger Class\n *\n * Central logging class with multiple transports, child loggers, and sensitive data masking.\n */\n\nimport type { LogLevel, LogMetadata, LoggerConfig, Transport } from './types';\nimport { LOG_LEVEL_PRIORITY } from './types';\nimport { maskSensitiveData } from './formatters';\n\n/**\n * Logger class\n */\nexport class Logger\n{\n private readonly config: LoggerConfig;\n private readonly module?: string;\n\n constructor(config: LoggerConfig)\n {\n this.config = config;\n this.module = config.module;\n }\n\n /**\n * Convert unknown error to Error object\n */\n private toError(error: unknown): Error\n {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n if (typeof error === 'object' && error !== null)\n {\n return new Error(JSON.stringify(error));\n }\n\n return new Error(String(error));\n }\n\n /**\n * Check if value is a context object (not an error)\n */\n private isContext(value: unknown): value is Record<string, unknown>\n {\n if (typeof value !== 'object' || value === null) return false;\n if (value instanceof Error) return false;\n\n // Use stack as the primary indicator for error detection\n // - Almost all Error objects have stack (auto-generated by JS engines)\n // - Regular objects rarely use 'stack' as a property name\n // - This avoids false positives for objects with 'name' or 'message' properties\n const hasStack = 'stack' in value && typeof (value as any).stack === 'string';\n\n if (hasStack)\n {\n return false; // Likely an error object\n }\n\n // No stack -> treat as context\n // This allows {name: \"...\", method: \"...\"} to be treated as context\n return true;\n }\n\n /**\n * Get current log level\n */\n get level(): LogLevel\n {\n return this.config.level;\n }\n\n /**\n * Create child logger (per module)\n */\n child(module: string): Logger\n {\n return new Logger({\n ...this.config,\n module,\n });\n }\n\n /**\n * Common log method with error/context detection\n */\n private logWithLevel(\n level: LogLevel,\n message: string,\n errorOrContext?: Error | unknown | Record<string, unknown>,\n context?: Record<string, unknown>\n ): void\n {\n if (errorOrContext instanceof Error)\n {\n this.log(level, message, errorOrContext, context);\n }\n else if (errorOrContext !== undefined && typeof errorOrContext === 'object' && !this.isContext(errorOrContext))\n {\n this.log(level, message, this.toError(errorOrContext), context);\n }\n else if (typeof errorOrContext === 'string' || typeof errorOrContext === 'number' || typeof errorOrContext === 'boolean')\n {\n this.log(level, message, this.toError(errorOrContext), context);\n }\n else\n {\n this.log(level, message, undefined, errorOrContext as Record<string, unknown>);\n }\n }\n\n /**\n * Debug log\n */\n debug(message: string, context?: Record<string, unknown>): void;\n debug(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n debug(message: string, errorOrContext?: Error | unknown | Record<string, unknown>, context?: Record<string, unknown>): void\n {\n this.logWithLevel('debug', message, errorOrContext, context);\n }\n\n /**\n * Info log\n */\n info(message: string, context?: Record<string, unknown>): void;\n info(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n info(message: string, errorOrContext?: Error | unknown | Record<string, unknown>, context?: Record<string, unknown>): void\n {\n this.logWithLevel('info', message, errorOrContext, context);\n }\n\n /**\n * Warn log\n */\n warn(message: string, context?: Record<string, unknown>): void;\n warn(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n warn(message: string, errorOrContext?: Error | unknown | Record<string, unknown>, context?: Record<string, unknown>): void\n {\n this.logWithLevel('warn', message, errorOrContext, context);\n }\n\n /**\n * Error log\n */\n error(message: string, context?: Record<string, unknown>): void;\n error(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n error(message: string, errorOrContext?: Error | unknown | Record<string, unknown>, context?: Record<string, unknown>): void\n {\n this.logWithLevel('error', message, errorOrContext, context);\n }\n\n /**\n * Fatal log\n */\n fatal(message: string, context?: Record<string, unknown>): void;\n fatal(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n fatal(message: string, errorOrContext?: Error | unknown | Record<string, unknown>, context?: Record<string, unknown>): void\n {\n this.logWithLevel('fatal', message, errorOrContext, context);\n }\n\n /**\n * Log processing (internal)\n */\n private log(level: LogLevel, message: string, error?: Error, context?: Record<string, unknown>): void\n {\n // Early return if log level is below configured level\n // This prevents unnecessary metadata creation and processing\n if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.config.level])\n {\n return;\n }\n\n const metadata: LogMetadata = {\n timestamp: new Date(),\n level,\n message,\n module: this.module,\n error,\n // Mask sensitive information in context to prevent credential leaks\n context: context ? maskSensitiveData(context) as Record<string, unknown> : undefined,\n };\n\n // Pass to all enabled Transports\n this.processTransports(metadata);\n }\n\n /**\n * Process Transports\n */\n private processTransports(metadata: LogMetadata): void\n {\n const promises = this.config.transports\n .filter(transport => transport.enabled)\n .map(transport => this.safeTransportLog(transport, metadata));\n\n // Async processing to prevent Transport errors from blocking logs\n Promise.all(promises).catch(error =>\n {\n // Use stderr directly to avoid circular logging\n const errorMessage = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[Logger] Transport error: ${errorMessage}\\n`);\n });\n }\n\n /**\n * Transport log (error-safe)\n */\n private async safeTransportLog(transport: Transport, metadata: LogMetadata): Promise<void>\n {\n try\n {\n await transport.log(metadata);\n }\n catch (error)\n {\n // Use stderr directly to avoid circular logging\n const errorMessage = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[Logger] Transport \"${transport.name}\" failed: ${errorMessage}\\n`);\n }\n }\n\n /**\n * Close all Transports\n */\n async close(): Promise<void>\n {\n const closePromises = this.config.transports\n .filter(transport => transport.close)\n .map(transport => transport.close!());\n\n await Promise.all(closePromises);\n }\n}","/**\n * Console Transport\n *\n * 콘솔 출력 Transport\n *\n * ✅ 구현 완료:\n * - 콘솔 출력 (stdout/stderr)\n * - 컬러 출력 지원\n * - 로그 레벨별 스트림 분리 (warn/error/fatal → stderr)\n *\n * 🔗 관련 파일:\n * - src/logger/types.ts (Transport 인터페이스)\n * - src/logger/formatters.ts (포맷터)\n * - src/logger/config.ts (설정)\n */\n\nimport type { Transport, LogMetadata, LogLevel, ConsoleTransportConfig } from '../types';\nimport { LOG_LEVEL_PRIORITY } from '../types';\nimport { formatConsole } from '../formatters';\n\n/**\n * Console Transport\n */\nexport class ConsoleTransport implements Transport\n{\n public readonly name = 'console';\n public readonly level: LogLevel;\n public readonly enabled: boolean;\n private readonly colorize: boolean;\n\n constructor(config: ConsoleTransportConfig)\n {\n this.level = config.level;\n this.enabled = config.enabled;\n this.colorize = config.colorize ?? true;\n }\n\n async log(metadata: LogMetadata): Promise<void>\n {\n // Enabled 상태 체크\n if (!this.enabled)\n {\n return;\n }\n\n // 로그 레벨 체크\n if (LOG_LEVEL_PRIORITY[metadata.level] < LOG_LEVEL_PRIORITY[this.level])\n {\n return;\n }\n\n // 포맷팅\n const message = formatConsole(metadata, this.colorize);\n\n // warn/error/fatal은 stderr로, 나머지는 stdout으로\n if (metadata.level === 'warn' || metadata.level === 'error' || metadata.level === 'fatal')\n {\n console.error(message);\n }\n else\n {\n console.log(message);\n }\n }\n}","/**\n * Logger Configuration\n *\n * Environment-based logger configuration with validation for console transport.\n */\n\nimport type {\n ConsoleTransportConfig,\n} from './types';\n\n/**\n * Console Transport configuration\n */\nexport function getConsoleConfig(): ConsoleTransportConfig\n{\n const isProduction = process.env.NODE_ENV === 'production';\n\n return {\n level: 'debug',\n enabled: true,\n colorize: !isProduction, // Dev: colored output, Production: plain text\n };\n}\n\n/**\n * Validate environment variables\n */\nfunction validateEnvironment(): void\n{\n const nodeEnv = process.env.NODE_ENV;\n\n if (!nodeEnv)\n {\n process.stderr.write(\n '[Logger] Warning: NODE_ENV is not set. Defaulting to test environment.\\n'\n );\n }\n // Allow any NODE_ENV value (development, production, test, staging, local, etc.)\n // No validation needed - users can use custom environments\n}\n\n/**\n * Validate all logger configuration\n */\nexport function validateConfig(): void\n{\n validateEnvironment();\n}","/**\n * Logger Factory\n *\n * Creates and initializes the logger instance with configured transports\n */\n\nimport { Logger } from './logger';\nimport { ConsoleTransport } from './transports/console';\nimport { getConsoleConfig, validateConfig } from './config';\nimport type { LogLevel, Transport } from './types';\nimport { LOG_LEVEL_PRIORITY } from './types';\n\n/**\n * Initialize transports based on environment and configuration\n */\nfunction initializeTransports(): Transport[]\n{\n const transports: Transport[] = [];\n\n // Console Transport (always enabled)\n const consoleConfig = getConsoleConfig();\n transports.push(new ConsoleTransport(consoleConfig));\n\n // Future: Add more transports (Slack, Email, etc.)\n // if (config.slack?.enabled) {\n // transports.push(new SlackTransport(config.slack));\n // }\n\n return transports;\n}\n\n/**\n * Get validated log level from environment variables\n */\nfunction getLogLevel(): LogLevel\n{\n const envLevel = process.env.SPFN_LOG_LEVEL\n || process.env.NEXT_PUBLIC_SPFN_LOG_LEVEL\n || 'info';\n\n if (envLevel in LOG_LEVEL_PRIORITY)\n {\n return envLevel as LogLevel;\n }\n\n process.stderr.write(\n `[Logger] Invalid log level \"${envLevel}\", defaulting to \"info\"\\n`\n );\n return 'info';\n}\n\n/**\n * Initialize logger with configuration validation\n */\nfunction initializeLogger(): Logger\n{\n // Validate configuration before creating logger\n validateConfig();\n\n // Create logger with configured transports\n return new Logger({\n level: getLogLevel(),\n transports: initializeTransports(),\n });\n}\n\n/**\n * Singleton Logger instance\n */\nexport const logger: Logger = initializeLogger();"]}
1
+ {"version":3,"sources":["../../src/logger/types.ts","../../src/logger/formatters.ts","../../src/logger/logger.ts","../../src/logger/transports/console.ts","../../src/logger/config.ts","../../src/logger/factory.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Logger Type Definitions\n *\n * 로깅 시스템 타입 정의\n *\n * ✅ 구현 완료:\n * - LogLevel 타입 정의\n * - LogMetadata 인터페이스\n * - Transport 인터페이스\n * - 환경별 설정 타입\n *\n * 🔗 관련 파일:\n * - src/logger/logger.ts (Logger 클래스)\n * - src/logger/transports/ (Transport 구현체)\n * - src/logger/config.ts (설정)\n */\n\n/**\n * 로그 레벨\n * debug < info < warn < error < fatal\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\n/**\n * 로그 레벨 우선순위\n */\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n fatal: 4,\n};\n\n/**\n * 로그 메타데이터\n */\nexport interface LogMetadata\n{\n timestamp: Date;\n level: LogLevel;\n message: string;\n module?: string;\n error?: Error;\n context?: Record<string, unknown>;\n}\n\n/**\n * Transport 인터페이스\n * 모든 Transport는 이 인터페이스를 구현해야 함\n */\nexport interface Transport\n{\n /**\n * Transport 이름\n */\n name: string;\n\n /**\n * 최소 로그 레벨 (이 레벨 이상만 처리)\n */\n level: LogLevel;\n\n /**\n * 활성화 여부\n */\n enabled: boolean;\n\n /**\n * 로그 처리 함수\n */\n log(metadata: LogMetadata): Promise<void>;\n\n /**\n * Transport 종료 (리소스 정리)\n */\n close?(): Promise<void>;\n}\n\n/**\n * Logger 설정\n */\nexport interface LoggerConfig\n{\n /**\n * 기본 로그 레벨\n */\n level: LogLevel;\n\n /**\n * 모듈명 (context)\n */\n module?: string;\n\n /**\n * Transport 리스트\n */\n transports: Transport[];\n}\n\n/**\n * Transport 설정 (공통)\n */\nexport interface TransportConfig\n{\n level: LogLevel;\n enabled: boolean;\n}\n\n/**\n * Console Transport 설정\n */\nexport interface ConsoleTransportConfig extends TransportConfig\n{\n colorize?: boolean;\n}\n","/**\n * Logger Formatters\n *\n * Log formatting utilities for console and JSON outputs with sensitive data masking.\n */\n\nimport type { LogLevel, LogMetadata } from './types';\n\n/**\n * 민감 정보로 간주되는 키 목록\n * 이 키들을 포함하는 필드는 자동으로 마스킹됨\n */\nconst SENSITIVE_KEYS = [\n 'password',\n 'passwd',\n 'pwd',\n 'secret',\n 'token',\n 'apikey',\n 'api_key',\n 'accesstoken',\n 'access_token',\n 'refreshtoken',\n 'refresh_token',\n 'authorization',\n 'auth',\n 'cookie',\n 'session',\n 'sessionid',\n 'session_id',\n 'privatekey',\n 'private_key',\n 'creditcard',\n 'credit_card',\n 'cardnumber',\n 'card_number',\n 'cvv',\n 'ssn',\n 'pin',\n];\n\n/**\n * 마스킹된 값\n */\nconst MASKED_VALUE = '***MASKED***';\n\n/**\n * 키가 민감 정보를 포함하는지 확인\n */\nfunction isSensitiveKey(key: string): boolean\n{\n const lowerKey = key.toLowerCase();\n\n return SENSITIVE_KEYS.some(sensitive => lowerKey.includes(sensitive));\n}\n\n/**\n * 민감 정보 마스킹\n * Context 객체에서 민감한 정보(비밀번호, 토큰 등)를 마스킹\n * Circular reference를 안전하게 처리\n *\n * @param data - 원본 데이터\n * @param seen - 순환 참조 감지용 WeakSet (내부 사용)\n * @returns 마스킹된 데이터\n */\nexport function maskSensitiveData(data: unknown, seen = new WeakSet<object>()): unknown\n{\n // null, undefined 처리\n if (data === null || data === undefined)\n {\n return data;\n }\n\n // 기본 타입은 그대로 반환\n if (typeof data !== 'object')\n {\n return data;\n }\n\n // Circular reference 감지\n if (seen.has(data as object))\n {\n return '[Circular]';\n }\n seen.add(data as object);\n\n // 배열 처리\n if (Array.isArray(data))\n {\n return data.map(item => maskSensitiveData(item, seen));\n }\n\n // 객체 처리\n const masked: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data))\n {\n if (isSensitiveKey(key))\n {\n // 민감 정보 키는 마스킹\n masked[key] = MASKED_VALUE;\n }\n else if (typeof value === 'object' && value !== null)\n {\n // 중첩된 객체는 재귀 처리 (seen 전달)\n masked[key] = maskSensitiveData(value, seen);\n }\n else\n {\n // 일반 값은 그대로 유지\n masked[key] = value;\n }\n }\n\n return masked;\n}\n\n/**\n * ANSI 컬러 코드\n */\nconst COLORS = {\n reset: '\\x1b[0m',\n bright: '\\x1b[1m',\n dim: '\\x1b[2m',\n\n // 로그 레벨 컬러\n debug: '\\x1b[36m', // cyan\n info: '\\x1b[32m', // green\n warn: '\\x1b[33m', // yellow\n error: '\\x1b[31m', // red\n fatal: '\\x1b[35m', // magenta\n\n // 추가 컬러\n gray: '\\x1b[90m',\n};\n\n/**\n * 로그 레벨을 컬러 문자열로 변환\n */\nexport function colorizeLevel(level: LogLevel): string\n{\n const color = COLORS[level];\n const levelStr = level.toUpperCase().padEnd(5);\n\n return `${color}${levelStr}${COLORS.reset}`;\n}\n\n/**\n * 타임스탬프 포맷 (ISO 8601)\n */\nexport function formatTimestamp(date: Date): string\n{\n return date.toISOString();\n}\n\n/**\n * 타임스탬프 포맷 (사람이 읽기 쉬운 형식)\n */\nexport function formatTimestampHuman(date: Date): string\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 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 const ms = String(date.getMilliseconds()).padStart(3, '0');\n\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;\n}\n\n/**\n * 에러 객체를 문자열로 변환 (스택 트레이스 포함)\n */\nexport function formatError(error: Error): string\n{\n const lines: string[] = [];\n\n lines.push(`${error.name}: ${error.message}`);\n\n if (error.stack)\n {\n const stackLines = error.stack.split('\\n').slice(1);\n lines.push(...stackLines);\n }\n\n // Cause chain — surfaces original PG errors hidden by DrizzleQueryError\n if (error.cause instanceof Error)\n {\n lines.push(`Caused by: ${formatError(error.cause)}`);\n }\n else if (error.cause !== undefined)\n {\n lines.push(`Caused by: ${String(error.cause)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Context 객체를 문자열로 변환\n */\nexport function formatContext(context: Record<string, unknown>): string\n{\n try\n {\n return JSON.stringify(context, null, 2);\n }\n catch (error)\n {\n return '[Context serialization failed]';\n }\n}\n\n/**\n * 콘솔용 컬러 포맷\n */\nexport function formatConsole(metadata: LogMetadata, colorize = true): string\n{\n const parts: string[] = [];\n\n // [타임스탬프]\n const timestamp = formatTimestampHuman(metadata.timestamp);\n if (colorize)\n {\n parts.push(`${COLORS.gray}[${timestamp}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[${timestamp}]`);\n }\n\n // [pid=12345]\n const pid = process.pid;\n if (colorize)\n {\n parts.push(`${COLORS.dim}[pid=${pid}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[pid=${pid}]`);\n }\n\n // [module=value]\n if (metadata.module)\n {\n if (colorize)\n {\n parts.push(`${COLORS.dim}[module=${metadata.module}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[module=${metadata.module}]`);\n }\n }\n\n // Context를 각각 [key=value] 형태로 추가\n if (metadata.context && Object.keys(metadata.context).length > 0)\n {\n Object.entries(metadata.context).forEach(([key, value]) =>\n {\n let valueStr: string;\n if (typeof value === 'string')\n {\n valueStr = value;\n }\n else if (typeof value === 'object' && value !== null)\n {\n try\n {\n valueStr = JSON.stringify(value);\n }\n catch (error)\n {\n valueStr = '[circular]';\n }\n }\n else\n {\n valueStr = String(value);\n }\n\n if (colorize)\n {\n parts.push(`${COLORS.dim}[${key}=${valueStr}]${COLORS.reset}`);\n }\n else\n {\n parts.push(`[${key}=${valueStr}]`);\n }\n });\n }\n\n // (LEVEL):\n const levelStr = metadata.level.toUpperCase();\n if (colorize)\n {\n const color = COLORS[metadata.level];\n parts.push(`${color}(${levelStr})${COLORS.reset}:`);\n }\n else\n {\n parts.push(`(${levelStr}):`);\n }\n\n // 메시지\n if (colorize)\n {\n parts.push(`${COLORS.bright}${metadata.message}${COLORS.reset}`);\n }\n else\n {\n parts.push(metadata.message);\n }\n\n let output = parts.join(' ');\n\n // 에러는 별도 줄로 추가\n if (metadata.error)\n {\n output += '\\n' + formatError(metadata.error);\n }\n\n return output;\n}\n\n/**\n * Error 객체를 cause 체인까지 포함한 직렬화 가능 객체로 변환\n */\nfunction formatErrorCauseChain(error: Error): Record<string, unknown>\n{\n const result: Record<string, unknown> = {\n name: error.name,\n message: error.message,\n stack: error.stack,\n };\n\n if (error.cause instanceof Error)\n {\n result.cause = formatErrorCauseChain(error.cause);\n }\n else if (error.cause !== undefined)\n {\n result.cause = String(error.cause);\n }\n\n return result;\n}\n\n/**\n * JSON 포맷 (파일 저장 및 전송용)\n */\nexport function formatJSON(metadata: LogMetadata): string\n{\n const obj: Record<string, unknown> = {\n timestamp: formatTimestamp(metadata.timestamp),\n level: metadata.level,\n message: metadata.message,\n };\n\n if (metadata.module)\n {\n obj.module = metadata.module;\n }\n\n if (metadata.context)\n {\n obj.context = metadata.context;\n }\n\n if (metadata.error)\n {\n obj.error = formatErrorCauseChain(metadata.error);\n }\n\n return JSON.stringify(obj);\n}\n\n/**\n * Drizzle ORM 에러에서 쿼리 정보 추출\n *\n * Drizzle ORM은 에러 메시지에 다음 형식으로 정보를 포함:\n * - \"Failed query: <QUERY>\\nparams: <PARAMS>\"\n * - \"Query: <QUERY>\"\n *\n * @param error - Error 객체\n * @returns 쿼리 정보 (query, params, table)\n */\nexport function extractQueryInfo(error: Error): {\n query?: string;\n params?: unknown;\n table?: string;\n} | null\n{\n const message = error.message;\n\n if (!message) return null;\n\n const result: {\n query?: string;\n params?: unknown;\n table?: string;\n } = {};\n\n // Extract query from \"Failed query: ...\" or \"Query: ...\"\n const queryMatch = message.match(/(?:Failed query:|Query:)\\s*([^\\n]+)/);\n if (queryMatch)\n {\n result.query = queryMatch[1].trim();\n\n // Extract table name from query (e.g., UPDATE \"table_name\" or INSERT INTO \"table_name\")\n const tableMatch = result.query.match(/(?:UPDATE|INSERT INTO|DELETE FROM|FROM)\\s+\"?([a-zA-Z_][a-zA-Z0-9_]*)\"?\\.\"?([a-zA-Z_][a-zA-Z0-9_]*)\"?|(?:UPDATE|INSERT INTO|DELETE FROM|FROM)\\s+\"?([a-zA-Z_][a-zA-Z0-9_]*)\"?/i);\n if (tableMatch)\n {\n // Schema.Table or just Table\n result.table = tableMatch[2] || tableMatch[3] || tableMatch[1];\n }\n }\n\n // Extract params from \"params: ...\"\n const paramsMatch = message.match(/params:\\s*(.+?)(?:\\n|$)/);\n if (paramsMatch)\n {\n const paramsStr = paramsMatch[1].trim();\n try\n {\n // Try to parse as comma-separated values\n result.params = paramsStr.split(',').map(p => p.trim());\n }\n catch (e)\n {\n result.params = paramsStr;\n }\n }\n\n return Object.keys(result).length > 0 ? result : null;\n}\n\n/**\n * Promise rejection의 호출 스택에서 실제 발생 위치 추출\n *\n * 스택 트레이스에서 다음 정보를 추출:\n * - 실제 에러 발생 파일 경로\n * - 라인 번호\n * - 함수명/메서드명\n * - Repository 정보 (있는 경우)\n *\n * @param error - Error 객체\n * @returns Promise context 정보\n */\nexport function extractPromiseContext(error: Error): Record<string, unknown>\n{\n const context: Record<string, unknown> = {};\n\n if (!error.stack) return context;\n\n const stackLines = error.stack.split('\\n');\n\n // Skip first line (error message) and find first meaningful stack frame\n // Ignore node_modules and internal Node.js paths\n for (let i = 1; i < stackLines.length; i++)\n {\n const line = stackLines[i].trim();\n\n // Skip node_modules and node internals\n if (line.includes('node_modules') || line.includes('node:internal')) continue;\n\n // Extract file, line number, and function name\n // Format: \"at ClassName.methodName (file.ts:line:col)\"\n // or: \"at functionName (file.ts:line:col)\"\n // or: \"at file.ts:line:col\"\n\n const match = line.match(/at\\s+(?:([a-zA-Z_$][\\w$]*(?:\\.[a-zA-Z_$][\\w$]*)*)\\s+)?\\(?([^)]+):(\\d+):(\\d+)\\)?/);\n\n if (match)\n {\n const [, functionName, filePath, lineNumber, columnNumber] = match;\n\n // Extract just the filename from the full path\n const fileNameMatch = filePath.match(/([^/\\\\]+)$/);\n const fileName = fileNameMatch ? fileNameMatch[1] : filePath;\n\n context.file = fileName;\n context.line = parseInt(lineNumber, 10);\n context.column = parseInt(columnNumber, 10);\n\n if (functionName)\n {\n // Check if it's a class method (e.g., \"ClassName.methodName\")\n const methodMatch = functionName.match(/^(.+)\\.([^.]+)$/);\n if (methodMatch)\n {\n const [, className, methodName] = methodMatch;\n\n context.class = className;\n context.method = methodName;\n\n // Check if it's a Repository\n if (className.includes('Repository'))\n {\n context.repository = className;\n }\n }\n else\n {\n context.function = functionName;\n }\n }\n\n // Found first relevant frame, stop here\n break;\n }\n }\n\n return context;\n}\n\n/**\n * Unhandled rejection 에러를 상세하게 포맷팅\n *\n * Promise context와 DB 쿼리 정보를 자동으로 추출하여\n * 에러 발생 위치와 원인을 명확하게 파악할 수 있도록 함\n *\n * @param reason - Rejection 원인 (Error 또는 기타)\n * @param promise - Promise 객체\n * @returns 상세 context 정보\n */\nexport function formatUnhandledRejection(reason: unknown, promise: Promise<unknown>): {\n error: Error;\n context: Record<string, unknown>;\n}\n{\n // Convert reason to Error if not already\n let error: Error;\n if (reason instanceof Error)\n {\n error = reason;\n }\n else if (typeof reason === 'string')\n {\n error = new Error(reason);\n }\n else\n {\n error = new Error(JSON.stringify(reason));\n }\n\n const context: Record<string, unknown> = {\n promise: String(promise),\n };\n\n // Extract promise context (file, line, function, etc.)\n const promiseContext = extractPromiseContext(error);\n if (Object.keys(promiseContext).length > 0)\n {\n context.promiseContext = promiseContext;\n }\n\n // Extract DB query info if available\n const queryInfo = extractQueryInfo(error);\n if (queryInfo)\n {\n context.queryInfo = queryInfo;\n }\n\n return { error, context };\n}\n","/**\n * Logger Class\n *\n * Central logging class with multiple transports, child loggers, and sensitive data masking.\n */\n\nimport { format } from 'node:util';\nimport type { LogLevel, LogMetadata, LoggerConfig, Transport } from './types';\nimport { LOG_LEVEL_PRIORITY } from './types';\nimport { maskSensitiveData } from './formatters';\n\nconst FORMAT_PATTERN = /%[sdifjoOc%]/;\n\n/**\n * Logger class\n */\nexport class Logger\n{\n private readonly config: LoggerConfig;\n private readonly module?: string;\n\n constructor(config: LoggerConfig)\n {\n this.config = config;\n this.module = config.module;\n }\n\n /**\n * Convert unknown error to Error object\n */\n private toError(error: unknown): Error\n {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n if (typeof error === 'object' && error !== null)\n {\n return new Error(JSON.stringify(error));\n }\n\n return new Error(String(error));\n }\n\n /**\n * Check if value is a context object (not an error)\n */\n private isContext(value: unknown): value is Record<string, unknown>\n {\n if (typeof value !== 'object' || value === null) return false;\n if (value instanceof Error) return false;\n\n // Use stack as the primary indicator for error detection\n // - Almost all Error objects have stack (auto-generated by JS engines)\n // - Regular objects rarely use 'stack' as a property name\n // - This avoids false positives for objects with 'name' or 'message' properties\n const hasStack = 'stack' in value && typeof (value as any).stack === 'string';\n\n if (hasStack)\n {\n return false; // Likely an error object\n }\n\n // No stack -> treat as context\n // This allows {name: \"...\", method: \"...\"} to be treated as context\n return true;\n }\n\n /**\n * Get current log level\n */\n get level(): LogLevel\n {\n return this.config.level;\n }\n\n /**\n * Create child logger (per module)\n */\n child(module: string): Logger\n {\n return new Logger({\n ...this.config,\n module,\n });\n }\n\n /**\n * Common log method with error/context detection\n */\n private logWithLevel(\n level: LogLevel,\n message: string,\n errorOrContext?: Error | unknown | Record<string, unknown>,\n context?: Record<string, unknown>,\n ): void\n {\n // printf-style format string: logger.info('url: %s', value)\n if (errorOrContext !== undefined && FORMAT_PATTERN.test(message))\n {\n this.log(level, format(message, errorOrContext), undefined, context);\n\n return;\n }\n\n if (errorOrContext instanceof Error)\n {\n this.log(level, message, errorOrContext, context);\n }\n else if (errorOrContext !== undefined && typeof errorOrContext === 'object' && !this.isContext(errorOrContext))\n {\n this.log(level, message, this.toError(errorOrContext), context);\n }\n else if (typeof errorOrContext === 'string' || typeof errorOrContext === 'number' || typeof errorOrContext === 'boolean')\n {\n this.log(level, message, this.toError(errorOrContext), context);\n }\n else\n {\n this.log(level, message, undefined, errorOrContext as Record<string, unknown>);\n }\n }\n\n /**\n * Debug log\n */\n debug(message: string, context?: Record<string, unknown>): void;\n debug(message: string, formatArg: string | number | boolean): void;\n debug(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n debug(message: string, errorOrContext?: unknown, context?: Record<string, unknown>): void\n {\n this.logWithLevel('debug', message, errorOrContext, context);\n }\n\n /**\n * Info log\n */\n info(message: string, context?: Record<string, unknown>): void;\n info(message: string, formatArg: string | number | boolean): void;\n info(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n info(message: string, errorOrContext?: unknown, context?: Record<string, unknown>): void\n {\n this.logWithLevel('info', message, errorOrContext, context);\n }\n\n /**\n * Warn log\n */\n warn(message: string, context?: Record<string, unknown>): void;\n warn(message: string, formatArg: string | number | boolean): void;\n warn(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n warn(message: string, errorOrContext?: unknown, context?: Record<string, unknown>): void\n {\n this.logWithLevel('warn', message, errorOrContext, context);\n }\n\n /**\n * Error log\n */\n error(message: string, context?: Record<string, unknown>): void;\n error(message: string, formatArg: string | number | boolean): void;\n error(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n error(message: string, errorOrContext?: unknown, context?: Record<string, unknown>): void\n {\n this.logWithLevel('error', message, errorOrContext, context);\n }\n\n /**\n * Fatal log\n */\n fatal(message: string, context?: Record<string, unknown>): void;\n fatal(message: string, formatArg: string | number | boolean): void;\n fatal(message: string, error: Error | unknown, context?: Record<string, unknown>): void;\n fatal(message: string, errorOrContext?: unknown, context?: Record<string, unknown>): void\n {\n this.logWithLevel('fatal', message, errorOrContext, context);\n }\n\n /**\n * Log processing (internal)\n */\n private log(level: LogLevel, message: string, error?: Error, context?: Record<string, unknown>): void\n {\n // Early return if log level is below configured level\n // This prevents unnecessary metadata creation and processing\n if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.config.level])\n {\n return;\n }\n\n const metadata: LogMetadata = {\n timestamp: new Date(),\n level,\n message,\n module: this.module,\n error,\n // Mask sensitive information in context to prevent credential leaks\n context: context ? maskSensitiveData(context) as Record<string, unknown> : undefined,\n };\n\n // Pass to all enabled Transports\n this.processTransports(metadata);\n }\n\n /**\n * Process Transports\n */\n private processTransports(metadata: LogMetadata): void\n {\n const promises = this.config.transports\n .filter(transport => transport.enabled)\n .map(transport => this.safeTransportLog(transport, metadata));\n\n // Async processing to prevent Transport errors from blocking logs\n Promise.all(promises).catch(error =>\n {\n // Use stderr directly to avoid circular logging\n const errorMessage = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[Logger] Transport error: ${errorMessage}\\n`);\n });\n }\n\n /**\n * Transport log (error-safe)\n */\n private async safeTransportLog(transport: Transport, metadata: LogMetadata): Promise<void>\n {\n try\n {\n await transport.log(metadata);\n }\n catch (error)\n {\n // Use stderr directly to avoid circular logging\n const errorMessage = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[Logger] Transport \"${transport.name}\" failed: ${errorMessage}\\n`);\n }\n }\n\n /**\n * Close all Transports\n */\n async close(): Promise<void>\n {\n const closePromises = this.config.transports\n .filter(transport => transport.close)\n .map(transport => transport.close!());\n\n await Promise.all(closePromises);\n }\n}\n","/**\n * Console Transport\n *\n * 콘솔 출력 Transport\n *\n * ✅ 구현 완료:\n * - 콘솔 출력 (stdout/stderr)\n * - 컬러 출력 지원\n * - 로그 레벨별 스트림 분리 (warn/error/fatal → stderr)\n *\n * 🔗 관련 파일:\n * - src/logger/types.ts (Transport 인터페이스)\n * - src/logger/formatters.ts (포맷터)\n * - src/logger/config.ts (설정)\n */\n\nimport type { Transport, LogMetadata, LogLevel, ConsoleTransportConfig } from '../types';\nimport { LOG_LEVEL_PRIORITY } from '../types';\nimport { formatConsole } from '../formatters';\n\n/**\n * Console Transport\n */\nexport class ConsoleTransport implements Transport\n{\n public readonly name = 'console';\n public readonly level: LogLevel;\n public readonly enabled: boolean;\n private readonly colorize: boolean;\n\n constructor(config: ConsoleTransportConfig)\n {\n this.level = config.level;\n this.enabled = config.enabled;\n this.colorize = config.colorize ?? true;\n }\n\n async log(metadata: LogMetadata): Promise<void>\n {\n // Enabled 상태 체크\n if (!this.enabled)\n {\n return;\n }\n\n // 로그 레벨 체크\n if (LOG_LEVEL_PRIORITY[metadata.level] < LOG_LEVEL_PRIORITY[this.level])\n {\n return;\n }\n\n // 포맷팅\n const message = formatConsole(metadata, this.colorize);\n\n // warn/error/fatal은 stderr로, 나머지는 stdout으로\n if (metadata.level === 'warn' || metadata.level === 'error' || metadata.level === 'fatal')\n {\n console.error(message);\n }\n else\n {\n console.log(message);\n }\n }\n}\n","/**\n * Logger Configuration\n *\n * Environment-based logger configuration with validation for console transport.\n */\n\nimport type {\n ConsoleTransportConfig,\n} from './types';\n\n/**\n * Console Transport configuration\n */\nexport function getConsoleConfig(): ConsoleTransportConfig\n{\n const isProduction = process.env.NODE_ENV === 'production';\n\n return {\n level: 'debug',\n enabled: true,\n colorize: !isProduction, // Dev: colored output, Production: plain text\n };\n}\n\n/**\n * Validate environment variables\n */\nfunction validateEnvironment(): void\n{\n const nodeEnv = process.env.NODE_ENV;\n\n if (!nodeEnv)\n {\n process.stderr.write(\n '[Logger] Warning: NODE_ENV is not set. Defaulting to test environment.\\n',\n );\n }\n // Allow any NODE_ENV value (development, production, test, staging, local, etc.)\n // No validation needed - users can use custom environments\n}\n\n/**\n * Validate all logger configuration\n */\nexport function validateConfig(): void\n{\n validateEnvironment();\n}\n","/**\n * Logger Factory\n *\n * Creates and initializes the logger instance with configured transports\n */\n\nimport { Logger } from './logger';\nimport { ConsoleTransport } from './transports/console';\nimport { getConsoleConfig, validateConfig } from './config';\nimport type { LogLevel, Transport } from './types';\nimport { LOG_LEVEL_PRIORITY } from './types';\n\n/**\n * Initialize transports based on environment and configuration\n */\nfunction initializeTransports(): Transport[]\n{\n const transports: Transport[] = [];\n\n // Console Transport (always enabled)\n const consoleConfig = getConsoleConfig();\n transports.push(new ConsoleTransport(consoleConfig));\n\n // Future: Add more transports (Slack, Email, etc.)\n // if (config.slack?.enabled) {\n // transports.push(new SlackTransport(config.slack));\n // }\n\n return transports;\n}\n\n/**\n * Get validated log level from environment variables\n */\nfunction getLogLevel(): LogLevel\n{\n const envLevel = process.env.SPFN_LOG_LEVEL\n || process.env.NEXT_PUBLIC_SPFN_LOG_LEVEL\n || 'info';\n\n if (envLevel in LOG_LEVEL_PRIORITY)\n {\n return envLevel as LogLevel;\n }\n\n process.stderr.write(\n `[Logger] Invalid log level \"${envLevel}\", defaulting to \"info\"\\n`,\n );\n\n return 'info';\n}\n\n/**\n * Initialize logger with configuration validation\n */\nfunction initializeLogger(): Logger\n{\n // Validate configuration before creating logger\n validateConfig();\n\n // Create logger with configured transports\n return new Logger({\n level: getLogLevel(),\n transports: initializeTransports(),\n });\n}\n\n/**\n * Singleton Logger instance\n */\nexport const logger: Logger = initializeLogger();\n"]}
@@ -1,4 +1,5 @@
1
- import { Context, Next } from 'hono';
1
+ import { Context, Next, MiddlewareHandler } from 'hono';
2
+ import { b as NamedMiddlewareFactory, N as NamedMiddleware } from '../define-middleware-DuXD8Hvu.js';
2
3
 
3
4
  /**
4
5
  * Error Handler Middleware
@@ -26,6 +27,28 @@ interface ErrorHandlerOptions {
26
27
  * @default true
27
28
  */
28
29
  enableLogging?: boolean;
30
+ /**
31
+ * Callback invoked when an error occurs
32
+ *
33
+ * Called asynchronously without blocking the response.
34
+ * Useful for external error notifications (Slack, PagerDuty, etc.)
35
+ */
36
+ onError?: (err: Error, context: OnErrorContext) => Promise<void> | void;
37
+ }
38
+ /**
39
+ * Context passed to onError callback
40
+ */
41
+ interface OnErrorContext {
42
+ statusCode: number;
43
+ path: string;
44
+ method: string;
45
+ requestId?: string;
46
+ timestamp: string;
47
+ userId?: string;
48
+ request: {
49
+ headers: Record<string, string>;
50
+ query: Record<string, string>;
51
+ };
29
52
  }
30
53
  /**
31
54
  * Error handler middleware for Hono
@@ -139,4 +162,222 @@ declare function maskSensitiveData(obj: any, sensitiveFields: string[], seen?: W
139
162
  */
140
163
  declare function RequestLogger(options?: RequestLoggerOptions): (c: Context, next: Next) => Promise<void>;
141
164
 
142
- export { ErrorHandler, type ErrorHandlerOptions, RequestLogger, type RequestLoggerConfig, type RequestLoggerOptions, maskSensitiveData };
165
+ /**
166
+ * Proxy-guard middleware
167
+ *
168
+ * Verifies that an inbound request came through a trusted Next.js RPC proxy
169
+ * (HMAC signature) and/or from an allowed browser origin, then tags the request
170
+ * with a `clientType`. Lets the backend reject direct-to-backend calls that
171
+ * bypass the proxy.
172
+ *
173
+ * This does NOT replace user authentication (JWT) — it answers "what kind of
174
+ * client is this?", not "who is the user?". It also cannot stop a client from
175
+ * calling the proxy itself; that is the web's inherent limit (see
176
+ * PROXY-BACKEND-AUTH-SPEC.md). Mobile clients call the backend directly and are
177
+ * handled by a future attestation path, OR'd into this same check.
178
+ *
179
+ * @module middleware/proxy-guard
180
+ */
181
+
182
+ /**
183
+ * How the request reached the backend.
184
+ * - `web`: verified via a trusted proxy signature
185
+ * - `untrusted`: could not be verified (only reached handlers in `tag` mode)
186
+ * - future: `ios` | `android` once attestation lands
187
+ */
188
+ type ClientType = 'web' | 'untrusted' | (string & {});
189
+ /**
190
+ * Enforcement mode.
191
+ * - `off`: middleware is a no-op (default — keeps existing apps working).
192
+ * - `tag`: verify and set `clientType`, but never reject (observe first).
193
+ * - `strict`: reject requests that fail verification.
194
+ */
195
+ type ProxyGuardMode = 'off' | 'tag' | 'strict';
196
+ /**
197
+ * Optional replay store. Returns `true` when the nonce is fresh (and records it),
198
+ * `false` when it has been seen before. Backed by Redis in multi-instance setups.
199
+ */
200
+ interface NonceStore {
201
+ checkAndSet(nonce: string, ttlMs: number): Promise<boolean>;
202
+ }
203
+ interface ProxyGuardConfig {
204
+ /**
205
+ * Active shared HMAC secret, as `<keyId>:<secret>` (or a bare secret).
206
+ * Defaults to `env.SPFN_PROXY_SECRET`. Without it (and no previous keys),
207
+ * signatures cannot be verified and every request is tagged `untrusted`.
208
+ */
209
+ secret?: string;
210
+ /**
211
+ * Previous (grace) keys still accepted for verification during rotation, as a
212
+ * comma-separated list of `<keyId>:<secret>`. The proxy never signs with
213
+ * these — they exist only so in-flight requests signed with the prior key
214
+ * keep verifying until the rollout settles. Defaults to
215
+ * `env.SPFN_PROXY_SECRET_PREVIOUS`. Backend-only, so it can live in
216
+ * `.env.server` (never exposed to the Next.js process).
217
+ */
218
+ previousSecrets?: string;
219
+ /** Enforcement mode. @default 'off' */
220
+ mode?: ProxyGuardMode;
221
+ /** Allowed clock skew / replay window in ms. @default 30000 */
222
+ windowMs?: number;
223
+ /**
224
+ * Browser origin allowlist. When set, a request carrying an `Origin` header
225
+ * not in this list is rejected (strict) / tagged (tag). Requests without an
226
+ * `Origin` (server-to-server, mobile) skip this check and fall back to the
227
+ * signature.
228
+ */
229
+ allowedOrigins?: string[];
230
+ /** Optional nonce store for hard replay rejection. Redis (multi-instance) or in-memory (single). */
231
+ nonceStore?: NonceStore;
232
+ /**
233
+ * When the nonce store throws (e.g. Redis down), reject the request instead of
234
+ * falling back to the timestamp window. Trades availability for strictness.
235
+ * @default false (fall back to the timestamp window)
236
+ */
237
+ nonceFailClosed?: boolean;
238
+ /** Paths to skip entirely (e.g. health checks). */
239
+ skipPaths?: string[];
240
+ /**
241
+ * Reject (413) once the streamed body exceeds this many bytes. Measured AS IT
242
+ * STREAMS — Content-Length is NOT trusted, so a missing/chunked/under-reported
243
+ * length can't bypass it. Bounds the per-request memory the guard buffers.
244
+ * Undefined = no cap (default). Multipart is exempt (unsigned).
245
+ */
246
+ maxBodyBytes?: number;
247
+ }
248
+ declare module 'hono' {
249
+ interface ContextVariableMap {
250
+ clientType?: ClientType;
251
+ }
252
+ }
253
+ /**
254
+ * Create the proxy-guard middleware.
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * export default defineServerConfig()
259
+ * .proxyGuard({ mode: 'strict', allowedOrigins: ['https://app.example.com'] })
260
+ * .routes(appRouter)
261
+ * .build();
262
+ * ```
263
+ */
264
+ declare function createProxyGuard(config?: ProxyGuardConfig): MiddlewareHandler;
265
+ /** Minimal cache client (compatible with ioredis Redis | Cluster). */
266
+ type CacheClient = {
267
+ set(key: string, value: string, ...args: any[]): Promise<any>;
268
+ };
269
+ /**
270
+ * Build a Redis-backed nonce store for hard replay rejection.
271
+ *
272
+ * Uses `SET key 1 PX ttl NX` — an atomic "set if absent". A fresh nonce returns
273
+ * 'OK'; a replayed one returns null. TTL keeps the key set bounded (only nonces
274
+ * within the replay window matter).
275
+ */
276
+ declare function createCacheNonceStore(cache: CacheClient, prefix?: string): NonceStore;
277
+ /**
278
+ * In-process nonce store for hard replay rejection without Redis.
279
+ *
280
+ * Good enough for a single instance — each process only knows its own nonces, so
281
+ * across multiple instances a replay routed to a different pod isn't caught. Use a
282
+ * cache-backed store (createCacheNonceStore) for multi-instance hard rejection.
283
+ * Entries self-expire after their TTL; the map is swept opportunistically so it
284
+ * stays bounded by the request rate within the replay window.
285
+ */
286
+ declare function createInMemoryNonceStore(): NonceStore;
287
+
288
+ /**
289
+ * One identity dimension to limit on. A bare string uses the top-level `limit`;
290
+ * an object can carry its own `limit` so dimensions can differ (e.g. a loose
291
+ * per-IP cap alongside a tight per-account cap).
292
+ */
293
+ type RateLimitDimension = string | {
294
+ key: string;
295
+ limit?: number;
296
+ };
297
+ interface RateLimitOptions {
298
+ /** Max requests allowed per window, applied to each dimension without its own `limit`. */
299
+ limit: number;
300
+ /** Window length in milliseconds. */
301
+ windowMs: number;
302
+ /**
303
+ * Namespace for the counter keys. Defaults to `${method} ${routePath}` so
304
+ * each route is limited independently.
305
+ */
306
+ scope?: string;
307
+ /**
308
+ * Identity dimensions to limit on. Each non-empty value is counted
309
+ * separately and the strictest wins (e.g. by IP and by account). A dimension
310
+ * may override the top-level `limit` via `{ key, limit }`. Defaults to the
311
+ * client IP only.
312
+ */
313
+ by?: (c: Context) => (RateLimitDimension | null | undefined)[] | Promise<(RateLimitDimension | null | undefined)[]>;
314
+ /** Reject with 429 instead of allowing through when the cache is unavailable. */
315
+ failClosed?: boolean;
316
+ /** Message for the 429 response. */
317
+ message?: string;
318
+ }
319
+ /**
320
+ * Best-effort client IP for rate-limit keying.
321
+ *
322
+ * Prefers the proxy-forwarded real client IP, but ONLY when proxy-guard verified
323
+ * the request came through our proxy (`clientType` set and not 'untrusted') — on a
324
+ * verified request the proxy is the sole hop, so the value is the true client and
325
+ * isn't client-spoofable. Otherwise (no proxy-guard, or unverified/direct request)
326
+ * the forwarded header is attacker-settable, so fall back to the raw chain — whose
327
+ * leftmost hop is itself spoofable, so still pair with an account/target dimension
328
+ * for anything security-sensitive.
329
+ *
330
+ * Last resort before giving up is the TCP peer address from the Node adapter
331
+ * socket. Without it, a deployment that sets no forwarding header would collapse
332
+ * every client onto a single `'unknown'` bucket.
333
+ */
334
+ declare function getClientIp(c: Context): string;
335
+ /**
336
+ * Redis-backed fixed-window rate limiter.
337
+ *
338
+ * @example
339
+ * ```typescript
340
+ * route.post('/_auth/login')
341
+ * .use([rateLimit({ limit: 10, windowMs: 60_000 })])
342
+ * .handler(...);
343
+ * ```
344
+ */
345
+ declare const rateLimit: NamedMiddlewareFactory<"rateLimit", [options: RateLimitOptions]>;
346
+ /** Set the fail-closed default for named policies. Called by the server at boot. */
347
+ declare function setRateLimitFailClosedDefault(failClosed: boolean): void;
348
+ /**
349
+ * Replace the named-policy registry. Called by the server at boot; passing
350
+ * undefined clears it (so a restart without policies doesn't keep stale ones).
351
+ */
352
+ declare function setRateLimitPolicies(policies?: Record<string, RateLimitOptions>): void;
353
+ /** Look up a configured policy by name — undefined when the app didn't set it. */
354
+ declare function getRateLimitPolicy(name: string): RateLimitOptions | undefined;
355
+ /**
356
+ * Rate-limit a route under a named policy.
357
+ *
358
+ * A package author tags a sensitive route with a policy name plus a safe
359
+ * fallback; the consuming app tunes the numbers centrally via
360
+ * `defineServerConfig().rateLimit({ policies: { [name]: {...} } })`. When the
361
+ * app configures the policy, its fields override the fallback (shallow merge);
362
+ * otherwise the fallback applies, so the route is protected out of the box.
363
+ *
364
+ * LAYERS on top of the global default limiter rather than replacing it: the tag
365
+ * has a distinct name (`rateLimit:<name>`) and does not skip the global, so a
366
+ * route gets both the global per-IP floor (when enabled) and this policy's own
367
+ * bucket — whichever is stricter trips first. Opt out of the global floor on a
368
+ * route with `.skip(['rateLimit'])`.
369
+ *
370
+ * The counter scope defaults to the policy name, so (a) every route sharing a
371
+ * policy shares one bucket, and (b) the key never collides with the global
372
+ * default's per-route `${method} ${path}` scope.
373
+ *
374
+ * @example
375
+ * ```typescript
376
+ * route.post('/_auth/login')
377
+ * .use([rateLimitPolicy('auth-login', { limit: 5, windowMs: 60_000 })])
378
+ * .handler(...);
379
+ * ```
380
+ */
381
+ declare function rateLimitPolicy(name: string, fallback: RateLimitOptions): NamedMiddleware<string>;
382
+
383
+ export { type ClientType, ErrorHandler, type ErrorHandlerOptions, type NonceStore, type OnErrorContext, type ProxyGuardConfig, type ProxyGuardMode, type RateLimitDimension, type RateLimitOptions, RequestLogger, type RequestLoggerConfig, type RequestLoggerOptions, createCacheNonceStore, createInMemoryNonceStore, createProxyGuard, getClientIp, getRateLimitPolicy, maskSensitiveData, rateLimit, rateLimitPolicy, setRateLimitFailClosedDefault, setRateLimitPolicies };