{"version":3,"file":"index.cjs","names":["colors","EOL","Random"],"sources":["../../../../../../@warlock.js/logger/src/log-channel.ts","../../../../../../@warlock.js/logger/src/channels/console-log.ts","../../../../../../@warlock.js/logger/src/channels/file-log.ts","../../../../../../@warlock.js/logger/src/utils/safe-json-stringify.ts","../../../../../../@warlock.js/logger/src/channels/json-file-log.ts","../../../../../../@warlock.js/logger/src/channels/sentry-log.ts","../../../../../../@warlock.js/logger/src/redact/redact.ts","../../../../../../@warlock.js/logger/src/utils/clear-message.ts","../../../../../../@warlock.js/logger/src/logger.ts","../../../../../../@warlock.js/logger/src/utils/capture-unhandled-errors.ts"],"sourcesContent":["import type {\r\n  BasicLogConfigurations,\r\n  LogContract,\r\n  LoggingData,\r\n  RedactConfig,\r\n} from \"./types\";\r\n\r\nexport abstract class LogChannel<\r\n  Options extends BasicLogConfigurations = BasicLogConfigurations,\r\n> implements LogContract {\r\n  /**\r\n   * Channel name\r\n   */\r\n  public name!: string;\r\n\r\n  /**\r\n   * Channel description\r\n   */\r\n  public description?: string;\r\n\r\n  /**\r\n   * Determine if channel is logging in terminal\r\n   */\r\n  public terminal = false;\r\n\r\n  /**\r\n   * Default Configurations\r\n   */\r\n  protected defaultConfigurations: Options = {} as Options;\r\n\r\n  /**\r\n   * Channel configurations\r\n   */\r\n  protected channelConfigurations: Options = {} as Options; //\r\n\r\n  /**\r\n   * Determine whether the channel is fully initialized\r\n   */\r\n  protected isInitialized = false;\r\n\r\n  /**\r\n   * Constructor\r\n   */\r\n  public constructor(configurations?: Options) {\r\n    if (configurations) {\r\n      this.setConfigurations(configurations);\r\n    }\r\n\r\n    setTimeout(async () => {\r\n      if (this.init) {\r\n        await this.init();\r\n      }\r\n\r\n      this.isInitialized = true;\r\n    }, 0);\r\n  }\r\n\r\n  /**\r\n   * Initialize the channel\r\n   */\r\n  protected init?(): void | Promise<void>;\r\n\r\n  /**\r\n   * Get config value\r\n   */\r\n  protected config<K extends keyof Options>(key: K): Options[K] {\r\n    return (\r\n      this.channelConfigurations[key] ?? (this.defaultConfigurations ?? {})[key]\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Set configurations\r\n   */\r\n  protected setConfigurations(configurations: Options) {\r\n    this.channelConfigurations = {\r\n      ...this.channelConfigurations,\r\n      ...configurations,\r\n    };\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Determine if the message should be logged\r\n   */\r\n  protected shouldBeLogged(data: LoggingData): boolean {\r\n    // check for debug mode\r\n    const allowedLevels = this.config(\"levels\");\r\n\r\n    if (allowedLevels?.length && !allowedLevels.includes(data.type))\r\n      return false;\r\n\r\n    const filter = this.config(\"filter\");\r\n\r\n    if (filter) {\r\n      return filter(data);\r\n    }\r\n\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * Log the given message\r\n   */\r\n  public abstract log(data: LoggingData): void | Promise<void>;\r\n\r\n  /**\r\n   * Synchronously flush messages\r\n   */\r\n  public flushSync?(): void;\r\n\r\n  /**\r\n   * Asynchronously flush buffered messages.\r\n   *\r\n   * Optional — channels that write synchronously per entry (e.g. `ConsoleLog`)\r\n   * don't implement it. Buffering channels override this to drain their buffer\r\n   * over async I/O so callers can `await log.flush()` on graceful shutdown.\r\n   */\r\n  public flush?(): void | Promise<void>;\r\n\r\n  /**\r\n   * Read the channel's redact config (if any). Used by `Logger` to apply\r\n   * per-channel additive redaction on top of the logger-wide floor.\r\n   * Subclasses normally don't override this — set `redact` in your channel\r\n   * configuration instead.\r\n   */\r\n  public getRedactConfig(): RedactConfig | undefined {\r\n    return this.config(\"redact\" as keyof Options) as RedactConfig | undefined;\r\n  }\r\n\r\n  /**\r\n   * Get date and time formats\r\n   */\r\n  protected getDateAndTimeFormat() {\r\n    const dateFormat = this.config(\"dateFormat\");\r\n    const date = dateFormat?.date ?? \"DD-MM-YYYY\";\r\n    const time = dateFormat?.time ?? \"HH:mm:ss\";\r\n\r\n    return { date, time };\r\n  }\r\n\r\n  /**\r\n   * get basic configurations with the given ones\r\n   */\r\n  protected withBasicConfigurations(configurations: Partial<Options>): Options {\r\n    return {\r\n      filter: () => true,\r\n      ...configurations,\r\n    } as any as Options;\r\n  }\r\n}\r\n","import { colors } from \"@mongez/copper\";\r\nimport { inspect } from \"util\";\r\nimport { LogChannel } from \"../log-channel\";\r\nimport type { BasicLogConfigurations, LoggingData, LogLevel } from \"../types\";\r\n\r\nexport type ConsoleLogConfig = BasicLogConfigurations & {\r\n  /**\r\n   * Render the log entry's `context` object on a second line after the main\r\n   * message. When `false`, context is silently dropped (the historical\r\n   * behavior). When `true`, contexts are pretty-printed with `util.inspect`\r\n   * — colored, depth-limited, ideal for development. Persistent channels\r\n   * (`FileLog`, `JSONFileLog`) always retain context regardless of this flag.\r\n   *\r\n   * @default false\r\n   */\r\n  showContext?: boolean;\r\n  /**\r\n   * Depth passed to `util.inspect` when rendering context. Only applies when\r\n   * `showContext` is enabled.\r\n   *\r\n   * @default 4\r\n   */\r\n  contextDepth?: number;\r\n};\r\n\r\n/**\r\n * The longest level name (`success`) sets the tag column width. Padding every\r\n * level name to this width keeps the timestamp / module / action columns\r\n * aligned across lines. The level icons are bare (text-style, single-cell)\r\n * glyphs, so padding the name alone produces true columns.\r\n */\r\nconst LEVEL_NAME_WIDTH = 7;\r\n\r\n/**\r\n * Per-level console presentation: the colored, fixed-width `{icon} {name}` tag\r\n * and the function that colors the message body.\r\n */\r\ntype LevelStyle = {\r\n  tag: string;\r\n  message: (message: LoggingData[\"message\"]) => string;\r\n};\r\n\r\n/**\r\n * Build a single-color, fixed-width tag — `{icon} {name}` with the name padded\r\n * so every tag spans the same number of columns.\r\n */\r\nfunction buildTag(\r\n  icon: string,\r\n  name: string,\r\n  color: (text: string) => string,\r\n): string {\r\n  return color(`${icon} ${name.padEnd(LEVEL_NAME_WIDTH)}`);\r\n}\r\n\r\n/**\r\n * Console styling per log level. The `fatal` tag is a bright-red background\r\n * badge — the same column width as the others (`\" ☠ fatal \"` is 9 cells, like\r\n * `{icon} {name.padEnd(7)}`), but impossible to miss in a wall of red `error`\r\n * lines, where a plain red tag would blend in.\r\n */\r\nconst LEVEL_STYLES: Record<LogLevel, LevelStyle> = {\r\n  debug: {\r\n    tag: buildTag(\"⚙\", \"debug\", colors.magentaBright),\r\n    message: colors.magentaBright,\r\n  },\r\n  info: {\r\n    tag: buildTag(\"ℹ\", \"info\", colors.blueBright),\r\n    message: colors.blueBright,\r\n  },\r\n  warn: {\r\n    tag: buildTag(\"⚠\", \"warn\", colors.yellow),\r\n    message: colors.yellowBright,\r\n  },\r\n  error: {\r\n    tag: buildTag(\"✗\", \"error\", colors.red),\r\n    message: colors.redBright,\r\n  },\r\n  success: {\r\n    tag: buildTag(\"✓\", \"success\", colors.green),\r\n    message: colors.greenBright,\r\n  },\r\n  fatal: {\r\n    tag: colors.bgRedBright(colors.whiteBright(colors.bold(\" ☠ fatal \"))),\r\n    message: colors.redBright.bold,\r\n  },\r\n};\r\n\r\n/**\r\n * Fallback for an unrecognized level — a plain `[log]` tag padded to the same\r\n * width so the columns stay aligned.\r\n */\r\nconst DEFAULT_STYLE: LevelStyle = {\r\n  tag: \"[log]\".padEnd(LEVEL_NAME_WIDTH + 2),\r\n  message: message => message as string,\r\n};\r\n\r\nexport class ConsoleLog extends LogChannel<ConsoleLogConfig> {\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public name = \"console\";\r\n\r\n  /**\r\n   * Determine if channel is logging in terminal\r\n   */\r\n  public terminal = true;\r\n\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public log(data: LoggingData) {\r\n    const { module, action, message, type: level } = data;\r\n\r\n    if (!this.shouldBeLogged(data)) return;\r\n\r\n    // Console shows time-only (HH:mm:ss.SSS) — within a dev session the date\r\n    // rarely changes, so the full ISO date + `T`/`Z` are just noise. Persistent\r\n    // channels (FileLog / JSONFileLog) keep the full ISO timestamp.\r\n    const time = new Date().toISOString().slice(11, 23); // i.e. \"10:22:00.000\"\r\n\r\n    const style = LEVEL_STYLES[level] ?? DEFAULT_STYLE;\r\n\r\n    // The slate timestamp recedes so the colored level + message lead the eye;\r\n    // module (cyan) and action (magenta) stay colored for subsystem scanning.\r\n    // `slate` (256-color neutral gray) reads cleaner than bright-black `gray`,\r\n    // which sits too close to the background on most terminal themes.\r\n    console.log(\r\n      style.tag,\r\n      colors.slate(`(${time})`),\r\n      colors.cyan(`[${module}]`),\r\n      colors.magenta(`[${action}]`),\r\n      style.message(message),\r\n    );\r\n\r\n    if (typeof message === \"object\") {\r\n      console.log(message);\r\n    }\r\n\r\n    // Render context on a second line when explicitly enabled. We only\r\n    // attempt rendering if there's anything meaningful to show — empty\r\n    // objects clutter the terminal without adding signal.\r\n    if (this.config(\"showContext\") && data.context && Object.keys(data.context).length > 0) {\r\n      const depth = this.config(\"contextDepth\") ?? 4;\r\n      console.log(\r\n        colors.slate(\"  ↳\"),\r\n        inspect(data.context, { colors: true, depth, breakLength: 80 }),\r\n      );\r\n    }\r\n  }\r\n}\r\n","import { ensureDirectoryAsync } from \"@warlock.js/fs\";\r\nimport dayjs from \"dayjs\";\r\nimport fs from \"fs\";\r\nimport { EOL } from \"os\";\r\nimport path from \"path\";\r\nimport { LogChannel } from \"../log-channel\";\r\nimport type {\r\n  BasicLogConfigurations,\r\n  LogContract,\r\n  LoggingData,\r\n  LogLevel,\r\n  LogMessage,\r\n} from \"../types\";\r\n\r\n// TODO: Add max messages per file before rotation\r\n\r\nexport type FileLogConfig = BasicLogConfigurations & {\r\n  storagePath?: string;\r\n  /**\r\n   * File name, without extension\r\n   */\r\n  name?: string;\r\n  /**\r\n   * chunk mode\r\n   * If set to `single`, the logs will be created in a single file, unless the rotate is set to true\r\n   * If set to `daily`, the logs will be created in a daily file, unless the rotate is set to true\r\n   * If set to `hourly`, the logs will be created in an hourly file, unless the rotate is set to true\r\n   * @default single\r\n   */\r\n  chunk?: \"single\" | \"daily\" | \"hourly\";\r\n  /**\r\n   * Whether to rotate the file\r\n   *\r\n   * @default true\r\n   */\r\n  rotate?: boolean;\r\n  /**\r\n   * File Extension\r\n   *\r\n   * @default log\r\n   */\r\n  extension?: string;\r\n  /**\r\n   * If rotate is set, the rotate name will be added to the file name suffixed with `-`\r\n   *\r\n   * @default DD-MM-YYYY\r\n   */\r\n  rotateFileName?: string;\r\n  /**\r\n   * Max file size before rotating the file\r\n   *\r\n   * @default 10MB\r\n   */\r\n  maxFileSize?: number;\r\n  /**\r\n   * Set the max messages that needs to be added before writing to the file\r\n   *\r\n   * @default 100\r\n   */\r\n  maxMessagesToWrite?: number;\r\n  /**\r\n   * Group logs by\r\n   * Please note that the order matters here\r\n   * For example, if you set `groupBy: ['level', 'module']`, the logs will be added in level name first, then by module\r\n   *\r\n   * @default none\r\n   */\r\n  groupBy?: (\"level\" | \"module\" | \"action\")[];\r\n  /**\r\n   * Define what levels should be logged\r\n   *\r\n   * @default all\r\n   */\r\n  levels?: LogLevel[];\r\n  /**\r\n   * Date and time format\r\n   */\r\n  dateFormat?: {\r\n    date?: string;\r\n    time?: string;\r\n  };\r\n};\r\n\r\nexport class FileLog extends LogChannel<FileLogConfig> implements LogContract {\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public name = \"file\";\r\n\r\n  /**\r\n   * Messages buffer\r\n   */\r\n  protected messages: LogMessage[] = [];\r\n\r\n  /**\r\n   * Grouped messages\r\n   */\r\n  protected groupedMessages: Record<string, LogMessage[]> = {};\r\n\r\n  /**\r\n   * Default channel configurations\r\n   */\r\n  protected defaultConfigurations: FileLogConfig = {\r\n    storagePath: process.cwd() + \"/storage/logs\",\r\n    rotate: true,\r\n    name: \"app\",\r\n    extension: \"log\",\r\n    chunk: \"single\",\r\n    maxMessagesToWrite: 100,\r\n    filter: () => true,\r\n    maxFileSize: 10 * 1024 * 1024, // 10MB\r\n    get rotateFileName() {\r\n      return dayjs().format(\"DD-MM-YYYY\");\r\n    },\r\n    dateFormat: {\r\n      date: \"DD-MM-YYYY\",\r\n      time: \"HH:mm:ss\",\r\n    },\r\n  };\r\n\r\n  /**\r\n   * Last write time\r\n   */\r\n  protected lastWriteTime = Date.now();\r\n\r\n  /**\r\n   * A flag to determine if the file is being written\r\n   */\r\n  protected isWriting = false;\r\n\r\n  /**\r\n   * Handle for the periodic flush interval. Stored so it can be cleared\r\n   * in `dispose()` — long-lived processes that create channels dynamically\r\n   * would otherwise leak one timer per channel.\r\n   */\r\n  protected flushIntervalHandle?: NodeJS.Timeout;\r\n\r\n  /**\r\n   * Check file size for file rotation\r\n   */\r\n  protected async checkAndRotateFile(filePath = this.filePath) {\r\n    if (!this.config(\"rotate\")) return;\r\n\r\n    try {\r\n      const stats = await fs.promises.stat(filePath);\r\n      if (stats.size >= this.config(\"maxFileSize\")!) {\r\n        await this.rotateLogFile();\r\n      }\r\n    } catch (error: any) {\r\n      // ENOENT is expected when the file hasn't been created yet — there is\r\n      // nothing to rotate, so stay silent. Surface anything else.\r\n      if (error.code !== \"ENOENT\") {\r\n        console.error(\"Error checking log file:\", error);\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Rotate log file\r\n   */\r\n  protected async rotateLogFile() {\r\n    const fileName = `${this.fileName}-${this.config(\"rotateFileName\")}-${Date.now()}`;\r\n\r\n    const extension = this.extension;\r\n\r\n    const rotatedFilePath = path.join(this.storagePath, `${fileName}.${extension}`);\r\n\r\n    await fs.promises.rename(this.filePath, rotatedFilePath).catch((error) => {\r\n      console.error(\"Error rotating file:\", error);\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Flush messages\r\n   *\r\n   * Starts a periodic re-check so low-traffic channels don't sit on buffered\r\n   * entries indefinitely. The handle is stored on the instance so `dispose()`\r\n   * can stop it — without this, every channel leaks a timer for the lifetime\r\n   * of the process.\r\n   */\r\n  protected initMessageFlush() {\r\n    this.flushIntervalHandle = setInterval(() => {\r\n      if (\r\n        this.messages.length > 0 &&\r\n        (this.messages.length >= this.maxMessagesToWrite || Date.now() - this.lastWriteTime > 5000)\r\n      ) {\r\n        this.writeMessagesToFile();\r\n      }\r\n    }, 5000);\r\n  }\r\n\r\n  /**\r\n   * Stop the background flush interval and drain any buffered entries.\r\n   *\r\n   * Call this when discarding a channel (e.g. reconfiguring the logger at\r\n   * runtime) so the 5-second timer doesn't keep the event loop alive. Safe to\r\n   * call more than once.\r\n   */\r\n  public dispose(): void {\r\n    if (this.flushIntervalHandle) {\r\n      clearInterval(this.flushIntervalHandle);\r\n      this.flushIntervalHandle = undefined;\r\n    }\r\n\r\n    this.flushSync();\r\n  }\r\n\r\n  /**\r\n   * Get file path\r\n   */\r\n  public get filePath() {\r\n    const fileName = this.fileName;\r\n\r\n    const extension = this.extension;\r\n\r\n    return path.join(this.storagePath, `${fileName}.${extension}`);\r\n  }\r\n\r\n  /**\r\n   * Get max messages\r\n   */\r\n  protected get maxMessagesToWrite(): number {\r\n    return this.config(\"maxMessagesToWrite\")!;\r\n  }\r\n\r\n  /**\r\n   * Get file name\r\n   */\r\n  public get fileName(): string {\r\n    const debugLevel = this.config(\"chunk\")!;\r\n\r\n    switch (debugLevel) {\r\n      case \"single\":\r\n      default:\r\n        return this.config(\"name\")!;\r\n      case \"daily\":\r\n        return dayjs().format(\"DD-MM-YYYY\");\r\n      case \"hourly\":\r\n        return dayjs().format(\"DD-MM-YYYY-HH-00-00-a\");\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get file extension\r\n   */\r\n  public get extension(): string {\r\n    return this.config(\"extension\")!;\r\n  }\r\n\r\n  /**\r\n   * Get content\r\n   */\r\n  protected get content() {\r\n    return this.messages.map((message) => message.content).join(EOL) + EOL;\r\n  }\r\n\r\n  /**\r\n   * Get storage path\r\n   */\r\n  public get storagePath(): string {\r\n    return this.config(\"storagePath\")!;\r\n  }\r\n\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  protected async init() {\r\n    const logsDirectory = this.storagePath;\r\n\r\n    await ensureDirectoryAsync(logsDirectory);\r\n\r\n    this.initMessageFlush();\r\n  }\r\n\r\n  /**\r\n   * Synchronously flush messages\r\n   */\r\n  public flushSync(): void {\r\n    if (this.messages.length === 0 && Object.keys(this.groupedMessages).length === 0) return;\r\n\r\n    if (this.messagedShouldBeGrouped) {\r\n      this.prepareGroupedMessages();\r\n      for (const key in this.groupedMessages) {\r\n        const directoryPath = path.join(this.storagePath, key);\r\n        fs.mkdirSync(directoryPath, { recursive: true });\r\n        const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n        const content = this.groupedMessages[key].map((message) => message.content).join(EOL) + EOL;\r\n        fs.appendFileSync(filePath, content);\r\n      }\r\n    } else {\r\n      fs.mkdirSync(this.storagePath, { recursive: true });\r\n      fs.appendFileSync(this.filePath, this.content);\r\n    }\r\n\r\n    this.onSave();\r\n  }\r\n\r\n  /**\r\n   * Asynchronously drain buffered entries to disk.\r\n   *\r\n   * The async analogue of {@link flushSync}: it reuses the same async writer\r\n   * as the background interval, so a caller on a graceful-shutdown path can\r\n   * `await channel.flush()` (or `await log.flush()`) and rely on the buffer\r\n   * being on disk once it resolves. `JSONFileLog` inherits this unchanged —\r\n   * its overridden `writeMessagesToFile` performs the JSON merge.\r\n   */\r\n  public async flush(): Promise<void> {\r\n    if (this.messages.length === 0 && Object.keys(this.groupedMessages).length === 0) {\r\n      return;\r\n    }\r\n\r\n    // Clear the in-flight lock so a deliberate drain is never short-circuited\r\n    // by a half-finished background write, then await the async writer to\r\n    // completion — unlike the fire-and-forget interval, callers depend on the\r\n    // buffer reaching disk before this resolves.\r\n    this.isWriting = false;\r\n\r\n    await this.writeMessagesToFile();\r\n  }\r\n\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public async log(data: LoggingData) {\r\n    const { module, action, message, type: level, context } = data;\r\n\r\n    if (!this.shouldBeLogged(data)) return;\r\n\r\n    const { date: dateFormat, time } = this.getDateAndTimeFormat();\r\n\r\n    const date = dayjs().format(dateFormat + \" \" + time);\r\n\r\n    let content = `[${date}] [${level}] [${module}][${action}]: `;\r\n\r\n    let stack: string | undefined;\r\n\r\n    // check if message is an instance of Error\r\n    if (message instanceof Error) {\r\n      // in that case we need to store the error message and stack trace\r\n      content += message.message + EOL;\r\n      content += `[trace]` + EOL;\r\n      content += message.stack;\r\n      stack = message.stack;\r\n    } else {\r\n      content += message;\r\n    }\r\n\r\n    this.messages.push({\r\n      content,\r\n      level,\r\n      date,\r\n      module,\r\n      action,\r\n      stack,\r\n      context,\r\n      timestamp: new Date().toISOString(),\r\n    });\r\n\r\n    await this.checkIfMessagesShouldBeWritten(); // Immediate check on buffer size\r\n  }\r\n\r\n  /**\r\n   * Check if messages should be written\r\n   */\r\n  protected async checkIfMessagesShouldBeWritten() {\r\n    if (this.messages.length >= this.maxMessagesToWrite || Date.now() - this.lastWriteTime > 5000) {\r\n      await this.writeMessagesToFile();\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Should be called after messages are saved\r\n   */\r\n  protected onSave() {\r\n    this.messages = [];\r\n    this.groupedMessages = {};\r\n    this.isWriting = false;\r\n    this.lastWriteTime = Date.now();\r\n  }\r\n\r\n  /**\r\n   * Check if messages should be grouped\r\n   */\r\n  protected get messagedShouldBeGrouped(): boolean {\r\n    return Number(this.config(\"groupBy\")?.length) > 0;\r\n  }\r\n\r\n  /**\r\n   * Write messages to the file\r\n   */\r\n  protected async writeMessagesToFile() {\r\n    if (this.messages.length === 0 || this.isWriting || !this.isInitialized) return;\r\n\r\n    this.isWriting = true;\r\n\r\n    if (this.messagedShouldBeGrouped) {\r\n      return await this.writeGroupedMessagesToFile();\r\n    }\r\n\r\n    await this.checkAndRotateFile(); // Ensure we check file size before writing\r\n\r\n    try {\r\n      await this.write(this.filePath, this.content);\r\n      this.onSave();\r\n    } catch (error) {\r\n      console.error(\"Failed to write log:\", error);\r\n      // Implement fallback logic here\r\n      this.isWriting = false;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Write grouped messages to the file\r\n   */\r\n  protected async writeGroupedMessagesToFile(): Promise<void> {\r\n    // first step, is to group the messages\r\n    this.prepareGroupedMessages();\r\n\r\n    // now each key in the grouped messages, represents the directory path that should extend the storage path\r\n    for (const key in this.groupedMessages) {\r\n      const directoryPath = path.join(this.storagePath, key);\r\n\r\n      await ensureDirectoryAsync(directoryPath);\r\n\r\n      const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n\r\n      await this.checkAndRotateFile(filePath); // Ensure we check file size before writing\r\n\r\n      const content = this.groupedMessages[key].map((message) => message.content).join(EOL) + EOL;\r\n\r\n      try {\r\n        await this.write(filePath, content);\r\n      } catch (error) {\r\n        console.error(\"Failed to write log:\", error);\r\n      }\r\n    }\r\n\r\n    this.onSave();\r\n    this.isWriting = false;\r\n  }\r\n\r\n  /**\r\n   * Prepare grouped messages\r\n   */\r\n  protected prepareGroupedMessages(): void {\r\n    this.messages.forEach((message) => {\r\n      const key = this.config(\"groupBy\")!\r\n        .map((groupKey) => encodeURIComponent(message[groupKey]))\r\n        .join(\"/\");\r\n\r\n      this.groupedMessages[key] = this.groupedMessages[key] || [];\r\n      this.groupedMessages[key].push(message);\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Start writing to the file\r\n   */\r\n  protected async write(filePath: string, content: string) {\r\n    return new Promise((resolve, reject) => {\r\n      const writer = fs.createWriteStream(filePath, { flags: \"a\" });\r\n\r\n      writer.write(content, (error) => {\r\n        writer.end();\r\n        if (error) {\r\n          reject(error);\r\n        } else {\r\n          resolve(true);\r\n        }\r\n      });\r\n    });\r\n  }\r\n}\r\n","import { stringify as safeStableStringify } from \"safe-stable-stringify\";\n\n/**\n * Replacer that surfaces Error data — `name`, `message`, and `stack` are\n * non-enumerable on `Error`, so neither default JSON serialization nor an\n * object spread captures them (both produce `{}`). They are copied explicitly;\n * the trailing spread then captures any additional enumerable props the caller\n * (or a subclass) attached, such as a `code` field.\n */\nfunction errorReplacer<Value = unknown>(\n  _key: string,\n  value: Value,\n): Record<string, unknown> | Value {\n  if (value instanceof Error) {\n    // Spread first, then the explicit Error fields. `name`/`message`/`stack`\n    // are non-enumerable on `Error`, so the spread never carries them — the\n    // explicit assignments are what surface them. Placing the spread first\n    // means those explicit keys legitimately take precedence over any\n    // identically-named *enumerable* prop a subclass attached (resolving the\n    // duplicate-key warning). Because `safe-stable-stringify` sorts keys, the\n    // insertion order here does not affect the serialized bytes.\n    return {\n      ...value,\n      name: value.name,\n      message: value.message,\n      stack: value.stack,\n    };\n  }\n\n  return value;\n}\n\n/**\n * JSON-serialize log payloads safely. Circular refs, BigInt, and repeated\n * non-tree references are handled by `safe-stable-stringify`; functions and\n * symbols are dropped (standard JSON behavior); Errors are expanded via\n * `errorReplacer`. Class instances serialize as their enumerable props.\n *\n * @example\n * await fs.promises.writeFile(filePath, safeJsonStringify(payload, 2));\n */\nexport function safeJsonStringify(value: unknown, space?: number): string {\n  return safeStableStringify(value, errorReplacer, space) ?? \"\";\n}\n","import { ensureDirectoryAsync, fileExistsAsync, getJsonFileAsync } from \"@warlock.js/fs\";\r\nimport dayjs from \"dayjs\";\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type { LogContract, LogMessage, LoggingData } from \"../types\";\r\nimport { safeJsonStringify } from \"../utils/safe-json-stringify\";\r\nimport { FileLog } from \"./file-log\";\r\n\r\nexport class JSONFileLog extends FileLog implements LogContract {\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public name = \"fileJson\";\r\n\r\n  /**\r\n   * Get file extension\r\n   */\r\n  public get extension(): string {\r\n    return \"json\";\r\n  }\r\n\r\n  /**\r\n   * Synchronously flush messages\r\n   */\r\n  public flushSync(): void {\r\n    if (this.messages.length === 0 && Object.keys(this.groupedMessages).length === 0) return;\r\n\r\n    if (this.messagedShouldBeGrouped) {\r\n      this.prepareGroupedMessages();\r\n      for (const key in this.groupedMessages) {\r\n        const directoryPath = path.join(this.storagePath, key);\r\n        fs.mkdirSync(directoryPath, { recursive: true });\r\n        const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n\r\n        let fileContents = { messages: [] as any[] };\r\n        if (fs.existsSync(filePath)) {\r\n          try {\r\n            fileContents = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\r\n            if (!Array.isArray(fileContents.messages)) fileContents.messages = [];\r\n          } catch (e) {\r\n            fileContents = { messages: [] };\r\n          }\r\n        }\r\n        fileContents.messages.push(...this.groupedMessages[key]);\r\n        fs.writeFileSync(filePath, safeJsonStringify(fileContents, 2));\r\n      }\r\n    } else {\r\n      fs.mkdirSync(this.storagePath, { recursive: true });\r\n      let fileContents = { messages: [] as any[] };\r\n      if (fs.existsSync(this.filePath)) {\r\n        try {\r\n          fileContents = JSON.parse(fs.readFileSync(this.filePath, \"utf-8\"));\r\n          if (!Array.isArray(fileContents.messages)) fileContents.messages = [];\r\n        } catch (e) {\r\n          fileContents = { messages: [] };\r\n        }\r\n      }\r\n      fileContents.messages.push(...this.messages);\r\n      fs.writeFileSync(this.filePath, safeJsonStringify(fileContents, 2));\r\n    }\r\n\r\n    this.onSave();\r\n  }\r\n\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public async log(data: LoggingData) {\r\n    let stack: string[] | undefined;\r\n\r\n    if (data.message instanceof Error) {\r\n      stack = data.message.stack?.split(\"\\n\");\r\n      data.message = data.message.message;\r\n    }\r\n\r\n    const { module, action, message, type: level, context } = data;\r\n\r\n    if (!this.shouldBeLogged(data)) return;\r\n\r\n    const { date: dateFormat, time } = this.getDateAndTimeFormat();\r\n\r\n    const date = dayjs().format(dateFormat + \" \" + time);\r\n\r\n    this.messages.push({\r\n      content: message,\r\n      level,\r\n      date,\r\n      module,\r\n      action,\r\n      stack,\r\n      context,\r\n      timestamp: new Date().toISOString(),\r\n    } as LogMessage);\r\n\r\n    await this.checkIfMessagesShouldBeWritten(); // Immediate check on buffer size\r\n  }\r\n\r\n  /**\r\n   * Write messages to the file\r\n   */\r\n  protected async writeMessagesToFile(): Promise<void> {\r\n    if (this.messages.length === 0 || this.isWriting || !this.isInitialized) return;\r\n\r\n    this.isWriting = true;\r\n\r\n    if (this.messagedShouldBeGrouped) {\r\n      return await this.writeGroupedMessagesToFile();\r\n    }\r\n\r\n    await this.checkAndRotateFile(); // Ensure file rotation is handled\r\n\r\n    let fileContents = { messages: [] as any[] };\r\n\r\n    if (await fileExistsAsync(this.filePath)) {\r\n      try {\r\n        fileContents = (await getJsonFileAsync(this.filePath)) as { messages: any[] };\r\n      } catch (error) {\r\n        console.error(\"Error reading log file, reinitializing:\", error);\r\n        fileContents = { messages: [] }; // Reinitialize the file if corrupted\r\n      }\r\n    } else {\r\n      fileContents = { messages: [] }; // Reinitialize the file if corrupted\r\n    }\r\n\r\n    fileContents.messages.push(...this.messages);\r\n\r\n    try {\r\n      await fs.promises.writeFile(this.filePath, safeJsonStringify(fileContents, 2));\r\n\r\n      this.onSave();\r\n    } catch (error) {\r\n      console.error(\"Failed to write log:\", error);\r\n      // Implement fallback logic here\r\n      this.isWriting = false;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Write grouped messages to the file\r\n   */\r\n  protected async writeGroupedMessagesToFile(): Promise<void> {\r\n    // first step, is to group the messages\r\n    this.prepareGroupedMessages();\r\n\r\n    // now each key in the grouped messages, represents the directory path that should extend the storage path\r\n    for (const key in this.groupedMessages) {\r\n      const directoryPath = path.join(this.storagePath, key);\r\n\r\n      await ensureDirectoryAsync(directoryPath);\r\n\r\n      const filePath = path.join(directoryPath, `${this.fileName}.${this.extension}`);\r\n\r\n      await this.checkAndRotateFile(filePath); // Ensure we check file size before writing\r\n\r\n      let fileContents: { messages: any[] } = { messages: [] };\r\n      if (await fileExistsAsync(filePath)) {\r\n        try {\r\n          fileContents = (await getJsonFileAsync(filePath)) as { messages: any[] };\r\n        } catch (error) {\r\n          console.error(\"Error reading log file, reinitializing:\", error);\r\n          fileContents = { messages: [] };\r\n        }\r\n      } else {\r\n        fileContents = { messages: [] };\r\n      }\r\n\r\n      fileContents.messages.push(...this.groupedMessages[key]);\r\n\r\n      try {\r\n        await fs.promises.writeFile(filePath, safeJsonStringify(fileContents, 2));\r\n      } catch (error) {\r\n        console.error(\"Failed to write log:\", error);\r\n        this.isWriting = false;\r\n      }\r\n    }\r\n\r\n    this.onSave();\r\n  }\r\n}\r\n","import { LogChannel } from \"../log-channel\";\r\nimport type { BasicLogConfigurations, LoggingData, LogLevel } from \"../types\";\r\nimport { safeJsonStringify } from \"../utils/safe-json-stringify\";\r\n\r\n// ── @sentry/node as an optional peer ─────────────────────────────────────────\r\n// @sentry/node is referenced ONLY through the local minimal types below — never\r\n// via `typeof import(\"@sentry/node\")`. This package is served as source\r\n// (`main` → `./src/index.ts`), so its `.ts` becomes part of a consumer's TS\r\n// program; a static type reference to the SDK would force module resolution and\r\n// break every consumer who (correctly) never installs the optional peer with a\r\n// TS2307 \"cannot find module\". The runtime import uses an indirect specifier for\r\n// the same reason, and its result is cast to these shapes. The shapes mirror the\r\n// stable `@sentry/node` public API (unchanged across v8–v10).\r\n\r\n/**\r\n * Sentry severity levels — the `@sentry/node` `SeverityLevel` union.\r\n */\r\ntype SentrySeverityLevel =\r\n  | \"fatal\"\r\n  | \"error\"\r\n  | \"warning\"\r\n  | \"log\"\r\n  | \"info\"\r\n  | \"debug\";\r\n\r\n/**\r\n * The Sentry `Scope` surface used while building a single event.\r\n */\r\nexport interface SentryScopeLike {\r\n  setLevel(level: SentrySeverityLevel): void;\r\n  setTags(tags: Record<string, string>): void;\r\n  setContext(name: string, context: Record<string, unknown> | null): void;\r\n}\r\n\r\n/**\r\n * A Sentry breadcrumb, as passed to `addBreadcrumb`.\r\n */\r\nexport type SentryBreadcrumb = {\r\n  category?: string;\r\n  message?: string;\r\n  level?: SentrySeverityLevel;\r\n  data?: Record<string, unknown>;\r\n};\r\n\r\n/**\r\n * The subset of the `@sentry/node` API `SentryLog` calls. The `@sentry/node`\r\n * namespace satisfies this shape, so an app can pass it straight through as\r\n * `client`; a test (or a custom forwarder) can supply a compatible stand-in.\r\n */\r\nexport interface SentryForwarder {\r\n  captureException(exception: unknown): string;\r\n  captureMessage(message: string, level?: SentrySeverityLevel): string;\r\n  addBreadcrumb(breadcrumb: SentryBreadcrumb): void;\r\n  withScope(callback: (scope: SentryScopeLike) => void): void;\r\n  flush(timeout?: number): Promise<boolean>;\r\n}\r\n\r\n/**\r\n * Sentry initialization options. Mirrors the common `@sentry/node` `NodeOptions`\r\n * fields; the index signature keeps any other SDK option valid without coupling\r\n * to the SDK's types.\r\n */\r\nexport type SentryInitOptions = {\r\n  dsn?: string;\r\n  environment?: string;\r\n  release?: string;\r\n  sampleRate?: number;\r\n  [key: string]: unknown;\r\n};\r\n\r\n/**\r\n * The lazily-imported `@sentry/node` namespace surface — the forwarder plus the\r\n * lifecycle calls the channel uses when it owns initialization.\r\n */\r\ninterface SentryNamespace extends SentryForwarder {\r\n  init(options?: SentryInitOptions): unknown;\r\n  getClient(): unknown;\r\n}\r\n\r\nlet Sentry: SentryNamespace | undefined;\r\nlet isModuleExists: boolean | null = null;\r\nlet loadingPromise: Promise<void> | undefined;\r\n\r\nconst SENTRY_INSTALL_INSTRUCTIONS = `\r\nThe Sentry log channel requires the @sentry/node package.\r\nInstall it with:\r\n\r\n  npm install @sentry/node\r\n\r\nOr with your preferred package manager:\r\n\r\n  pnpm add @sentry/node\r\n  yarn add @sentry/node\r\n`.trim();\r\n\r\n/**\r\n * Load @sentry/node once, lazily and concurrency-safely. A bare catch maps any\r\n * import failure to \"not installed\" — the curated install message surfaces at\r\n * `log()` time, never as a boot-time module-resolution crash.\r\n */\r\nfunction loadSentry(): Promise<void> {\r\n  if (isModuleExists !== null) {\r\n    return Promise.resolve();\r\n  }\r\n\r\n  if (loadingPromise) {\r\n    return loadingPromise;\r\n  }\r\n\r\n  loadingPromise = (async () => {\r\n    try {\r\n      // Indirect specifier (typed `string`, not a literal) so TypeScript never\r\n      // statically resolves the optional peer — see the file header.\r\n      const sentryModule: string = \"@sentry/node\";\r\n      Sentry = (await import(sentryModule)) as unknown as SentryNamespace;\r\n      isModuleExists = true;\r\n    } catch {\r\n      isModuleExists = false;\r\n    }\r\n  })();\r\n\r\n  return loadingPromise;\r\n}\r\n\r\nexport type SentryLogConfig = BasicLogConfigurations & {\r\n  /**\r\n   * Reuse an already-initialized Sentry instance — typically the `@sentry/node`\r\n   * namespace from an app that already calls `Sentry.init(...)`. When set, the\r\n   * channel forwards through it and never imports or re-initializes the SDK.\r\n   */\r\n  client?: SentryForwarder;\r\n  /**\r\n   * Initialize Sentry from these options instead of reusing a host client. The\r\n   * channel lazily imports `@sentry/node` and calls `Sentry.init(options)` once,\r\n   * guarded so it never clobbers an existing client.\r\n   */\r\n  options?: SentryInitOptions;\r\n  /**\r\n   * Levels delivered as Sentry *events* (these consume the error quota). Every\r\n   * other level is recorded as a breadcrumb that rides along with the next\r\n   * event, costing no quota.\r\n   *\r\n   * @default [\"fatal\", \"error\", \"warn\"]\r\n   */\r\n  eventLevels?: LogLevel[];\r\n  /**\r\n   * Milliseconds `flush()` waits for the transport to drain on shutdown.\r\n   *\r\n   * @default 2000\r\n   */\r\n  flushTimeout?: number;\r\n};\r\n\r\n/**\r\n * Forwards log entries to Sentry.\r\n *\r\n * Entries at an `eventLevels` level (`error` / `warn` by default) become Sentry\r\n * **events**: an `Error` message via `captureException` (preserving the real\r\n * stack), any other message via `captureMessage`. Every other level becomes a\r\n * **breadcrumb** — buffered and attached to the next event, consuming no error\r\n * quota. `module` / `action` are attached as searchable tags and the entry's\r\n * `context` as a structured Sentry context.\r\n *\r\n * The SDK is an optional peer: pass an existing `client` (reused as-is) or\r\n * `options` (the channel lazily imports `@sentry/node` and initializes it). On\r\n * graceful shutdown, `await log.flush()` drains pending events via\r\n * `Sentry.flush(timeout)`.\r\n *\r\n * @example\r\n * // Existing app — reuse the initialized Sentry client\r\n * import * as Sentry from \"@sentry/node\";\r\n * log.addChannel(new SentryLog({ client: Sentry }));\r\n *\r\n * @example\r\n * // New app — let the channel initialize Sentry\r\n * log.addChannel(new SentryLog({ options: { dsn: process.env.SENTRY_DSN } }));\r\n */\r\nexport class SentryLog extends LogChannel<SentryLogConfig> {\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public name = \"sentry\";\r\n\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public description =\r\n    \"Forwards entries to Sentry as events (error/warn) or breadcrumbs (everything else)\";\r\n\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  protected defaultConfigurations: SentryLogConfig = {\r\n    eventLevels: [\"fatal\", \"error\", \"warn\"],\r\n    flushTimeout: 2_000,\r\n  };\r\n\r\n  /**\r\n   * The resolved forwarder — the injected `client` or the lazily-imported\r\n   * `@sentry/node` namespace. `undefined` until `init()` resolves, and when the\r\n   * SDK is absent (then `log()` surfaces the install message once).\r\n   */\r\n  private sentry?: SentryForwarder;\r\n\r\n  /**\r\n   * Guards the one-time \"@sentry/node is not installed\" notice so a missing SDK\r\n   * doesn't spam stderr on every entry.\r\n   */\r\n  private warnedMissing = false;\r\n\r\n  /**\r\n   * Resolve an injected `client` (the Sentry namespace) **synchronously**, so an\r\n   * entry logged on the same tick as construction — e.g. at app boot, before\r\n   * the base schedules `init()` on the next tick via `setTimeout(0)` — is not\r\n   * silently dropped. The `options` (lazy-import) path is inherently async and\r\n   * still resolves in `init()`.\r\n   */\r\n  public constructor(configurations?: SentryLogConfig) {\r\n    super(configurations);\r\n\r\n    const injected = this.config(\"client\");\r\n\r\n    if (injected) {\r\n      this.sentry = injected;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Resolve the forwarder: reuse the injected client, otherwise lazily import\r\n   * `@sentry/node` and (only when explicit `options` are supplied and no client\r\n   * exists yet) initialize it. Never throws — the base runs `init()` inside an\r\n   * un-awaited `setTimeout`, so a throw would become an unhandled rejection and\r\n   * `isInitialized` would never flip; a missing SDK is reported from `log()`.\r\n   */\r\n  protected async init(): Promise<void> {\r\n    const injected = this.config(\"client\");\r\n\r\n    if (injected) {\r\n      this.sentry = injected;\r\n\r\n      return;\r\n    }\r\n\r\n    await loadSentry();\r\n\r\n    if (!Sentry) {\r\n      return;\r\n    }\r\n\r\n    const options = this.config(\"options\");\r\n\r\n    if (options && !Sentry.getClient()) {\r\n      Sentry.init(options);\r\n    }\r\n\r\n    this.sentry = Sentry;\r\n  }\r\n\r\n  /**\r\n   * {@inheritdoc}\r\n   */\r\n  public async log(data: LoggingData): Promise<void> {\r\n    if (!this.shouldBeLogged(data)) {\r\n      return;\r\n    }\r\n\r\n    if (isModuleExists === null) {\r\n      // wait until module is fully loaded\r\n      await loadSentry();\r\n    }\r\n\r\n    if (!this.sentry) {\r\n      this.reportMissingSdk();\r\n\r\n      return;\r\n    }\r\n\r\n    const { module, action, message, type: level, context } = data;\r\n\r\n    if (this.isEventLevel(level)) {\r\n      this.captureEvent(this.sentry, {\r\n        module,\r\n        action,\r\n        message,\r\n        level,\r\n        context,\r\n      });\r\n\r\n      return;\r\n    }\r\n\r\n    this.sentry.addBreadcrumb({\r\n      category: module,\r\n      message: this.toText(message),\r\n      level: this.toSentryLevel(level),\r\n      data: context,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Drain pending Sentry events. Bounded by `flushTimeout` so an unreachable\r\n   * Sentry can never hang a graceful shutdown. No-op when the SDK is absent.\r\n   */\r\n  public async flush(): Promise<void> {\r\n    if (!this.sentry) {\r\n      return;\r\n    }\r\n\r\n    await this.sentry.flush(this.config(\"flushTimeout\"));\r\n  }\r\n\r\n  /**\r\n   * Whether the level should be sent as a Sentry event (vs a breadcrumb).\r\n   */\r\n  private isEventLevel(level: LogLevel): boolean {\r\n    return Boolean(this.config(\"eventLevels\")?.includes(level));\r\n  }\r\n\r\n  /**\r\n   * Send an entry as a Sentry event. An `Error` message goes through\r\n   * `captureException` so Sentry parses the real stack and groups properly;\r\n   * any other message goes through `captureMessage`. `module` / `action` are\r\n   * attached as tags and `context` as a structured context, scoped to this\r\n   * event only via `withScope`.\r\n   */\r\n  private captureEvent(\r\n    sentry: SentryForwarder,\r\n    entry: {\r\n      module: string;\r\n      action: string;\r\n      message: unknown;\r\n      level: LogLevel;\r\n      context?: Record<string, any>;\r\n    },\r\n  ): void {\r\n    const { module, action, message, level, context } = entry;\r\n    const sentryLevel = this.toSentryLevel(level);\r\n\r\n    sentry.withScope((scope) => {\r\n      scope.setLevel(sentryLevel);\r\n      scope.setTags({ module, action });\r\n\r\n      if (context) {\r\n        scope.setContext(\"context\", context);\r\n      }\r\n\r\n      if (message instanceof Error) {\r\n        sentry.captureException(message);\r\n      } else {\r\n        sentry.captureMessage(this.toText(message), sentryLevel);\r\n      }\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Map a logger level to a Sentry severity. `success` has no Sentry\r\n   * equivalent, so it is reported as informational.\r\n   */\r\n  private toSentryLevel(level: LogLevel): SentrySeverityLevel {\r\n    switch (level) {\r\n      case \"warn\":\r\n        return \"warning\";\r\n      case \"success\":\r\n        return \"info\";\r\n      default:\r\n        return level; // debug | info | error | fatal map 1:1\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Coerce a message into the string Sentry's APIs expect — an `Error`'s\r\n   * `.message`, a string as-is, anything else safely JSON-serialized.\r\n   */\r\n  private toText(message: unknown): string {\r\n    if (typeof message === \"string\") {\r\n      return message;\r\n    }\r\n\r\n    if (message instanceof Error) {\r\n      return message.message;\r\n    }\r\n\r\n    return safeJsonStringify(message);\r\n  }\r\n\r\n  /**\r\n   * Surface the install instructions exactly once when the SDK is absent. The\r\n   * logger can't log through itself here, so this writes to stderr — matching\r\n   * how the file channels report write failures.\r\n   */\r\n  private reportMissingSdk(): void {\r\n    if (isModuleExists === false && !this.warnedMissing) {\r\n      this.warnedMissing = true;\r\n      console.error(SENTRY_INSTALL_INSTRUCTIONS);\r\n    }\r\n  }\r\n}\r\n","import type { LoggingData, RedactCensor, RedactConfig } from \"../types\";\n\n/**\n * Deep-clone a value with structural fidelity for log entries — handles plain\n * objects, arrays, `Date`, `Error`, and primitives. Anything else is copied\n * by reference (we only redact paths through plain objects/arrays anyway,\n * and rebuilding e.g. a `Buffer` would change semantics).\n *\n * Purpose-built rather than reaching for `structuredClone`: `Error` instances\n * lose their `message`/`stack` under `structuredClone` in some Node versions,\n * and the logger pipeline carries them often.\n */\nfunction cloneEntry<T>(value: T, seen = new WeakMap<object, any>()): T {\n  if (value === null || typeof value !== \"object\") {\n    return value;\n  }\n\n  if (seen.has(value as unknown as object)) {\n    return seen.get(value as unknown as object);\n  }\n\n  if (value instanceof Date) {\n    return new Date(value.getTime()) as unknown as T;\n  }\n\n  if (value instanceof Error) {\n    const copy = new (value.constructor as ErrorConstructor)(value.message);\n    copy.stack = value.stack;\n    copy.name = value.name;\n    return copy as unknown as T;\n  }\n\n  if (Array.isArray(value)) {\n    const arr: any[] = [];\n    seen.set(value as unknown as object, arr);\n    for (const item of value) {\n      arr.push(cloneEntry(item, seen));\n    }\n    return arr as unknown as T;\n  }\n\n  const out: Record<string, any> = {};\n  seen.set(value as unknown as object, out);\n  for (const key of Object.keys(value as Record<string, any>)) {\n    out[key] = cloneEntry((value as Record<string, any>)[key], seen);\n  }\n  return out as unknown as T;\n}\n\n/**\n * Apply a single censor decision to a value. String censors are returned\n * verbatim; function censors receive the original value plus the dotted\n * path so callers can implement value-aware redaction (mask all but the\n * last 4 chars, hash, etc.).\n */\nfunction applyCensor(value: any, censor: RedactCensor, path: string[]): any {\n  if (typeof censor === \"function\") {\n    return censor(value, path.join(\".\"));\n  }\n  return censor;\n}\n\n/**\n * Walk `target` following the remaining `segments` of a path pattern,\n * replacing matched leaves via `censor`. Operates in place — the caller\n * is responsible for cloning before calling.\n *\n * Wildcards:\n * - `*` matches exactly one segment (any key on a plain object, any index\n *   on an array — stringified for the path that's passed to a function\n *   censor).\n * - `**` matches zero or more segments greedily; the rest of the pattern\n *   is then attempted at the current level and at every descendant.\n */\nfunction redactAtPath(\n  target: any,\n  segments: string[],\n  censor: RedactCensor,\n  pathTrail: string[],\n): void {\n  if (target === null || typeof target !== \"object\") {\n    return;\n  }\n\n  if (segments.length === 0) {\n    return;\n  }\n\n  const [head, ...rest] = segments;\n\n  if (head === \"**\") {\n    // Try matching `rest` at the current level (the zero-segment match\n    // case), then recurse into every child carrying the `**` forward so\n    // it keeps matching at deeper levels too.\n    if (rest.length > 0) {\n      redactAtPath(target, rest, censor, pathTrail);\n    }\n    const keys = Array.isArray(target)\n      ? target.map((_, index) => String(index))\n      : Object.keys(target);\n    for (const key of keys) {\n      redactAtPath(target[key], segments, censor, [...pathTrail, key]);\n    }\n    return;\n  }\n\n  const keysToVisit =\n    head === \"*\"\n      ? Array.isArray(target)\n        ? target.map((_, index) => String(index))\n        : Object.keys(target)\n      : Array.isArray(target)\n        ? // Numeric segment can index into an array.\n          /^\\d+$/.test(head) && Number(head) < target.length\n          ? [head]\n          : []\n        : Object.prototype.hasOwnProperty.call(target, head)\n          ? [head]\n          : [];\n\n  for (const key of keysToVisit) {\n    if (rest.length === 0) {\n      target[key] = applyCensor(target[key], censor, [...pathTrail, key]);\n    } else {\n      redactAtPath(target[key], rest, censor, [...pathTrail, key]);\n    }\n  }\n}\n\n/**\n * Produce a new `LoggingData` with every path in `config.paths` replaced\n * by `config.censor`. The original entry is never mutated — channels and\n * other call sites can hold references to the input safely.\n *\n * No-op (returns the input by reference) when `config` is `undefined` or\n * its `paths` array is empty, so the fast path stays fast.\n */\nexport function applyRedact(\n  data: LoggingData,\n  config: RedactConfig | undefined,\n): LoggingData {\n  if (!config || config.paths.length === 0) {\n    return data;\n  }\n\n  const censor = config.censor ?? \"[REDACTED]\";\n  const cloned = cloneEntry(data);\n\n  for (const pattern of config.paths) {\n    const segments = pattern.split(\".\").filter((segment) => segment.length > 0);\n    if (segments.length === 0) continue;\n    redactAtPath(cloned, segments, censor, []);\n  }\n\n  return cloned;\n}\n\n/**\n * Combine two redact configs into one effective config. Used to merge a\n * channel's additive paths on top of the logger-wide floor.\n *\n * - `paths` are concatenated; duplicates are kept (the matcher tolerates\n *   them, and de-duping cross-config would mask a developer typo).\n * - `censor` from the channel wins; falls back to the logger's; falls back\n *   to the default `\"[REDACTED]\"`.\n */\nexport function mergeRedact(\n  base: RedactConfig | undefined,\n  extra: RedactConfig | undefined,\n): RedactConfig | undefined {\n  if (!base && !extra) return undefined;\n  if (!base) return extra;\n  if (!extra) return base;\n\n  return {\n    paths: [...base.paths, ...extra.paths],\n    censor: extra.censor ?? base.censor,\n  };\n}\n","/**\r\n * Clear message from any terminal codes\r\n */\r\nexport function clearMessage(message: any) {\r\n  if (typeof message !== \"string\") return message;\r\n\r\n  // eslint-disable-next-line no-control-regex\r\n  return message.replace(/\\u001b[^m]*?m/g, \"\");\r\n}\r\n","import { Random } from \"@mongez/reinforcements\";\r\nimport type { LogChannel } from \"./log-channel\";\r\nimport { applyRedact, mergeRedact } from \"./redact\";\r\nimport type {\r\n  AutoFlushEvent,\r\n  LoggingData,\r\n  LogLevel,\r\n  OmittedLoggingData,\r\n  RedactConfig,\r\n} from \"./types\";\r\nimport { clearMessage } from \"./utils/clear-message\";\r\n\r\nconst SIGNAL_EVENTS: ReadonlySet<AutoFlushEvent> = new Set([\r\n  \"SIGINT\",\r\n  \"SIGTERM\",\r\n  \"SIGHUP\",\r\n  \"SIGBREAK\",\r\n  \"SIGUSR2\",\r\n]);\r\n\r\n/**\r\n * Severity ranks used by `setMinLevel`. Higher number = more severe. The\r\n * ordering matches conventional log-level hierarchies: `debug` is noisiest\r\n * and easiest to drop; `error` is the loudest and never dropped by the\r\n * minimum-level filter. `success` sits beside `info` — it's an informational\r\n * outcome, not a warning.\r\n */\r\nconst LEVEL_RANK: Record<LogLevel, number> = {\r\n  debug: 0,\r\n  info: 1,\r\n  success: 1,\r\n  warn: 2,\r\n  error: 3,\r\n  fatal: 4,\r\n};\r\n\r\nexport class Logger {\r\n  /**\r\n   * Current channel\r\n   */\r\n  public channels: LogChannel[] = [];\r\n\r\n  public id = \"logger-\" + Random.string(32);\r\n\r\n  /**\r\n   * Registered auto-flush handlers, keyed by event name. Stored so repeated\r\n   * calls to `enableAutoFlush` replace rather than stack, and so\r\n   * `disableAutoFlush` can remove them cleanly.\r\n   */\r\n  private autoFlushHandlers = new Map<AutoFlushEvent, () => void>();\r\n\r\n  /**\r\n   * Logger-wide minimum severity. When set, entries below this level are\r\n   * dropped before any channel is invoked — cheaper than per-channel `levels`\r\n   * filters because the fan-out loop is skipped entirely. `undefined` means\r\n   * no minimum (every entry reaches every channel that accepts it).\r\n   */\r\n  private minLevel?: LogLevel;\r\n\r\n  /**\r\n   * Logger-wide redaction floor. Applied once before fan-out — every\r\n   * channel receives an entry with these paths already censored. Channel\r\n   * configs can extend the path list (additive); they cannot remove paths\r\n   * set here.\r\n   */\r\n  private redactConfig?: RedactConfig;\r\n\r\n  /**\r\n   * Add a new channel\r\n   */\r\n  public addChannel(channel: LogChannel) {\r\n    this.channels.push(channel);\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Set base configurations\r\n   */\r\n  public configure(config: {\r\n    channels?: LogChannel[];\r\n    autoFlushOn?: AutoFlushEvent[];\r\n    minLevel?: LogLevel;\r\n    redact?: RedactConfig;\r\n  }) {\r\n    if (config.channels) {\r\n      this.channels = config.channels;\r\n    }\r\n\r\n    if (config.autoFlushOn) {\r\n      this.enableAutoFlush(config.autoFlushOn);\r\n    }\r\n\r\n    if (config.minLevel !== undefined) {\r\n      this.setMinLevel(config.minLevel);\r\n    }\r\n\r\n    if (config.redact !== undefined) {\r\n      this.setRedact(config.redact);\r\n    }\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Set the logger-wide redaction floor. Applied to every entry before\r\n   * fan-out; channel configs add more paths on top, never fewer. Pass\r\n   * `undefined` to clear.\r\n   *\r\n   * @example\r\n   * log.setRedact({\r\n   *   paths: [\"context.password\", \"context.*.token\"],\r\n   *   censor: \"[REDACTED]\",\r\n   * });\r\n   */\r\n  public setRedact(config: RedactConfig | undefined): this {\r\n    this.redactConfig = config;\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Read the active logger-wide redact config (or `undefined`).\r\n   */\r\n  public getRedact(): RedactConfig | undefined {\r\n    return this.redactConfig;\r\n  }\r\n\r\n  /**\r\n   * Drop every entry whose severity is below `level` before fan-out. Cheaper\r\n   * than per-channel `levels` filters because the loop never runs and no\r\n   * channel receives the entry. Pass `undefined` to clear and accept all\r\n   * levels again.\r\n   *\r\n   * @example\r\n   * // production: silence debug noise everywhere at once\r\n   * logger.setMinLevel(\"info\");\r\n   */\r\n  public setMinLevel(level: LogLevel | undefined): this {\r\n    this.minLevel = level;\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Read the active minimum severity (or `undefined` when none is set).\r\n   */\r\n  public getMinLevel(): LogLevel | undefined {\r\n    return this.minLevel;\r\n  }\r\n\r\n  /**\r\n   * Set channels\r\n   */\r\n  public setChannels(channels: LogChannel[]) {\r\n    this.channels = channels;\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Normalize log data to a single object\r\n   */\r\n  private normalizeLogData(\r\n    dataOrModule: LoggingData | OmittedLoggingData | string,\r\n    action?: string,\r\n    message: any = \"\",\r\n    level?: LogLevel,\r\n    context?: Record<string, any>,\r\n  ): LoggingData {\r\n    if (typeof dataOrModule === \"object\") {\r\n      // If level is provided, override type\r\n      return {\r\n        type: (level || (dataOrModule as any).type || \"info\") as LogLevel,\r\n        module: dataOrModule.module,\r\n        action: dataOrModule.action,\r\n        message: dataOrModule.message,\r\n        ...(context ? { context } : dataOrModule.context ? { context: dataOrModule.context } : {}),\r\n      };\r\n    }\r\n    return {\r\n      type: (level || \"info\") as LogLevel,\r\n      module: dataOrModule,\r\n      action: action as string,\r\n      message,\r\n      ...(context ? { context } : {}),\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Make log\r\n   *\r\n   * Fans out a single log entry to every registered channel. Non-terminal\r\n   * channels receive a copy whose `message` has had ANSI color codes stripped\r\n   * — each channel sees its own shallow clone so one channel cannot observe\r\n   * another's mutations (e.g. a later terminal channel still sees the original\r\n   * colored message).\r\n   */\r\n  public async log(data: LoggingData) {\r\n    if (this.minLevel && LEVEL_RANK[data.type] < LEVEL_RANK[this.minLevel]) {\r\n      return this;\r\n    }\r\n\r\n    // Apply the logger-wide redact floor once. Every channel sees the\r\n    // result; no channel can undo a logger-wide redaction (additive-only\r\n    // semantics).\r\n    const baseEntry = applyRedact(data, this.redactConfig);\r\n\r\n    for (const channel of this.channels) {\r\n      const channelRedact = channel.getRedactConfig?.();\r\n      const effectiveRedact = channelRedact\r\n        ? mergeRedact(this.redactConfig, channelRedact)\r\n        : undefined;\r\n\r\n      // When the channel adds paths, redact again from `data` rather than\r\n      // from `baseEntry` so the merged config (which already contains the\r\n      // logger-wide paths) does the full pass — avoids double-cloning the\r\n      // already-redacted base.\r\n      let payload = effectiveRedact ? applyRedact(data, effectiveRedact) : baseEntry;\r\n\r\n      if (channel.terminal === false) {\r\n        payload = { ...payload, message: clearMessage(payload.message) };\r\n      }\r\n\r\n      channel.log(payload);\r\n    }\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Make debug log\r\n   */\r\n  public debug(\r\n    dataOrModule: OmittedLoggingData | string,\r\n    action?: string,\r\n    message: any = \"\",\r\n    context?: Record<string, any>,\r\n  ) {\r\n    const data = this.normalizeLogData(dataOrModule, action, message, \"debug\", context);\r\n    return this.log(data);\r\n  }\r\n\r\n  /**\r\n   * Make info log\r\n   */\r\n  public info(\r\n    dataOrModule: OmittedLoggingData | string,\r\n    action?: string,\r\n    message: any = \"\",\r\n    context?: Record<string, any>,\r\n  ) {\r\n    const data = this.normalizeLogData(dataOrModule, action, message, \"info\", context);\r\n    return this.log(data);\r\n  }\r\n\r\n  /**\r\n   * Make warn log\r\n   */\r\n  public warn(\r\n    dataOrModule: OmittedLoggingData | string,\r\n    action?: string,\r\n    message: any = \"\",\r\n    context?: Record<string, any>,\r\n  ) {\r\n    const data = this.normalizeLogData(dataOrModule, action, message, \"warn\", context);\r\n    return this.log(data);\r\n  }\r\n\r\n  /**\r\n   * Make error log\r\n   */\r\n  public error(\r\n    dataOrModule: OmittedLoggingData | string,\r\n    action?: string,\r\n    message: any = \"\",\r\n    context?: Record<string, any>,\r\n  ) {\r\n    const data = this.normalizeLogData(dataOrModule, action, message, \"error\", context);\r\n    return this.log(data);\r\n  }\r\n\r\n  /**\r\n   * Make success log\r\n   */\r\n  public success(\r\n    dataOrModule: OmittedLoggingData | string,\r\n    action?: string,\r\n    message: any = \"\",\r\n    context?: Record<string, any>,\r\n  ) {\r\n    const data = this.normalizeLogData(dataOrModule, action, message, \"success\", context);\r\n\r\n    return this.log(data);\r\n  }\r\n\r\n  /**\r\n   * Make fatal log — for unrecoverable failures where the application is going\r\n   * down (failed bootstrap, lost connection to a required dependency that the\r\n   * caller has decided not to retry, an `uncaughtException`).\r\n   *\r\n   * Identical shape to {@link error}; the level is purely informational —\r\n   * `fatal` does NOT auto-flush or exit. The caller decides whether to call\r\n   * `await log.flush()` and `process.exit(...)`.\r\n   */\r\n  public fatal(\r\n    dataOrModule: OmittedLoggingData | string,\r\n    action?: string,\r\n    message: any = \"\",\r\n    context?: Record<string, any>,\r\n  ) {\r\n    const data = this.normalizeLogData(dataOrModule, action, message, \"fatal\", context);\r\n\r\n    return this.log(data);\r\n  }\r\n\r\n  /**\r\n   * Log an `error` entry when `condition` is falsy. No-op otherwise — the\r\n   * entry is never built and channels are not invoked, so this is genuinely\r\n   * free in the happy path. Mirrors the spirit of `console.assert` but routes\r\n   * through the logger pipeline so persistent channels capture failures.\r\n   *\r\n   * @example\r\n   * log.assert(user !== null, \"auth\", \"session\", \"user vanished mid-flight\", { sessionId });\r\n   */\r\n  public assert(\r\n    condition: unknown,\r\n    module: string,\r\n    action: string,\r\n    message: any,\r\n    context?: Record<string, any>,\r\n  ): Promise<Logger> | Logger {\r\n    if (condition) return this;\r\n    return this.error(module, action, message, context);\r\n  }\r\n\r\n  /**\r\n   * Start a duration timer. The returned function emits an `info` entry\r\n   * with `completed in <ms>ms` and a `durationMs` field in `context` when\r\n   * called. Pass an object to `end()` to merge extra fields into context.\r\n   *\r\n   * @example\r\n   * const end = log.timer(\"db\", \"users.findById\");\r\n   * const user = await usersRepo.findById(id);\r\n   * end({ id, found: !!user });\r\n   */\r\n  public timer(\r\n    module: string,\r\n    action: string,\r\n  ): (extra?: Record<string, any>) => Promise<Logger> {\r\n    const startedAt = Date.now();\r\n    return (extra?: Record<string, any>) => {\r\n      const durationMs = Date.now() - startedAt;\r\n      return this.info(module, action, `completed in ${durationMs}ms`, {\r\n        durationMs,\r\n        ...(extra ?? {}),\r\n      });\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Get channel by name\r\n   */\r\n  public channel(name: string) {\r\n    return this.channels.find((channel) => channel.name === name);\r\n  }\r\n\r\n  /**\r\n   * Synchronously flush logs\r\n   */\r\n  public flushSync() {\r\n    for (const channel of this.channels) {\r\n      if (channel.flushSync) {\r\n        channel.flushSync();\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Asynchronously drain every channel that implements `flush()`.\r\n   *\r\n   * Unlike {@link flushSync}, this awaits each channel's async I/O — the\r\n   * correct call for a graceful shutdown that can afford to wait\r\n   * (`await log.flush()` after closing the HTTP server, before\r\n   * `process.exit`). A channel whose delivery is async (a network transport,\r\n   * an async disk write) implements `flush()`, not `flushSync()`.\r\n   *\r\n   * Channels are isolated: a channel whose flush rejects can neither prevent\r\n   * the others from draining nor escape as an unhandled rejection. Channels\r\n   * without `flush()` are skipped.\r\n   *\r\n   * @example\r\n   * async function shutdown() {\r\n   *   await httpServer.close();\r\n   *   await log.flush();\r\n   *   process.exit(0);\r\n   * }\r\n   */\r\n  public async flush(): Promise<void> {\r\n    await Promise.allSettled(\r\n      this.channels.map(async (channel) => {\r\n        if (!channel.flush) {\r\n          return;\r\n        }\r\n\r\n        try {\r\n          await channel.flush();\r\n        } catch {\r\n          // A single channel must never break shutdown for the others —\r\n          // a graceful drain is best-effort across every channel.\r\n        }\r\n      }),\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Register one process-level handler per event that calls `flushSync()`\r\n   * before the process terminates.\r\n   *\r\n   * For signal events (`SIGINT`, `SIGTERM`, `SIGHUP`, `SIGBREAK`, `SIGUSR2`)\r\n   * the handler flushes and then re-raises the signal so Node's default exit\r\n   * behavior runs. For `beforeExit`, the handler flushes in place — Node exits\r\n   * naturally afterwards.\r\n   *\r\n   * Idempotent: calling with the same events replaces the previous handlers.\r\n   * Call `disableAutoFlush()` to unregister.\r\n   *\r\n   * @example\r\n   * log.configure({\r\n   *   channels: [new ConsoleLog(), new FileLog()],\r\n   *   autoFlushOn: [\"SIGINT\", \"SIGTERM\", \"beforeExit\"],\r\n   * });\r\n   */\r\n  public enableAutoFlush(events: AutoFlushEvent[]): this {\r\n    this.disableAutoFlush();\r\n\r\n    for (const event of events) {\r\n      const handler = SIGNAL_EVENTS.has(event)\r\n        ? () => {\r\n            this.flushSync();\r\n            process.off(event, handler);\r\n            process.kill(process.pid, event as NodeJS.Signals);\r\n          }\r\n        : () => {\r\n            this.flushSync();\r\n          };\r\n\r\n      process.on(event, handler);\r\n      this.autoFlushHandlers.set(event, handler);\r\n    }\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Remove every handler previously registered by `enableAutoFlush`.\r\n   * Safe to call when no handlers are registered.\r\n   */\r\n  public disableAutoFlush(): this {\r\n    for (const [event, handler] of this.autoFlushHandlers) {\r\n      process.off(event, handler);\r\n    }\r\n\r\n    this.autoFlushHandlers.clear();\r\n\r\n    return this;\r\n  }\r\n}\r\n\r\n/**\r\n * The package singleton. Use this for everyday logging — `log.info(...)`,\r\n * `log.error(...)`, `log.configure(...)`. Custom logger instances can be\r\n * created by instantiating `Logger` directly.\r\n *\r\n * The name is intentionally short: `log` reads naturally at the call site\r\n * (`log.info(\"auth\", \"login\", \"ok\")`) and matches the convention used in\r\n * pino, bunyan, and most JS logging tutorials.\r\n *\r\n * Note that `log` is a `Logger` instance, **not** a function — the bare\r\n * callable form was removed when the dual `log` / `logger` exports were\r\n * collapsed into a single name. Use `log.info(...)` (or any other level\r\n * shortcut) to emit entries.\r\n */\r\nexport const log = new Logger();\r\n","import { log } from \"../logger\";\r\n\r\n/**\r\n * Route Node's process-level failure events through the logger so they land in\r\n * every configured channel with full stack context. Registers one listener for\r\n * `unhandledRejection` and one for `uncaughtException`; call once at startup\r\n * after channels are configured. Pair with `autoFlushOn: [\"beforeExit\"]` so the\r\n * final entry survives the process exit that follows an uncaught exception.\r\n *\r\n * Levels chosen for semantic honesty:\r\n *\r\n * - `uncaughtException` → `log.fatal` — by default Node terminates the process,\r\n *   so the failure is unrecoverable.\r\n * - `unhandledRejection` → `log.error` — a rejected promise is a failure, but\r\n *   not necessarily process-ending (depends on Node's `--unhandled-rejections`\r\n *   policy and your app's recovery), so it stays at error.\r\n *\r\n * @example\r\n * log.configure({ channels: [new ConsoleLog(), new FileLog()] });\r\n * captureAnyUnhandledRejection();\r\n */\r\nexport function captureAnyUnhandledRejection() {\r\n  process.on(\"unhandledRejection\", (reason: any) => {\r\n    log.error(\"app\", \"unhandledRejection\", reason);\r\n  });\r\n\r\n  process.on(\"uncaughtException\", (error) => {\r\n    log.fatal(\"app\", \"uncaughtException\", error);\r\n  });\r\n}\r\n"],"mappings":"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"}